Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking change: add structure to manifest #26

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions release/firmware/ftlog/example_firmware_release.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
{
"schema_version": 1,
"component": "TRUSTED_APPLET",
"git_tag_name": "0.1.2",
"git_commit_fingerprint": "aac1e176cfac1a1e079b5f624b83fda54b5d0f76",
"firmware_digest_sha256": "8l4TaroPsSq+zwG+XMPZw+EdpUoXH0IT4cKM2RmFyNE=",
"tamago_version": "1.20.6",
"build_envs": [
"DEBUG=1",
"CHECK=no"
],
"git": {
mhutchinson marked this conversation as resolved.
Show resolved Hide resolved
"tag_name": "0.1.2",
"commit_fingerprint": "aac1e176cfac1a1e079b5f624b83fda54b5d0f76"
},
"build": {
"tamago_version": "1.20.6",
"envs": [
"DEBUG=1",
"CHECK=no"
]
},
"output": {
"firmware_digest_sha256": "8l4TaroPsSq+zwG+XMPZw+EdpUoXH0IT4cKM2RmFyNE="
},
"hab": {
"target": "ci",
"signature_digest_sha256": "8l4TaroPsSq+zwG+XMPZw+EdpUoXH0IT4cKM2RmFyNE="
Expand Down
60 changes: 42 additions & 18 deletions release/firmware/ftlog/log_entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,47 +31,71 @@ const (

// FirmwareRelease represents a firmware release in the log.
type FirmwareRelease struct {
// SchemaVersion gives a unique ID for this version of the schema. This will be
// incremented when there are breaking changes to the schema that all clients
// should be aware of.
SchemaVersion int `json:"schema_version"`

// Component identifies the type of firmware (e.g. OS or applet).
// This component is key to disambiguate what the firmware is, and other
// implicit information can be derived from this. For example, the git
// repository that the code should be checked out from to reproduce the
// build.
Component string `json:"component"`

// GitTagName identifies the version of this release, e.g. "0.1.2"
GitTagName semver.Version `json:"git_tag_name"`
// Git contains information about the origin of the code used to build this release.
Git Git `json:"git"`

// Build contains information about the toolchain used to build this release.
Build Build `json:"build"`

// Output contains commitments to the binaries distributed in this release.
Output Output `json:"output"`

// HAB holds a signature and related data for firmware which must be authenticated
// by the device's mask ROM at boot.
// Currently, this is only meaningful for Bootloader and Recovery firmware images.
HAB *HAB `json:"hab,omitempty"`
}

// Git holds information about the source from which the binary was built.
type Git struct {
// TagName identifies the version of this release, e.g. "0.1.2"
TagName semver.Version `json:"tag_name"`

// GitCommitFingerprint contains the hex-encoded SHA-1 commit hash of the git repository when checked
// CommitFingerprint contains the hex-encoded SHA-1 commit hash of the git repository when checked
// out at TagName. Committing to this information allows verifiers that cannot
// reproduce a build to quickly narrow down the problem space:
// - if this GitCommitFingerprint is different then they have checked out different code
// - if this CommitFingerprint is different then they have checked out different code
// than was used to build the binary. This could happen if the wrong repo was
// used, or because the TagName was changed to a different commit
// - if the GitCommitFingerprint is the same, then they have the same code checked out but
// - if the CommitFingerprint is the same, then they have the same code checked out but
// there is a problem with the build toolchain (different tooling or non-reproducible
// builds).
GitCommitFingerprint string `json:"git_commit_fingerprint"`

// FirmwareDigestSha256 is the hash of the compiled firmware binary. Believers that are
// installing a firmware release must check that the firmware data they are going to
// believe has a fingerprint matching this hash. Verifiers that check out the correct
// source repo & version must be able to reproducibly build a binary that has this fingerprint.
FirmwareDigestSha256 []byte `json:"firmware_digest_sha256"`
CommitFingerprint string `json:"commit_fingerprint"`
}

// Build holds information about the build toolchain and methodology for turning the source
// into the binary.
type Build struct {
// TamagoVersion identifies the version of [Tamago] that the builder used to compile
// the binary with FirmwareDigestSha256.
//
// [Tamago]: https://github.com/usbarmory/tamago
TamagoVersion semver.Version `json:"tamago_version"`

// BuildEnvs contains all environment variables set for this build. Each value in the string
// Envs contains all environment variables set for this build. Each value in the string
// array will be a single key/value assignment, such as "DEBUG=1".
BuildEnvs []string `json:"build_envs,omitempty"`
Envs []string `json:"envs,omitempty"`
}

// HAB holds a signature and related data for firmware which must be authenticated
// by the device's mask ROM at boot.
// Currently, this is only meaningful for Bootloader and Recovery firmware images.
HAB *HAB `json:"hab,omitempty"`
// Output holds commitments to the binary artifacts that were produced.
type Output struct {
// FirmwareDigestSha256 is the hash of the compiled firmware binary. Believers that are
// installing a firmware release must check that the firmware data they are going to
// believe has a fingerprint matching this hash. Verifiers that check out the correct
// source repo & version must be able to reproducibly build a binary that has this fingerprint.
FirmwareDigestSha256 []byte `json:"firmware_digest_sha256"`
}

// HAB holds information relating to SecureBoot.
Expand Down
17 changes: 10 additions & 7 deletions release/firmware/ftlog/log_entries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,31 @@ func TestParseFirmwareRelease(t *testing.T) {
if err := json.Unmarshal(bs, &r); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if got, want := r.SchemaVersion, 1; got != want {
t.Errorf("Got %q, want %q", got, want)
}
if got, want := r.Component, ftlog.ComponentApplet; got != want {
t.Errorf("Got %q, want %q", got, want)
}
if got, want := r.GitTagName, *semver.New("0.1.2"); got != want {
if got, want := r.Git.TagName, *semver.New("0.1.2"); got != want {
t.Errorf("Got %q, want %q", got, want)
}
if got, want := len(r.GitCommitFingerprint), 40; got != want {
if got, want := len(r.Git.CommitFingerprint), 40; got != want {
t.Errorf("Got %d, want %d", got, want)
}
if got, want := r.GitCommitFingerprint, "aac1e176cfac1a1e079b5f624b83fda54b5d0f76"; got != want {
if got, want := r.Git.CommitFingerprint, "aac1e176cfac1a1e079b5f624b83fda54b5d0f76"; got != want {
t.Errorf("Got %x, want %x", got, want)
}
if got, want := len(r.FirmwareDigestSha256), 32; got != want {
if got, want := len(r.Output.FirmwareDigestSha256), 32; got != want {
t.Errorf("Got %d, want %d", got, want)
}
if got, want := r.FirmwareDigestSha256, mustDecode("8l4TaroPsSq+zwG+XMPZw+EdpUoXH0IT4cKM2RmFyNE="); !bytes.Equal(got, want) {
if got, want := r.Output.FirmwareDigestSha256, mustDecode("8l4TaroPsSq+zwG+XMPZw+EdpUoXH0IT4cKM2RmFyNE="); !bytes.Equal(got, want) {
t.Errorf("Got %x, want %x", got, want)
}
if got, want := r.TamagoVersion, *semver.New("1.20.6"); got != want {
if got, want := r.Build.TamagoVersion, *semver.New("1.20.6"); got != want {
t.Errorf("Got %q, want %q", got, want)
}
if got, want := r.BuildEnvs, []string{"DEBUG=1", "CHECK=no"}; !reflect.DeepEqual(got, want) {
if got, want := r.Build.Envs, []string{"DEBUG=1", "CHECK=no"}; !reflect.DeepEqual(got, want) {
t.Errorf("Got %q, want %q", got, want)
}
if got, want := r.HAB.Target, "ci"; got != want {
Expand Down
10 changes: 5 additions & 5 deletions release/firmware/update/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ type FetcherOpts struct {

// BinaryPath returns the relative path within a bucket for the binary referenced by the manifest.
func BinaryPath(fr ftlog.FirmwareRelease) (string, error) {
if len(fr.FirmwareDigestSha256) == 0 {
if len(fr.Output.FirmwareDigestSha256) == 0 {
return "", errors.New("firmware digest unset")
}
return fmt.Sprintf("%064x", fr.FirmwareDigestSha256), nil
return fmt.Sprintf("%064x", fr.Output.FirmwareDigestSha256), nil
}

// HABSignaturePath returns the relative path within a bucket for the HAB signature referenced by the manifest.
Expand Down Expand Up @@ -160,7 +160,7 @@ func (f *Fetcher) GetLatestVersions(_ context.Context) (os semver.Version, apple
if f.latestOS == nil || f.latestApplet == nil {
return semver.Version{}, semver.Version{}, errors.New("no versions of OS or applet found in log")
}
return f.latestOS.manifest.GitTagName, f.latestApplet.manifest.GitTagName, nil
return f.latestOS.manifest.Git.TagName, f.latestApplet.manifest.Git.TagName, nil
}

func (f *Fetcher) GetOS(ctx context.Context) (firmware.Bundle, error) {
Expand Down Expand Up @@ -314,8 +314,8 @@ func (f *Fetcher) Scan(ctx context.Context) error {
// numbering.
func highestRelease(current *firmwareRelease, candidate *firmwareRelease) *firmwareRelease {
if current == nil ||
current.manifest.GitTagName.LessThan(candidate.manifest.GitTagName) ||
current.manifest.GitTagName.Equal(candidate.manifest.GitTagName) {
current.manifest.Git.TagName.LessThan(candidate.manifest.Git.TagName) ||
current.manifest.Git.TagName.Equal(candidate.manifest.Git.TagName) {
return candidate
}
return current
Expand Down
Loading
Loading