From 437d6d3cdf3cc48cdda1835f05a4badc3c7f8742 Mon Sep 17 00:00:00 2001 From: Al Cutter Date: Mon, 26 Feb 2024 18:29:59 +0000 Subject: [PATCH] Version is a SemVer string (#194) --- api/api.go | 3 +- api/api.pb.go | 8 ++--- api/api.proto | 2 +- trusted_os/ctl.go | 10 +++++-- trusted_os/rpmb.go | 65 +++++++++++++++++++---------------------- trusted_os/rpmb_fake.go | 62 ++++++++++++++++----------------------- 6 files changed, 69 insertions(+), 81 deletions(-) diff --git a/api/api.go b/api/api.go index caad910..4ab46fd 100644 --- a/api/api.go +++ b/api/api.go @@ -17,7 +17,6 @@ package api import ( "bytes" "fmt" - "time" "google.golang.org/protobuf/proto" @@ -98,7 +97,7 @@ func (p *Status) Print() string { status.WriteString(fmt.Sprintf("Secure Boot ................: %v\n", p.HAB)) status.WriteString(fmt.Sprintf("SRK hash ...................: %s\n", p.SRKHash)) status.WriteString(fmt.Sprintf("Revision ...................: %s\n", p.Revision)) - status.WriteString(fmt.Sprintf("Version ....................: %d (%s)\n", p.Version, time.Unix(int64(p.Version), 0))) + status.WriteString(fmt.Sprintf("Version ....................: %s\n", p.Version)) status.WriteString(fmt.Sprintf("Runtime ....................: %s\n", p.Runtime)) status.WriteString(fmt.Sprintf("Link .......................: %v\n", p.Link)) status.WriteString(fmt.Sprintf("IdentityCounter ............: %d\n", p.IdentityCounter)) diff --git a/api/api.pb.go b/api/api.pb.go index 94e2ca3..8ba411b 100644 --- a/api/api.pb.go +++ b/api/api.pb.go @@ -292,7 +292,7 @@ type Status struct { HAB bool `protobuf:"varint,2,opt,name=HAB,proto3" json:"HAB,omitempty"` Revision string `protobuf:"bytes,3,opt,name=Revision,proto3" json:"Revision,omitempty"` Build string `protobuf:"bytes,4,opt,name=Build,proto3" json:"Build,omitempty"` - Version uint32 `protobuf:"varint,5,opt,name=Version,proto3" json:"Version,omitempty"` + Version string `protobuf:"bytes,5,opt,name=Version,proto3" json:"Version,omitempty"` Runtime string `protobuf:"bytes,6,opt,name=Runtime,proto3" json:"Runtime,omitempty"` Link bool `protobuf:"varint,7,opt,name=Link,proto3" json:"Link,omitempty"` Witness *WitnessStatus `protobuf:"bytes,8,opt,name=Witness,proto3" json:"Witness,omitempty"` @@ -362,11 +362,11 @@ func (x *Status) GetBuild() string { return "" } -func (x *Status) GetVersion() uint32 { +func (x *Status) GetVersion() string { if x != nil { return x.Version } - return 0 + return "" } func (x *Status) GetRuntime() string { @@ -642,7 +642,7 @@ var file_api_proto_rawDesc = []byte{ 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x4c, 0x69, 0x6e, diff --git a/api/api.proto b/api/api.proto index 9f314f2..5daaf02 100644 --- a/api/api.proto +++ b/api/api.proto @@ -125,7 +125,7 @@ message Status { bool HAB = 2; string Revision = 3; string Build = 4; - uint32 Version = 5; + string Version = 5; string Runtime = 6; bool Link = 7; WitnessStatus Witness = 8; diff --git a/trusted_os/ctl.go b/trusted_os/ctl.go index 02109b4..72531d7 100644 --- a/trusted_os/ctl.go +++ b/trusted_os/ctl.go @@ -57,7 +57,13 @@ type controlInterface struct { } func getStatus() (s *api.Status) { - version, _ := parseVersion(Version) + version, err := parseVersion(Version) + versionString := "unknown" + if err != nil { + log.Printf("failed to get version: %v", err) + } else { + versionString = version.String() + } s = &api.Status{ Serial: fmt.Sprintf("%X", imx6ul.UniqueID()), @@ -65,7 +71,7 @@ func getStatus() (s *api.Status) { SRKHash: SRKHash, Revision: Revision, Build: Build, - Version: version, + Version: versionString, Runtime: fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH), // TODO(jayhou): set IdentityCounter here. } diff --git a/trusted_os/rpmb.go b/trusted_os/rpmb.go index 563c115..72e9dc4 100644 --- a/trusted_os/rpmb.go +++ b/trusted_os/rpmb.go @@ -21,17 +21,17 @@ import ( "bytes" "crypto/aes" "crypto/sha256" - "encoding/binary" + "encoding/gob" "errors" "fmt" "log" - "strconv" "golang.org/x/crypto/pbkdf2" "github.com/usbarmory/tamago/soc/nxp/imx6ul" "github.com/usbarmory/tamago/soc/nxp/usdhc" + "github.com/coreos/go-semver/semver" "github.com/usbarmory/crucible/otp" "github.com/transparency-dev/armored-witness-os/rpmb" @@ -41,7 +41,7 @@ const ( // RPMB sector for CVE-2020-13799 mitigation dummySector = 0 // version epoch length - versionLength = 4 + versionLength = 32 // RPMB sector for OS rollback protection osVersionSector = 1 // RPMB sector for TA rollback protection @@ -144,43 +144,40 @@ func (r *RPMB) init() error { return nil } -func parseVersion(s string) (version uint32, err error) { - v, err := strconv.Atoi(s) - - if err != nil { - return - } - - return uint32(v), nil +func parseVersion(s string) (version *semver.Version, err error) { + return semver.NewVersion(s) } // expectedVersion returns the version epoch stored in an RPMB area of the // internal eMMC. -func (r *RPMB) expectedVersion(offset uint16) (version uint32, err error) { +func (r *RPMB) expectedVersion(offset uint16) (*semver.Version, error) { if r.partition == nil { - return 0, errors.New("RPMB has not been initialized") + return nil, errors.New("RPMB has not been initialized") } buf := make([]byte, versionLength) - - if err = r.partition.Read(offset, buf); err != nil { - return + if err := r.partition.Read(offset, buf); err != nil { + return nil, err + } + var v string + if err := gob.NewDecoder(bytes.NewBuffer(buf)).Decode(&v); err != nil { + return nil, err } - return binary.BigEndian.Uint32(buf), nil + return semver.NewVersion(v) } // updateVersion writes a new version epoch in an RPMB area of the internal // eMMC. -func (r *RPMB) updateVersion(offset uint16, version uint32) (err error) { +func (r *RPMB) updateVersion(offset uint16, version semver.Version) error { if r.partition == nil { return errors.New("RPMB has not been initialized") } - - buf := make([]byte, versionLength) - binary.BigEndian.PutUint32(buf, version) - - return r.partition.Write(offset, buf) + buf := &bytes.Buffer{} + if err := gob.NewEncoder(buf).Encode(version.String()); err != nil { + return err + } + return r.partition.Write(offset, buf.Bytes()) } // checkVersion verifies version information against RPMB stored data. @@ -190,29 +187,27 @@ func (r *RPMB) updateVersion(offset uint16, version uint32) (err error) { // // If the passed version is more recent than the RPMB area information then the // internal eMMC is updated with it. -func (r *RPMB) checkVersion(offset uint16, s string) (err error) { - version, err := parseVersion(s) - +func (r *RPMB) checkVersion(offset uint16, s string) error { + runningVersion, err := parseVersion(s) if err != nil { - return + return err } expectedVersion, err := r.expectedVersion(offset) - if err != nil { - return + return err } switch { - case expectedVersion > version: + case runningVersion.LessThan(*expectedVersion): return errors.New("version mismatch") - case expectedVersion == version: - return - case expectedVersion < version: - return r.updateVersion(offset, version) + case expectedVersion.Equal(*runningVersion): + return nil + case expectedVersion.LessThan(*runningVersion): + return r.updateVersion(offset, *runningVersion) } - return + return nil } // transfer performs an authenticated data transfer to the card RPMB partition, diff --git a/trusted_os/rpmb_fake.go b/trusted_os/rpmb_fake.go index e9d3e97..b9866a6 100644 --- a/trusted_os/rpmb_fake.go +++ b/trusted_os/rpmb_fake.go @@ -18,16 +18,15 @@ package main import ( - "encoding/binary" "errors" - "strconv" + "github.com/coreos/go-semver/semver" "github.com/transparency-dev/armored-witness-os/rpmb" ) const ( // version epoch length - versionLength = 4 + versionLength = 32 // RPMB sector for OS rollback protection osVersionSector = 1 // RPMB sector for TA rollback protection @@ -47,72 +46,61 @@ type RPMB struct { func newRPMB(_ Card) (*RPMB, error) { return &RPMB{ - mem: make(map[numSectors][sectorLength]byte), + mem: [numSectors][sectorLength]byte{}, }, nil } -func r (*RPMB) init() error { +func (r *RPMB) init() error { return nil } -func parseVersion(s string) (version uint32, err error) { - v, err := strconv.Atoi(s) - if err != nil { - return - } - - return uint32(v), nil +func parseVersion(s string) (version *semver.Version, err error) { + return semver.NewVersion(s) } // expectedVersion returns the version epoch stored in a fake RPMB area. -func (r *RPMB) expectedVersion(sector uint16) (version uint32, err error) { - buf := make([]byte, versionLength) - copy(buf, r.mem[sector]) +func (r *RPMB) expectedVersion(offset uint16) (*semver.Version, error) { + v := string(r.mem[offset][:versionLength]) - return binary.BigEndian.Uint32(buf), nil + return semver.NewVersion(v) } // updateVersion writes a new version epoch in a fake RPMB area. -func (r *RPMB) updateVersion(sector uint16, version uint32) (err error) { - buf := make([]byte, versionLength) - binary.BigEndian.PutUint32(buf, version) - - copy(r.mem[sector], buf) +func (r *RPMB) updateVersion(offset uint16, version semver.Version) error { + copy(r.mem[offset][:], []byte(version.String())) r.counter++ return nil } -// checkVersion verifies version information against fake RPMB stored data. +// checkVersion verifies version information against RPMB stored data. // // If the passed version is older than the RPMB area information of the // internal eMMC an error is returned. // // If the passed version is more recent than the RPMB area information then the // internal eMMC is updated with it. -func (r *RPMB) checkVersion(sector uint16, s string) (err error) { - version, err := parseVersion(s) - +func (r *RPMB) checkVersion(offset uint16, s string) error { + runningVersion, err := parseVersion(s) if err != nil { - return + return err } - expectedVersion, err := r.expectedVersion(sector) - + expectedVersion, err := r.expectedVersion(offset) if err != nil { - return + return err } switch { - case expectedVersion > version: + case runningVersion.LessThan(*expectedVersion): return errors.New("version mismatch") - case expectedVersion == version: - return - case expectedVersion < version: - return r.updateVersion(sector, version) + case expectedVersion.Equal(*runningVersion): + return nil + case expectedVersion.LessThan(*runningVersion): + return r.updateVersion(offset, *runningVersion) } - return + return nil } // transfer performs a data transfer to the fake RPMB area, @@ -124,10 +112,10 @@ func (r *RPMB) transfer(sector uint16, buf []byte, n *uint32, write bool) (err e } if write { - copy(r.mem[sector], buf) + copy(r.mem[sector][:], buf) r.counter++ } else { - copy(buf, r.mem[sector]) + copy(buf, r.mem[sector][:]) } if n != nil {