diff --git a/cmds/fittool/commands/addrawheaders/command.go b/cmds/fittool/commands/addrawheaders/command.go index 9203d05a..834f4851 100644 --- a/cmds/fittool/commands/addrawheaders/command.go +++ b/cmds/fittool/commands/addrawheaders/command.go @@ -58,7 +58,7 @@ func (cmd *Command) Execute(args []string) error { return fmt.Errorf("unable to open the firmware image file '%s': %w", cmd.UEFIPath, err) } - table, err := fit.GetTableFrom(file) + table, _, err := fit.GetTableFrom(file) if err != nil { return fmt.Errorf("unable to get FIT from the firmware image: %w", err) } diff --git a/cmds/fittool/commands/removeheaders/command.go b/cmds/fittool/commands/removeheaders/command.go index 2b83c1bc..a0bbad04 100644 --- a/cmds/fittool/commands/removeheaders/command.go +++ b/cmds/fittool/commands/removeheaders/command.go @@ -43,7 +43,7 @@ func (cmd *Command) Execute(args []string) error { return fmt.Errorf("unable to open the firmware image file '%s': %w", cmd.UEFIPath, err) } - table, err := fit.GetTableFrom(file) + table, _, err := fit.GetTableFrom(file) if err != nil { return fmt.Errorf("unable to get FIT from the firmware image: %w", err) } diff --git a/cmds/fittool/commands/setrawheaders/command.go b/cmds/fittool/commands/setrawheaders/command.go index ae31e612..f8154c31 100644 --- a/cmds/fittool/commands/setrawheaders/command.go +++ b/cmds/fittool/commands/setrawheaders/command.go @@ -59,7 +59,7 @@ func (cmd *Command) Execute(args []string) error { return fmt.Errorf("unable to open the firmware image file '%s': %w", cmd.UEFIPath, err) } - table, err := fit.GetTableFrom(file) + table, _, err := fit.GetTableFrom(file) if err != nil { return fmt.Errorf("unable to get FIT from the firmware image: %w", err) } diff --git a/pkg/intel/metadata/README.md b/pkg/intel/metadata/README.md index 8f649092..bed105b4 100644 --- a/pkg/intel/metadata/README.md +++ b/pkg/intel/metadata/README.md @@ -1,59 +1,527 @@ -Prequesites: -Open another terminal and go get the sm2/sm3 crypto library into your gopath. -The code generator disables go modules. +# Intel BG/CBnT Metadata + + +- [Intel BG/CBnT Metadata](#intel-bgcbnt-metadata) + - [Structure](#structure) + - [cbnt](#cbnt) + - [bootpolicy](#bootpolicy) + - [keymanifest](#keymanifest) + - [Structure Modification/Extension](#structure-modificationextension) + - [Types with Static Sizes](#types-with-static-sizes) + - [Types with Dynamic Sizes](#types-with-dynamic-sizes) + - [Field Type Quick Reference](#field-type-quick-reference) + - [Adding a Structure](#adding-a-structure) + - [Extending a Structure](#extending-a-structure) + + +This directory contains reusable definitions of common BG/CBnT structures and Boot Policy and Key Manifests for Intel platforms. + +## Structure ``` -GOPATH=your/go/path go get github.com/tjfoc/gmsm/sm2 +├── cbnt # Converged Boot Guard and TXT +│   ├── bootpolicy # BG/CBnT Boot Policy Manifest +│   ├── keymanifest # BG/CBnT Key Manifest +├── common # Common elements +│   ├── examples +│   ├── manifestcodegen # Legacy code generator +│   ├── pretty # Helper for printing the structures in human readable format +│   ├── tracedbinary +│   └── unittest # +└── fit # Firmware Interface Table ``` -To generalize all the logic related to Boot Policy Manifest and Key Manifest -we use code generation. Therefore it is enough to create structure declarations -and run command from this directory: +## cbnt +The `cbnt` packages defines the structures that are used by both Boot Policy and Key Manifests. +Most of the structures are shared between Boot Guard 1.0 and CBnT, with an exception for the following: + +- Chipset AC Module Information +- TPM Info List + +## bootpolicy +The `bootpolicy` package defines the Boot Policy Manifest and its child structures. In contrast with `cbnt`, +there are more differences between Boot Guard 1.0 and CBnT. Therefore, the Manifest constructor make the distinction +between the versions and returns the versioned implementation of the Manifest interface. Similar pattern is being used for +the structures that form the Manifest. + +The users of the `bootpolicy` packages should therefore make use of type assertions. These are safe in this context provided +that the constructor is feed with the supported version, and the error is handled correctly. An example usage: +```go +bpm, err := bootpolicy.NewManifest(cbnt.Version10) +if err != nil { + return nil, err +} +bgbpm = bpm.(*bootpolicy.ManifestBG) ``` -go run ./../common/manifestcodegen/cmd/manifestcodegen/ . ./bootpolicy ./key + +From that point on, the elements of the implementation may be accessed directly, for example: +```go +flags := bgbpm.SE[0].Flags +if !flags.AuthorityMeasure() { + return false, fmt.Errorf("pcr-7 data should extended for OS security") +} +if !flags.TPMFailureLeavesHierarchiesEnabled() { + return false, fmt.Errorf("tpm failure should lead to default measurements from PCR0 to PCR7") +} ``` -It will performe the code autogeneration in directories: -* Current directory (`.`), which contains common structures for different manifests; -* Boot Policy Manifest directory (`./bootpolicy`); -* and Key Manifest directory (`./key`). +## keymanifest +The `keymanifest` package defines the Key Manifest. It follows the same design as `bootpolicy`. An example usage: +```go +km, err := keymanifest.NewManifest(b.Version) +if err != nil { + return nil, err +} +cbntkm = km.(*keymanifest.CBnTManifest) -To check if the files are in the up-to-date state, one may add option `-check`: +hash := cbntkm.PubKeyHashAlg + if hash == cbnt.AlgSHA1 || hash.IsNull() { + return false, fmt.Errorf("KM signature uses insecure hash algorithm SHA1/Null") + } ``` -go run ./../common/manifestcodegen/cmd/manifestcodegen/ -check . ./bootpolicy ./key + +## Structure Modification/Extension +All the structures should implement the `Structure` interface (see [`cbnt/types.go`](cbnt/types.go)): +```go +Structure interface { + io.ReaderFrom + io.WriterTo + TotalSize() uint64 + SizeOf(id int) (uint64, error) + OffsetOf(id int) (uint64, error) + Layout() []LayoutField + Validate() error + PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string +} +``` + +The most important method of the structure is the `Layout()` as it provides the `Common.ReadFrom()`, `Common.WriteTo()`, `Common.SizeOf()` and `Common.OffsetOf()` methods +with the information of the actual type the operation is supposed to be done. The common methods are accessed by letting all types to have `Common` struct +as a field. +> [!NOTE] +> `Common` struct should never be included in the `Layout()`! Otherwise, it will be treated as the actual part of the CBnT data structure. + +### Types with Static Sizes +In most cases, that is, for the types that do not include fields that have their size determined at compile time, most of work is done in `Layout()` +method. Let's take `StructureInfoCBNT` as an example: +```go +func (s StructInfoCBNT) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "ID", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.ID }, + Type: ManifestFieldArrayStatic, + }, + { + ID: 1, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Version }, + Type: ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Variable 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Variable0 }, + Type: ManifestFieldEndValue, + }, + { + ID: 3, + Name: "Element Size", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.ElementSize }, + Type: ManifestFieldEndValue, + }, + } +} ``` -Or if it is required to debug/trace the behavior of autogenerated code, one -may add option `-trace`: +All the values are known at compile time, and therefore the sizes defined in the specification can be filled in directly. + +### Types with Dynamic Sizes +The types that have dynamic sizes are different beasts. They should still describe all fields through `Layout()`, but the `Size` +closure is computed from runtime values. The actual implementation is left for the person introducing a new type. + +There are three "Types" of dynamic sized fields: + - `ManifestFieldArrayDynamicWithSize` for byte arrays whose length is known from context (for example another field or algorithm-dependent size). +An example can be seen in `Key` type which is declared as: +```go +type Key struct { + Common + KeyAlg Algorithm `json:"keyAlg"` + Version uint8 `require:"0x10" json:"keyVersion"` + KeySize BitSize `json:"keyBitsize"` + Data []byte `countValue:"keyDataSize()" json:"keyData"` +} ``` -go run ./../common/manifestcodegen/cmd/manifestcodegen/ -trace . ./bootpolicy ./key +The size of `Data` field is equal to the value of `KeySize` which is first determined once the structure is filled-in on a call to `Common.ReadFrom()`. +Therefore, the size closure in `Key`'s `Layout()` has to return the value of `KeySize`: +```go +{ + ID: 3, + Name: "Data", + Size: func() uint64 { return uint64(k.keyDataSize()) }, + Value: func() any { return &k.Data }, + Type: ManifestFieldArrayDynamicWithSize, +}, ``` -In this case the code will write a verbose log into stdout. +- `ManifestFieldArrayDynamicWithPrefix` for byte arrays that carry their own size prefix on the wire. An example for such case is the `HashStructure` type declared as: +```go +type HashStructure struct { + Common + HashAlg Algorithm `default:"0x10" json:"hsAlg"` + HashBuffer []byte `json:"hsBuffer"` +} +``` + +The size of `HashBuffer` is dynamic, but unlike `ManifestFieldArrayDynamicWithSize`, this encoding stores a `uint16` +length prefix before the actual bytes. Therefore, the `Size` closure includes both the prefix and the payload (which depends on the hash algorithm type): +```go +{ + ID: 1, + Name: "Hash Buffer", + Size: func() uint64 { + h, err := s.HashAlg.Hash() + if err != nil { + return uint64(binary.Size(uint16(0))) + } + return uint64(binary.Size(uint16(0))) + uint64(h.Size()) + }, + Value: func() any { return &s.HashBuffer }, + Type: ManifestFieldArrayDynamicWithPrefix, +}, +``` +Here, `binary.Size(uint16(0))` is the on-wire size prefix. + +- `ManifestFieldList` for repeated items where count and element parsing/writing are custom; this requires `ReadList` and `WriteList` handlers. An example +for such case is the `HashList` type, declared as: +```go +type HashList struct { + Common + Size uint16 `rehashValue:"TotalSize()" json:"hlSize"` + List []HashStructure `json:"hlList"` +} +``` + +Such types require addition fields to be returned in `Layout()`, and are an exception from the rule that all R/W logic is shared between the types. This is +motivated by the fact that `Common.ReadFrom()`/`Common.WriteTo()` cannot infer neither the type of the element on the list (well, it is possible with [reflection](https://go.dev/blog/laws-of-reflection), +though it would make common R/W methods look like dark magic), nor many elements should be processed and how each one should be +serialized. Therefore, `ReadList` and `WriteList` closures must be provided: +```go +{ + ReadList: func(r io.Reader) (int64, error) { + var count uint16 + if err := binary.Read(r, endianess, &count); err != nil { + return 0, fmt.Errorf("unable to read the count for field 'List': %w", err) + } + totalN := int64(binary.Size(count)) + s.List = make([]HashStructure, count) + for idx := range s.List { + n, err := s.List[idx].ReadFrom(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field 'List[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + WriteList: func(w io.Writer) (int64, error) { + count := uint16(len(s.List)) + if err := binary.Write(w, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to write the count for field 'List': %w", err) + } + totalN := int64(binary.Size(count)) + for idx := range s.List { + n, err := s.List[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'List[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, +}, +``` +Common R/W method will then call the closures and let them do type specific work. +Similarly as with other dynamic sizes, the closure has to provide the way of the runtime calculation that can be done +after the call to `Common.ReadFrom()`. An example `HashList`: + +```go +{ + Size: func() uint64 { + size := uint64(binary.Size(uint16(0))) + for idx := range s.List { + size += s.List[idx].Common.TotalSize(&s.List[idx]) + } + return size +}, +``` + +- `ManifestFieldSubStruct` for fields that represent another type that implement `Structure` interface. An example of such case is the `KeySignature` type declared as: +```go +type KeySignature struct { + Common + Version uint8 `require:"0x10" json:"ksVersion,omitempty"` + Key Key `json:"ksKey"` + Signature Signature `json:"ksSignature"` +} +``` + +Effectively, in such case R/W and size/offsets of sub-types will be recursively determined using the common methods under the hood: +```go + { + ID: 1, + Name: "Key", + Size: func() uint64 { return s.Key.Common.TotalSize(&s.Key) }, + Value: func() any { return &s.Key }, + Type: ManifestFieldSubStruct, + }, + { + ID: 2, + Name: "Signature", + Size: func() uint64 { return s.Signature.Common.TotalSize(&s.Signature) }, + Value: func() any { return &s.Signature }, + Type: ManifestFieldSubStruct, + }, +``` +As implementing entity of a type that contains fields with sub-types, there is no need to concern about **how** sub-type will perform R/W and size/offset determination, as +long it is known that its layout is correctly defined. + +> [!NOTE] +> for all dynamic fields, keep `Size()` aligned with the exact binary representation consumed/written by that field type. + +### Field Type Quick Reference +As described above, `Common.ReadFrom()` and `Common.WriteTo()` dispatch behavior by `LayoutField.Type`: + +* `ManifestFieldEndValue`: plain fixed-size scalar value. +* `ManifestFieldArrayStatic`: fixed-size array. +* `ManifestFieldArrayDynamicWithSize`: dynamic byte array with externally defined size. +* `ManifestFieldArrayDynamicWithPrefix`: dynamic byte array with encoded size prefix. +* `ManifestFieldList`: custom list logic via `ReadList`/`WriteList`. +* `ManifestFieldSubStruct`: nested structure implementing `io.ReaderFrom`/`io.WriterTo`. + +### Adding a Structure +This will be showcased on imaginary structure, let's call it `X`. + +1. Declare the struct (in the example we will use all `ManifestFieldType` variants): +```go +// Represents X structure as defined in document #nnnnnn +type X struct { + Common + UUID [16]byte `json:"superUUIDforXstruct"` + Version uint8 `require:"0x32" json:"versionX"` + Signature Signature `json:"signatureX"` + HashList []HashStructure `json:"hlList"` + SizeOfData1 BitSize `json:"szData1X"` + Data1 []byte `json:"data1X"` + Data2 []byte `json:"data2X"` +} +``` + +2. Define `Layout()` in strict on-wire order and map each field to a `ManifestFieldType`: +```go +func (s *X) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "UUID", + Size: func() uint64 { return 16 }, + Value: func() any { return &s.UUID }, + Type: ManifestFieldArrayStatic, + }, + { + ID: 1, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Version }, + Type: ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Signature", + Size: func() uint64 { return s.Signature.Common.TotalSize(&s.Signature) }, + Value: func() any { return &s.Signature }, + Type: ManifestFieldSubStruct, + }, + { + ID: 3, + Name: fmt.Sprintf("Hash List: length %d", len(s.HashList)), + Size: func() uint64 { + size := uint64(binary.Size(uint16(0))) + for idx := range s.HashList { + size += s.HashList[idx].TotalSize() + } + return size + }, + Value: func() any { return &s.HashList }, + Type: ManifestFieldList, + ReadList: func(r io.Reader) (int64, error) { + var count uint16 + if err := binary.Read(r, endianess, &count); err != nil { + return 0, fmt.Errorf("unable to read count for field 'HashList': %w", err) + } + totalN := int64(binary.Size(count)) + s.HashList = make([]HashStructure, count) + for idx := range s.HashList { + n, err := s.HashList[idx].ReadFrom(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field 'HashList[%d]': %w", idx, err) + } + totalN += n + } + return totalN, nil + }, + WriteList: func(w io.Writer) (int64, error) { + count := uint16(len(s.HashList)) + if err := binary.Write(w, endianess, &count); err != nil { + return 0, fmt.Errorf("unable to write count for field 'HashList': %w", err) + } + totalN := int64(binary.Size(count)) + for idx := range s.HashList { + n, err := s.HashList[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'HashList[%d]': %w", idx, err) + } + totalN += n + } + return totalN, nil + }, + }, + { + ID: 4, + Name: "SizeOfData1", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.SizeOfData1 }, + Type: ManifestFieldEndValue, + }, + { + ID: 5, + Name: "Data1", + Size: func() uint64 { return uint64(&s.SizeOfData1) }, + Value: func() any { return &s.Data1 }, + Type: ManifestFieldArrayDynamicWithSize, + }, + { + ID: 6, + Name: "Data2", + Size: func() uint64 { return uint64(binary.Size(uint16(0)) + len(s.Data2)) }, + Value: func() any { return &s.Data2 }, + Type: ManifestFieldArrayDynamicWithPrefix, + }, + } +} +``` + +3. Keep common methods delegated to `Common`: +```go +func (s *X) ReadFrom(r io.Reader) (int64, error) { return s.Common.ReadFrom(r, s) } +func (s *X) WriteTo(w io.Writer) (int64, error) { return s.Common.WriteTo(w, s) } +func (s *X) TotalSize() uint64 { return s.Common.TotalSize(s) } +func (s *X) SizeOf(id int) (uint64, error) { return s.Common.SizeOf(s, id) } +func (s *X) OffsetOf(id int) (uint64, error) { return s.Common.OffsetOf(s, id) } +func (s *X) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "X", opts...) +} +``` + +4. Implement `Validate()` and/or `Rehash()` if any values are derived from other fields, for example: +```go +func (s *X) Rehash() { + s.SizeOfData1 = BitSize(len(s.Data1)) +} + +func (s *X) Validate() error { + if int(s.SizeOfData1) != len(s.Data1) { + return fmt.Errorf("field 'SizeOfData1' expects %d, but has %d", len(s.Data1), s.SizeOfData1) + } + return nil +} +``` +If you need a completely new field behavior beyond existing `ManifestFieldType` values, +extend the `ManifestFieldType` constants and add corresponding handling in both +`Common.ReadFrom()` and `Common.WriteTo()`. + +### Extending a Structure +Let's take `SECBnT` as an example here, and assume that the update specification adds a field that stores the size of `IBBSegments`. Then we need to adapt the following: +1. Type definition +```go +type SECBnT struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__IBBS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` + Reserved0 [1]byte `require:"0" json:"seReserved0,omitempty"` + SetNumber uint8 `require:"0" json:"seSetNumber,omitempty"` + Reserved1 [1]byte `require:"0" json:"seReserved1,omitempty"` + PBETValue PBETValue `json:"sePBETValue"` + Flags SEFlags `json:"seFlags"` + IBBMCHBAR uint64 `json:"seIBBMCHBAR"` + VTdBAR uint64 `json:"seVTdBAR"` + DMAProtBase0 uint32 `json:"seDMAProtBase0"` + DMAProtLimit0 uint32 `json:"seDMAProtLimit0"` + DMAProtBase1 uint64 `json:"seDMAProtBase1"` + DMAProtLimit1 uint64 `json:"seDMAProtLimit1"` + PostIBBHash cbnt.HashStructure `json:"sePostIBBHash"` + IBBEntryPoint uint32 `json:"seIBBEntry"` + DigestList cbnt.HashList `json:"seDigestList"` + OBBHash cbnt.HashStructure `json:"seOBBHash"` + Reserved2 [3]byte `require:"0" json:"seReserved2,omitempty"` + // NEW: size of IBBSegments + SizeOfIBBSeg [2]byte `json:"seSizeOfIBBSeg,omitemptu"` + IBBSegments []IBBSegment `countType:"uint8" json:"seIBBSegments,omitempty"` +} +``` + +2. Layout descriptor +```go +func (s *SECBnT) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + ... + { + ID: 16, + Name: "Reserved 2", + Size: func() uint64 { return 3 }, + Value: func() any { return &s.Reserved2 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + // New entry + { + ID: 17, + Name: "Size of IBB Segments", + Size: func() uint64 { return 2 }, + Value: func() any { return s.IBBSegments.TotalSize() }, // Yes, this example makes little sense, but it is more about the mechanics of the approach than logics of specification. + Type: cbnt.ManifestFieldArrayDynamicWithSize, + } + { + ID: 18, // Incremented + Name: fmt.Sprintf("IBBSegments: Array of \"IBB Segments Element\" of length %d", len(s.IBBSegments)), + Size: func() uint64 { + ... + } +} +``` + +3. Any affected API calls + +`SizeOf` and `OffsetOf` methods depend on the ID of a field. Thus, after modifying the layout descriptor, these have to be adjusted. + -If you need to edit the template, please edit file: `./common/manifestcodegen/cmd/manifestcodegen/template_methods.tpl.go`. +## Testing -# Field tags +There are two types of tests used for the metadata related packages: -There are few special struct field tags which are recognized by the code -generator: -* `id` -- defines the element Structure ID string (for example `__TXTS__`). -* `version` -- defines the value of `StructVersion` (see document #575623). -* `countType` -- used only for slices and it defines which variable type is - used to store amount of items of the slice. Arrays in a structure in a Manifest - is almost always prepended with a count variable, and we automatically map - it to the real amount of elements of our slice. And to do that we need to know - the bitsize of the counter, therefore this tag exists. -* `countValue` -- (see also `countType`) sometimes a counter requires special - transformations before it could be maped into the real amount of elements - of a slice. `countValue` allows to define a function to calculate the - real count value. -* `require` -- defines the value required by the document #575623. -* `default` -- defines the default value. -* `prettyValue` -- defines the function which prints the value in a pretty format. -* `rehashValue` -- is used to receive an auto-updated value, for example it could - be handy to automatically update size-fields. +- Unit Tests: for the structures in the `cbnt` packages. +- Integration Tests: for manifests. These are further described in a dedicated [README](/pkg/intel/metadata/common/integration/README.md). -See also: +To run all the tests: +```bash +go test ./cbnt ``` -grep -RIn field.TagGet ./ -``` \ No newline at end of file diff --git a/pkg/intel/metadata/bg/bgbootpolicy/bpmh.go b/pkg/intel/metadata/bg/bgbootpolicy/bpmh.go deleted file mode 100644 index d1619d1e..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/bpmh.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bgbootpolicy - -import "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - -type BPMH struct { - StructInfo `id:"__ACBP__" version:"0x10"` - - HdrStructVersion uint8 `json:"HdrStructVersion"` - - PMBPMVersion uint8 `json:"bpmhRevision"` - - // PrettyString: BPM SVN - BPMSVN bg.SVN `json:"bpmhSNV"` - // PrettyString: ACM SVN Auth - ACMSVNAuth bg.SVN `json:"bpmhACMSVN"` - - Reserved0 [1]byte `require:"0" json:"bpmhReserved0,omitempty"` - - NEMDataStack Size4K `json:"bpmhNEMStackSize"` -} - -// Size4K is a size in units of 4096 bytes. -type Size4K uint16 - -// InBytes returns the size in bytes. -func (s Size4K) InBytes() uint32 { - return uint32(s) * 4096 -} - -// NewSize4K returns the given size as multiple of 4K -func NewSize4K(size uint32) Size4K { - return Size4K(size / 4096) -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/bpmh_manifestcodegen.go b/pkg/intel/metadata/bg/bgbootpolicy/bpmh_manifestcodegen.go deleted file mode 100644 index 8ab2faf3..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/bpmh_manifestcodegen.go +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy - -package bgbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = bg.StructInfo{} -) - -// NewBPMH returns a new instance of BPMH with -// all default values set. -func NewBPMH() *BPMH { - s := &BPMH{} - copy(s.StructInfo.ID[:], []byte(StructureIDBPMH)) - s.StructInfo.Version = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *BPMH) Validate() error { - // See tag "require" - for idx := range s.Reserved0 { - if s.Reserved0[idx] != 0 { - return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) - } - } - - return nil -} - -// StructureIDBPMH is the StructureID (in terms of -// the document #575623) of element 'BPMH'. -const StructureIDBPMH = "__ACBP__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *BPMH) GetStructInfo() bg.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *BPMH) SetStructInfo(newStructInfo bg.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the BPMH from 'r' in format defined in the document #575623. -func (s *BPMH) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the BPMH from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *BPMH) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // HdrStructVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.HdrStructVersion) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HdrStructVersion': %w", err) - } - totalN += int64(n) - } - - // PMBPMVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.PMBPMVersion) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PMBPMVersion': %w", err) - } - totalN += int64(n) - } - - // BPMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.BPMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'BPMSVN': %w", err) - } - totalN += int64(n) - } - - // ACMSVNAuth (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.ACMSVNAuth) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ACMSVNAuth': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // NEMDataStack (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.NEMDataStack) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'NEMDataStack': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *BPMH) RehashRecursive() { - s.StructInfo.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *BPMH) Rehash() { -} - -// WriteTo writes the BPMH into 'w' in format defined in -// the document #575623. -func (s *BPMH) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // HdrStructVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.HdrStructVersion) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HdrStructVersion': %w", err) - } - totalN += int64(n) - } - - // PMBPMVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.PMBPMVersion) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PMBPMVersion': %w", err) - } - totalN += int64(n) - } - - // BPMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.BPMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'BPMSVN': %w", err) - } - totalN += int64(n) - } - - // ACMSVNAuth (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.ACMSVNAuth) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ACMSVNAuth': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // NEMDataStack (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.NEMDataStack) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'NEMDataStack': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *BPMH) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// HdrStructVersionSize returns the size in bytes of the value of field HdrStructVersion -func (s *BPMH) HdrStructVersionTotalSize() uint64 { - return 1 -} - -// PMBPMVersionSize returns the size in bytes of the value of field PMBPMVersion -func (s *BPMH) PMBPMVersionTotalSize() uint64 { - return 1 -} - -// BPMSVNSize returns the size in bytes of the value of field BPMSVN -func (s *BPMH) BPMSVNTotalSize() uint64 { - return 1 -} - -// ACMSVNAuthSize returns the size in bytes of the value of field ACMSVNAuth -func (s *BPMH) ACMSVNAuthTotalSize() uint64 { - return 1 -} - -// Reserved0Size returns the size in bytes of the value of field Reserved0 -func (s *BPMH) Reserved0TotalSize() uint64 { - return 1 -} - -// NEMDataStackSize returns the size in bytes of the value of field NEMDataStack -func (s *BPMH) NEMDataStackTotalSize() uint64 { - return 2 -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *BPMH) StructInfoOffset() uint64 { - return 0 -} - -// HdrStructVersionOffset returns the offset in bytes of field HdrStructVersion -func (s *BPMH) HdrStructVersionOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// PMBPMVersionOffset returns the offset in bytes of field PMBPMVersion -func (s *BPMH) PMBPMVersionOffset() uint64 { - return s.HdrStructVersionOffset() + s.HdrStructVersionTotalSize() -} - -// BPMSVNOffset returns the offset in bytes of field BPMSVN -func (s *BPMH) BPMSVNOffset() uint64 { - return s.PMBPMVersionOffset() + s.PMBPMVersionTotalSize() -} - -// ACMSVNAuthOffset returns the offset in bytes of field ACMSVNAuth -func (s *BPMH) ACMSVNAuthOffset() uint64 { - return s.BPMSVNOffset() + s.BPMSVNTotalSize() -} - -// Reserved0Offset returns the offset in bytes of field Reserved0 -func (s *BPMH) Reserved0Offset() uint64 { - return s.ACMSVNAuthOffset() + s.ACMSVNAuthTotalSize() -} - -// NEMDataStackOffset returns the offset in bytes of field NEMDataStack -func (s *BPMH) NEMDataStackOffset() uint64 { - return s.Reserved0Offset() + s.Reserved0TotalSize() -} - -// Size returns the total size of the BPMH. -func (s *BPMH) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.HdrStructVersionTotalSize() - size += s.PMBPMVersionTotalSize() - size += s.BPMSVNTotalSize() - size += s.ACMSVNAuthTotalSize() - size += s.Reserved0TotalSize() - size += s.NEMDataStackTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *BPMH) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "BPMH", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Hdr Struct Version", "", &s.HdrStructVersion, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "PMBPM Version", "", &s.PMBPMVersion, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "BPM SVN", "", &s.BPMSVN, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "ACM SVN Auth", "", &s.ACMSVNAuth, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "NEM Data Stack", "", &s.NEMDataStack, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v Size4K) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Size 4 K", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", v.InBytes(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v Size4K) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the Size4K into 'w' in binary format. -func (v Size4K) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the Size4K from 'r' in binary format. -func (v Size4K) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/manifest.go b/pkg/intel/metadata/bg/bgbootpolicy/manifest.go deleted file mode 100644 index 67016104..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/manifest.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bgbootpolicy - -import ( - "bytes" - "fmt" - - pkgbytes "github.com/linuxboot/fiano/pkg/bytes" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/uefi" -) - -// StructInfo is the common header of any element. -type StructInfo = bg.StructInfo - -// PrettyString: Boot Policy Manifest -type Manifest struct { - // PrettyString: BPMH: Header - BPMH `rehashValue:"rehashedBPMH()" json:"bpmHeader"` - // PrettyString: SE: Header - SE []SE `json:"bpmSE"` - // PrettyString: PME: Platform Manufacturer - PME *PM `json:"bpmPME,omitempty"` - // PrettyString: PMSE: Signature - PMSE Signature `json:"bpmSignature"` -} - -// StructInfo is the information about how to parse the structure. -func (bpm Manifest) StructInfo() StructInfo { - return bpm.BPMH.StructInfo -} - -// ValidateIBB returns an error if IBB segments does not match the signature -func (bpm *Manifest) ValidateIBB(firmware uefi.Firmware) error { - if bpm.SE[0].Digest.TotalSize() == 0 { - return fmt.Errorf("no IBB hashes") - } - - digest := bpm.SE[0].Digest - - h, err := digest.HashAlg.Hash() - if err != nil { - return fmt.Errorf("invalid hash function: %v", digest.HashAlg) - } - - for _, _range := range bpm.IBBDataRanges(uint64(len(firmware.Buf()))) { - if _, err := h.Write(firmware.Buf()[_range.Offset:_range.End()]); err != nil { - return fmt.Errorf("unable to hash: %w", err) - } - } - hashValue := h.Sum(nil) - - if !bytes.Equal(hashValue, digest.HashBuffer) { - return fmt.Errorf("IBB %s hash mismatch: %X != %X", digest.HashAlg, hashValue, digest.HashBuffer) - } - - return nil -} - -// IBBDataRanges returns data ranges of IBB. -func (bpm *Manifest) IBBDataRanges(firmwareSize uint64) pkgbytes.Ranges { - var result pkgbytes.Ranges - - for _, seg := range bpm.SE[0].IBBSegments { - if seg.Flags&1 == 1 { - continue - } - startIdx := calculateOffsetFromPhysAddr(uint64(seg.Base), firmwareSize) - result = append(result, pkgbytes.Range{Offset: startIdx, Length: uint64(seg.Size)}) - } - - return result -} - -// calculateOffsetFromPhysAddr calculates the offset within an image -// of the physical address (address to a region mapped from -// the SPI chip). -// -// Examples: -// -// calculateOffsetFromPhysAddr(0xffffffff, 0x1000) == 0xfff -// calculateOffsetFromPhysAddr(0xffffffc0, 0x1000) == 0xfc0 -func calculateOffsetFromPhysAddr(physAddr uint64, imageSize uint64) uint64 { - const basePhysAddr = 1 << 32 // "4GiB" - startAddr := basePhysAddr - imageSize - return physAddr - startAddr -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/manifest_manifestcodegen.go b/pkg/intel/metadata/bg/bgbootpolicy/manifest_manifestcodegen.go deleted file mode 100644 index 263b4a32..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/manifest_manifestcodegen.go +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy - -package bgbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = bg.StructInfo{} -) - -// NewManifest returns a new instance of Manifest with -// all default values set. -func NewManifest() *Manifest { - s := &Manifest{} - // Recursively initializing a child structure: - s.BPMH = *NewBPMH() - // Recursively initializing a child structure: - s.PMSE = *NewSignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Manifest) Validate() error { - // Recursively validating a child structure: - if err := s.BPMH.Validate(); err != nil { - return fmt.Errorf("error on field 'BPMH': %w", err) - } - // See tag "rehashValue" - { - expectedValue := BPMH(s.rehashedBPMH()) - if s.BPMH != expectedValue { - return fmt.Errorf("field 'BPMH' expects write-value '%v', but has %v", expectedValue, s.BPMH) - } - } - // Recursively validating a child structure: - if err := s.PMSE.Validate(); err != nil { - return fmt.Errorf("error on field 'PMSE': %w", err) - } - - return nil -} - -// fieldIndexByStructID returns the position index within -// structure Manifest of the field by its StructureID -// (see document #575623, an example of StructureID value is "__KEYM__"). -func (_ Manifest) fieldIndexByStructID(structID string) int { - switch structID { - case StructureIDBPMH: - return 0 - case StructureIDSE: - return 1 - case StructureIDPM: - return 2 - case StructureIDSignature: - return 3 - } - - return -1 -} - -// fieldNameByIndex returns the name of the field by its position number -// within structure Manifest. -func (_ Manifest) fieldNameByIndex(fieldIndex int) string { - switch fieldIndex { - case 0: - return "BPMH" - case 1: - return "SE" - case 2: - return "PME" - case 3: - return "PMSE" - } - - return fmt.Sprintf("invalidFieldIndex_%d", fieldIndex) -} - -// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. -func (s *Manifest) ReadFrom(r io.Reader) (returnN int64, returnErr error) { - var missingFieldsByIndices = [4]bool{ - 0: true, - 3: true, - } - defer func() { - if returnErr != nil { - return - } - for fieldIndex, v := range missingFieldsByIndices { - if v { - returnErr = fmt.Errorf("field '%s' is missing", s.fieldNameByIndex(fieldIndex)) - break - } - } - }() - var totalN int64 - previousFieldIndex := int(-1) - for { - var structInfo bg.StructInfo - err := binary.Read(r, binary.LittleEndian, &structInfo) - if err == io.EOF || err == io.ErrUnexpectedEOF { - return totalN, nil - } - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(structInfo)) - - structID := structInfo.ID.String() - fieldIndex := s.fieldIndexByStructID(structID) - if fieldIndex < 0 { - // TODO: report error "unknown structure ID: '"+structID+"'" - continue - } - if bg.StrictOrderCheck && fieldIndex < previousFieldIndex { - return totalN, fmt.Errorf("invalid order of fields (%d < %d): structure '%s' is out of order", fieldIndex, previousFieldIndex, structID) - } - missingFieldsByIndices[fieldIndex] = false - - var n int64 - switch structID { - case StructureIDBPMH: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'BPMH' is not a slice, but multiple elements found") - } - s.BPMH.SetStructInfo(structInfo) - n, err = s.BPMH.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field BPMH at %d: %w", totalN, err) - } - case StructureIDSE: - var el SE - el.SetStructInfo(structInfo) - n, err = el.ReadDataFrom(r) - s.SE = append(s.SE, el) - if err != nil { - return totalN, fmt.Errorf("unable to read field SE at %d: %w", totalN, err) - } - case StructureIDPM: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'PME' is not a slice, but multiple elements found") - } - s.PME = &PM{} - s.PME.SetStructInfo(structInfo) - n, err = s.PME.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field PME at %d: %w", totalN, err) - } - case StructureIDSignature: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'PMSE' is not a slice, but multiple elements found") - } - s.PMSE.SetStructInfo(structInfo) - n, err = s.PMSE.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field PMSE at %d: %w", totalN, err) - } - default: - return totalN, fmt.Errorf("there is no field with structure ID '%s' in Manifest", structInfo.ID) - } - totalN += n - previousFieldIndex = fieldIndex - } -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Manifest) RehashRecursive() { - s.BPMH.Rehash() - if s.PME != nil { - s.PME.Rehash() - } - s.PMSE.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Manifest) Rehash() { - s.BPMH = BPMH(s.rehashedBPMH()) -} - -// WriteTo writes the Manifest into 'w' in format defined in -// the document #575623. -func (s *Manifest) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // BPMH (ManifestFieldType: element) - { - n, err := s.BPMH.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'BPMH': %w", err) - } - totalN += int64(n) - } - - // SE (ManifestFieldType: elementList) - { - for idx := range s.SE { - n, err := s.SE[idx].WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SE[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - // PME (ManifestFieldType: element) - if s.PME != nil { - n, err := s.PME.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PME': %w", err) - } - totalN += int64(n) - } - - // PMSE (ManifestFieldType: element) - { - n, err := s.PMSE.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PMSE': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// BPMHSize returns the size in bytes of the value of field BPMH -func (s *Manifest) BPMHTotalSize() uint64 { - return s.BPMH.TotalSize() -} - -// SESize returns the size in bytes of the value of field SE -func (s *Manifest) SETotalSize() uint64 { - var size uint64 - for idx := range s.SE { - size += s.SE[idx].TotalSize() - } - return size -} - -// PMESize returns the size in bytes of the value of field PME -func (s *Manifest) PMETotalSize() uint64 { - return s.PME.TotalSize() -} - -// PMSESize returns the size in bytes of the value of field PMSE -func (s *Manifest) PMSETotalSize() uint64 { - return s.PMSE.TotalSize() -} - -// BPMHOffset returns the offset in bytes of field BPMH -func (s *Manifest) BPMHOffset() uint64 { - return 0 -} - -// SEOffset returns the offset in bytes of field SE -func (s *Manifest) SEOffset() uint64 { - return s.BPMHOffset() + s.BPMHTotalSize() -} - -// PMEOffset returns the offset in bytes of field PME -func (s *Manifest) PMEOffset() uint64 { - return s.SEOffset() + s.SETotalSize() -} - -// PMSEOffset returns the offset in bytes of field PMSE -func (s *Manifest) PMSEOffset() uint64 { - return s.PMEOffset() + s.PMETotalSize() -} - -// Size returns the total size of the Manifest. -func (s *Manifest) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.BPMHTotalSize() - size += s.SETotalSize() - size += s.PMETotalSize() - size += s.PMSETotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Manifest) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Boot Policy Manifest", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "BPMH: Header", "", &s.BPMH, opts...)...) - // ManifestFieldType is elementList - lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("SE: Array of \"Boot Policy Manifest\" of length %d", len(s.SE)), s.SE)) - for i := 0; i < len(s.SE); i++ { - lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.SE[i].PrettyString(depth+2, true))) - } - if depth < 1 { - lines = append(lines, "") - } - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "PME: Platform Manufacturer", "", s.PME, opts...)...) - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "PMSE: Signature", "", &s.PMSE, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/manifest_nocodegen.go b/pkg/intel/metadata/bg/bgbootpolicy/manifest_nocodegen.go deleted file mode 100644 index 823a427e..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/manifest_nocodegen.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// -// To avoid errors "bpm.KeySignatureOffsetTotalSize undefined" and -// "bpm.BPMH.PrettyString undefined" we place these functions to a file -// with a build tag "!manifestcodegen" - -package bgbootpolicy - -import ( - "fmt" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -func (bpm *Manifest) rehashedBPMH() BPMH { - return bpm.BPMH -} - -// Print prints the Manifest -func (bpm Manifest) Print() { - fmt.Printf("%v", bpm.BPMH.PrettyString(1, true)) - for _, item := range bpm.SE { - fmt.Printf("%v", item.PrettyString(1, true)) - } - - if bpm.PME != nil { - fmt.Printf("%v\n", bpm.PME.PrettyString(1, true)) - } else { - fmt.Println(" --PME--\n\tnot set!(optional)") - } - - if bpm.PMSE.Signature.DataTotalSize() < 1 { - fmt.Printf("%v\n", bpm.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) - fmt.Printf(" --PMSE--\n\tBoot Policy Manifest not signed!\n\n") - } else { - fmt.Printf("%v\n", bpm.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) - } -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/manifest_test.go b/pkg/intel/metadata/bg/bgbootpolicy/manifest_test.go deleted file mode 100644 index 7c7139b4..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/manifest_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package bgbootpolicy - -import ( - "testing" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/unittest" -) - -func TestReadWrite(t *testing.T) { - unittest.BGManifestReadWrite(t, &Manifest{}, "testdata/bpm.bin") - unittest.BGManifestReadWrite(t, &Manifest{}, "testdata/bpm2.bin") - unittest.BGManifestReadWrite(t, &Manifest{}, "testdata/bpm3.bin") -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/pm.go b/pkg/intel/metadata/bg/bgbootpolicy/pm.go deleted file mode 100644 index 1ff19427..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/pm.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bgbootpolicy - -type PM struct { - StructInfo `id:"__PMDA__" version:"0x10"` - DataSize uint16 `json:"pcDataSize"` - Data []byte `json:"pcData"` -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/pm_manifestcodegen.go b/pkg/intel/metadata/bg/bgbootpolicy/pm_manifestcodegen.go deleted file mode 100644 index 8ed47f54..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/pm_manifestcodegen.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy - -package bgbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = bg.StructInfo{} -) - -// NewPM returns a new instance of PM with -// all default values set. -func NewPM() *PM { - s := &PM{} - copy(s.StructInfo.ID[:], []byte(StructureIDPM)) - s.StructInfo.Version = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *PM) Validate() error { - - return nil -} - -// StructureIDPM is the StructureID (in terms of -// the document #575623) of element 'PM'. -const StructureIDPM = "__PMDA__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *PM) GetStructInfo() bg.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *PM) SetStructInfo(newStructInfo bg.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the PM from 'r' in format defined in the document #575623. -func (s *PM) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the PM from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *PM) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // DataSize (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.DataSize) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'DataSize': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - var size uint16 - err := binary.Read(r, binary.LittleEndian, &size) - if err != nil { - return totalN, fmt.Errorf("unable to the read size of field 'Data': %w", err) - } - totalN += int64(binary.Size(size)) - s.Data = make([]byte, size) - n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *PM) RehashRecursive() { - s.StructInfo.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *PM) Rehash() { -} - -// WriteTo writes the PM into 'w' in format defined in -// the document #575623. -func (s *PM) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // DataSize (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.DataSize) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'DataSize': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - size := uint16(len(s.Data)) - err := binary.Write(w, binary.LittleEndian, size) - if err != nil { - return totalN, fmt.Errorf("unable to write the size of field 'Data': %w", err) - } - totalN += int64(binary.Size(size)) - n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *PM) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// DataSizeSize returns the size in bytes of the value of field DataSize -func (s *PM) DataSizeTotalSize() uint64 { - return 2 -} - -// DataSize returns the size in bytes of the value of field Data -func (s *PM) DataTotalSize() uint64 { - size := uint64(binary.Size(uint16(0))) - size += uint64(len(s.Data)) - return size -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *PM) StructInfoOffset() uint64 { - return 0 -} - -// DataSizeOffset returns the offset in bytes of field DataSize -func (s *PM) DataSizeOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// DataOffset returns the offset in bytes of field Data -func (s *PM) DataOffset() uint64 { - return s.DataSizeOffset() + s.DataSizeTotalSize() -} - -// Size returns the total size of the PM. -func (s *PM) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.DataSizeTotalSize() - size += s.DataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *PM) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "PM", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Data Size", "", &s.DataSize, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/se.go b/pkg/intel/metadata/bg/bgbootpolicy/se.go deleted file mode 100644 index 4490445e..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/se.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bgbootpolicy - -import ( - "fmt" - "math" - "time" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" -) - -// PrettyString: IBB Segments Element -type SE struct { - StructInfo `id:"__IBBS__" version:"0x10"` - Reserved0 [1]byte `require:"0" json:"seReserved0,omitempty"` - Reserved1 [1]byte `require:"0" json:"seReserved1,omitempty"` - PBETValue PBETValue `json:"sePBETValue"` - Flags SEFlags `json:"seFlags"` - // PrettyString: IBB MCHBAR - IBBMCHBAR uint64 `json:"seIBBMCHBAR"` - // PrettyString: VT-d BAR - VTdBAR uint64 `json:"seVTdBAR"` - // PrettyString: DMA Protection 0 Base Address - PMRLBase uint32 `json:"seDMAProtBase0"` - // PrettyString: DMA Protection 0 Limit Address - PMRLLimit uint32 `json:"seDMAProtLimit0"` - // PrettyString: DMA Protection 1 Base Address - Reserved2 [8]byte `json:"seDMAProtBase1"` - // PrettyString: DMA Protection 2 Limit Address - Reserved3 [8]byte `json:"seDMAProtLimit1"` - - PostIBBHash bg.HashStructureFill `json:"sePostIBBHash"` - - IBBEntryPoint uint32 `json:"seIBBEntry"` - - Digest bg.HashStructure `json:"seDigestList"` - - IBBSegments []IBBSegment `countType:"uint8" json:"seIBBSegments,omitempty"` -} - -type PBETValue uint8 - -// PBETValue returns the raw value of the timer setting. -func (pbet PBETValue) PBETValue() uint8 { - return uint8(pbet) & 0x0f -} - -// Duration returns the value as time.Duration. -func (pbet PBETValue) Duration() time.Duration { - v := pbet.PBETValue() - if v == 0 { - return math.MaxInt64 - } - return time.Second * time.Duration(5+v) -} - -func (pbet *PBETValue) SetDuration(duration time.Duration) time.Duration { - v := duration.Nanoseconds()/time.Second.Nanoseconds() - 5 - if v <= 0 { - v = 1 - } - if v >= 16 { - v = 0 - } - *pbet = PBETValue(v) - - return pbet.Duration() -} - -type SEFlags uint32 - -func (flags SEFlags) Reserved0() uint32 { - return uint32(flags & 0xffffffe0) -} - -// PrettyString-true: BIOS supports Top Swap remediation action -// PrettyString-false: BIOS does not support Top Swap remediation action -func (flags SEFlags) SupportsTopSwapRemediation() bool { - return flags&0x10 != 0 -} - -// PrettyString-true: Leave Hierarchies enabled. Cap all PCRs on failure. -// PrettyString-false: Do not leave enabled. Disable all Hierarchies or deactivate on failure. -func (flags SEFlags) TPMFailureLeavesHierarchiesEnabled() bool { - return flags&0x08 != 0 -} - -// PrettyString-true: Extend Authority Measurements into the Authority PCR 7 -// PrettyString-false: Do not extend into the Authority PCR 7 -func (flags SEFlags) AuthorityMeasure() bool { - return flags&0x04 != 0 -} - -// PrettyString-true: Issue TPM Start-up from Locality 3 -// PrettyString-false: Disabled -func (flags SEFlags) Locality3Startup() bool { - return flags&0x02 != 0 -} - -// PrettyString-true: Enable DMA Protection -// PrettyString-false: Disable DMA Protection -func (flags SEFlags) DMAProtection() bool { - return flags&0x01 != 0 -} - -type IBBSegment struct { - Reserved [2]byte `require:"0" json:"ibbSegReserved"` - Flags uint16 `json:"ibbSegFlags"` - Base uint32 `json:"ibbSegBase"` - Size uint32 `json:"ibbSegSize"` -} - -type CachingType uint8 - -const ( - CachingTypeWriteProtect = CachingType(iota) - CachingTypeWriteBack - CachingTypeReserved0 - CachingTypeReserved1 -) - -// String implements fmt.Stringer. -func (c CachingType) String() string { - switch c { - case CachingTypeWriteProtect: - return "write_protect" - case CachingTypeWriteBack: - return "write_back" - case CachingTypeReserved0: - return "value_0x02" - case CachingTypeReserved1: - return "value_0x03" - } - return fmt.Sprintf("unexpected_value_0x%02X", uint8(c)) -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/se_manifestcodegen.go b/pkg/intel/metadata/bg/bgbootpolicy/se_manifestcodegen.go deleted file mode 100644 index dc315619..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/se_manifestcodegen.go +++ /dev/null @@ -1,947 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy - -package bgbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = bg.StructInfo{} -) - -// NewIBBSegment returns a new instance of IBBSegment with -// all default values set. -func NewIBBSegment() *IBBSegment { - s := &IBBSegment{} - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *IBBSegment) Validate() error { - // See tag "require" - for idx := range s.Reserved { - if s.Reserved[idx] != 0 { - return fmt.Errorf("'Reserved[%d]' is expected to be 0, but it is %v", idx, s.Reserved[idx]) - } - } - - return nil -} - -// ReadFrom reads the IBBSegment from 'r' in format defined in the document #575623. -func (s *IBBSegment) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Reserved (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Flags': %w", err) - } - totalN += int64(n) - } - - // Base (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Base) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Base': %w", err) - } - totalN += int64(n) - } - - // Size (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Size) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Size': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *IBBSegment) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *IBBSegment) Rehash() { -} - -// WriteTo writes the IBBSegment into 'w' in format defined in -// the document #575623. -func (s *IBBSegment) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Reserved (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Flags': %w", err) - } - totalN += int64(n) - } - - // Base (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Base) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Base': %w", err) - } - totalN += int64(n) - } - - // Size (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Size) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Size': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// ReservedSize returns the size in bytes of the value of field Reserved -func (s *IBBSegment) ReservedTotalSize() uint64 { - return 2 -} - -// FlagsSize returns the size in bytes of the value of field Flags -func (s *IBBSegment) FlagsTotalSize() uint64 { - return 2 -} - -// BaseSize returns the size in bytes of the value of field Base -func (s *IBBSegment) BaseTotalSize() uint64 { - return 4 -} - -// SizeSize returns the size in bytes of the value of field Size -func (s *IBBSegment) SizeTotalSize() uint64 { - return 4 -} - -// ReservedOffset returns the offset in bytes of field Reserved -func (s *IBBSegment) ReservedOffset() uint64 { - return 0 -} - -// FlagsOffset returns the offset in bytes of field Flags -func (s *IBBSegment) FlagsOffset() uint64 { - return s.ReservedOffset() + s.ReservedTotalSize() -} - -// BaseOffset returns the offset in bytes of field Base -func (s *IBBSegment) BaseOffset() uint64 { - return s.FlagsOffset() + s.FlagsTotalSize() -} - -// SizeOffset returns the offset in bytes of field Size -func (s *IBBSegment) SizeOffset() uint64 { - return s.BaseOffset() + s.BaseTotalSize() -} - -// Size returns the total size of the IBBSegment. -func (s *IBBSegment) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.ReservedTotalSize() - size += s.FlagsTotalSize() - size += s.BaseTotalSize() - size += s.SizeTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *IBBSegment) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "IBB Segment", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved", "", &s.Reserved, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Flags", "", &s.Flags, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Base", "", &s.Base, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Size", "", &s.Size, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// NewSE returns a new instance of SE with -// all default values set. -func NewSE() *SE { - s := &SE{} - copy(s.StructInfo.ID[:], []byte(StructureIDSE)) - s.StructInfo.Version = 0x10 - // Recursively initializing a child structure: - s.PostIBBHash = *bg.NewHashStructureFill() - // Recursively initializing a child structure: - s.Digest = *bg.NewHashStructure() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *SE) Validate() error { - // See tag "require" - for idx := range s.Reserved0 { - if s.Reserved0[idx] != 0 { - return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) - } - } - // See tag "require" - for idx := range s.Reserved1 { - if s.Reserved1[idx] != 0 { - return fmt.Errorf("'Reserved1[%d]' is expected to be 0, but it is %v", idx, s.Reserved1[idx]) - } - } - // Recursively validating a child structure: - if err := s.PostIBBHash.Validate(); err != nil { - return fmt.Errorf("error on field 'PostIBBHash': %w", err) - } - // Recursively validating a child structure: - if err := s.Digest.Validate(); err != nil { - return fmt.Errorf("error on field 'Digest': %w", err) - } - - return nil -} - -// StructureIDSE is the StructureID (in terms of -// the document #575623) of element 'SE'. -const StructureIDSE = "__IBBS__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *SE) GetStructInfo() bg.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *SE) SetStructInfo(newStructInfo bg.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the SE from 'r' in format defined in the document #575623. -func (s *SE) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the SE from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *SE) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // Reserved1 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved1[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved1': %w", err) - } - totalN += int64(n) - } - - // PBETValue (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.PBETValue) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PBETValue': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Flags': %w", err) - } - totalN += int64(n) - } - - // IBBMCHBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Read(r, binary.LittleEndian, &s.IBBMCHBAR) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'IBBMCHBAR': %w", err) - } - totalN += int64(n) - } - - // VTdBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Read(r, binary.LittleEndian, &s.VTdBAR) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'VTdBAR': %w", err) - } - totalN += int64(n) - } - - // PMRLBase (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.PMRLBase) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PMRLBase': %w", err) - } - totalN += int64(n) - } - - // PMRLLimit (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.PMRLLimit) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PMRLLimit': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Read(r, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // Reserved3 (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Read(r, binary.LittleEndian, s.Reserved3[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved3': %w", err) - } - totalN += int64(n) - } - - // PostIBBHash (ManifestFieldType: subStruct) - { - n, err := s.PostIBBHash.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PostIBBHash': %w", err) - } - totalN += int64(n) - } - - // IBBEntryPoint (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.IBBEntryPoint) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'IBBEntryPoint': %w", err) - } - totalN += int64(n) - } - - // Digest (ManifestFieldType: subStruct) - { - n, err := s.Digest.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Digest': %w", err) - } - totalN += int64(n) - } - - // IBBSegments (ManifestFieldType: list) - { - var count uint8 - err := binary.Read(r, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to read the count for field 'IBBSegments': %w", err) - } - totalN += int64(binary.Size(count)) - s.IBBSegments = make([]IBBSegment, count) - - for idx := range s.IBBSegments { - n, err := s.IBBSegments[idx].ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'IBBSegments[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *SE) RehashRecursive() { - s.StructInfo.Rehash() - s.PostIBBHash.Rehash() - s.Digest.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *SE) Rehash() { -} - -// WriteTo writes the SE into 'w' in format defined in -// the document #575623. -func (s *SE) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // Reserved1 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved1[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved1': %w", err) - } - totalN += int64(n) - } - - // PBETValue (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.PBETValue) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PBETValue': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Flags': %w", err) - } - totalN += int64(n) - } - - // IBBMCHBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Write(w, binary.LittleEndian, &s.IBBMCHBAR) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'IBBMCHBAR': %w", err) - } - totalN += int64(n) - } - - // VTdBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Write(w, binary.LittleEndian, &s.VTdBAR) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'VTdBAR': %w", err) - } - totalN += int64(n) - } - - // PMRLBase (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.PMRLBase) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PMRLBase': %w", err) - } - totalN += int64(n) - } - - // PMRLLimit (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.PMRLLimit) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PMRLLimit': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Write(w, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // Reserved3 (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Write(w, binary.LittleEndian, s.Reserved3[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved3': %w", err) - } - totalN += int64(n) - } - - // PostIBBHash (ManifestFieldType: subStruct) - { - n, err := s.PostIBBHash.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PostIBBHash': %w", err) - } - totalN += int64(n) - } - - // IBBEntryPoint (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.IBBEntryPoint) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'IBBEntryPoint': %w", err) - } - totalN += int64(n) - } - - // Digest (ManifestFieldType: subStruct) - { - n, err := s.Digest.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Digest': %w", err) - } - totalN += int64(n) - } - - // IBBSegments (ManifestFieldType: list) - { - count := uint8(len(s.IBBSegments)) - err := binary.Write(w, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to write the count for field 'IBBSegments': %w", err) - } - totalN += int64(binary.Size(count)) - for idx := range s.IBBSegments { - n, err := s.IBBSegments[idx].WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'IBBSegments[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *SE) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// Reserved0Size returns the size in bytes of the value of field Reserved0 -func (s *SE) Reserved0TotalSize() uint64 { - return 1 -} - -// Reserved1Size returns the size in bytes of the value of field Reserved1 -func (s *SE) Reserved1TotalSize() uint64 { - return 1 -} - -// PBETValueSize returns the size in bytes of the value of field PBETValue -func (s *SE) PBETValueTotalSize() uint64 { - return 1 -} - -// FlagsSize returns the size in bytes of the value of field Flags -func (s *SE) FlagsTotalSize() uint64 { - return 4 -} - -// IBBMCHBARSize returns the size in bytes of the value of field IBBMCHBAR -func (s *SE) IBBMCHBARTotalSize() uint64 { - return 8 -} - -// VTdBARSize returns the size in bytes of the value of field VTdBAR -func (s *SE) VTdBARTotalSize() uint64 { - return 8 -} - -// PMRLBaseSize returns the size in bytes of the value of field PMRLBase -func (s *SE) PMRLBaseTotalSize() uint64 { - return 4 -} - -// PMRLLimitSize returns the size in bytes of the value of field PMRLLimit -func (s *SE) PMRLLimitTotalSize() uint64 { - return 4 -} - -// Reserved2Size returns the size in bytes of the value of field Reserved2 -func (s *SE) Reserved2TotalSize() uint64 { - return 8 -} - -// Reserved3Size returns the size in bytes of the value of field Reserved3 -func (s *SE) Reserved3TotalSize() uint64 { - return 8 -} - -// PostIBBHashSize returns the size in bytes of the value of field PostIBBHash -func (s *SE) PostIBBHashTotalSize() uint64 { - return s.PostIBBHash.TotalSize() -} - -// IBBEntryPointSize returns the size in bytes of the value of field IBBEntryPoint -func (s *SE) IBBEntryPointTotalSize() uint64 { - return 4 -} - -// DigestSize returns the size in bytes of the value of field Digest -func (s *SE) DigestTotalSize() uint64 { - return s.Digest.TotalSize() -} - -// IBBSegmentsSize returns the size in bytes of the value of field IBBSegments -func (s *SE) IBBSegmentsTotalSize() uint64 { - var size uint64 - size += uint64(binary.Size(uint8(0))) - for idx := range s.IBBSegments { - size += s.IBBSegments[idx].TotalSize() - } - return size -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *SE) StructInfoOffset() uint64 { - return 0 -} - -// Reserved0Offset returns the offset in bytes of field Reserved0 -func (s *SE) Reserved0Offset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// Reserved1Offset returns the offset in bytes of field Reserved1 -func (s *SE) Reserved1Offset() uint64 { - return s.Reserved0Offset() + s.Reserved0TotalSize() -} - -// PBETValueOffset returns the offset in bytes of field PBETValue -func (s *SE) PBETValueOffset() uint64 { - return s.Reserved1Offset() + s.Reserved1TotalSize() -} - -// FlagsOffset returns the offset in bytes of field Flags -func (s *SE) FlagsOffset() uint64 { - return s.PBETValueOffset() + s.PBETValueTotalSize() -} - -// IBBMCHBAROffset returns the offset in bytes of field IBBMCHBAR -func (s *SE) IBBMCHBAROffset() uint64 { - return s.FlagsOffset() + s.FlagsTotalSize() -} - -// VTdBAROffset returns the offset in bytes of field VTdBAR -func (s *SE) VTdBAROffset() uint64 { - return s.IBBMCHBAROffset() + s.IBBMCHBARTotalSize() -} - -// PMRLBaseOffset returns the offset in bytes of field PMRLBase -func (s *SE) PMRLBaseOffset() uint64 { - return s.VTdBAROffset() + s.VTdBARTotalSize() -} - -// PMRLLimitOffset returns the offset in bytes of field PMRLLimit -func (s *SE) PMRLLimitOffset() uint64 { - return s.PMRLBaseOffset() + s.PMRLBaseTotalSize() -} - -// Reserved2Offset returns the offset in bytes of field Reserved2 -func (s *SE) Reserved2Offset() uint64 { - return s.PMRLLimitOffset() + s.PMRLLimitTotalSize() -} - -// Reserved3Offset returns the offset in bytes of field Reserved3 -func (s *SE) Reserved3Offset() uint64 { - return s.Reserved2Offset() + s.Reserved2TotalSize() -} - -// PostIBBHashOffset returns the offset in bytes of field PostIBBHash -func (s *SE) PostIBBHashOffset() uint64 { - return s.Reserved3Offset() + s.Reserved3TotalSize() -} - -// IBBEntryPointOffset returns the offset in bytes of field IBBEntryPoint -func (s *SE) IBBEntryPointOffset() uint64 { - return s.PostIBBHashOffset() + s.PostIBBHashTotalSize() -} - -// DigestOffset returns the offset in bytes of field Digest -func (s *SE) DigestOffset() uint64 { - return s.IBBEntryPointOffset() + s.IBBEntryPointTotalSize() -} - -// IBBSegmentsOffset returns the offset in bytes of field IBBSegments -func (s *SE) IBBSegmentsOffset() uint64 { - return s.DigestOffset() + s.DigestTotalSize() -} - -// Size returns the total size of the SE. -func (s *SE) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.Reserved0TotalSize() - size += s.Reserved1TotalSize() - size += s.PBETValueTotalSize() - size += s.FlagsTotalSize() - size += s.IBBMCHBARTotalSize() - size += s.VTdBARTotalSize() - size += s.PMRLBaseTotalSize() - size += s.PMRLLimitTotalSize() - size += s.Reserved2TotalSize() - size += s.Reserved3TotalSize() - size += s.PostIBBHashTotalSize() - size += s.IBBEntryPointTotalSize() - size += s.DigestTotalSize() - size += s.IBBSegmentsTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *SE) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "IBB Segments Element", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 1", "", &s.Reserved1, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "PBET Value", "", &s.PBETValue, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Flags", "", &s.Flags, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "IBB MCHBAR", "", &s.IBBMCHBAR, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "VT-d BAR", "", &s.VTdBAR, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 0 Base Address", "", &s.PMRLBase, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 0 Limit Address", "", &s.PMRLLimit, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 1 Base Address", "", &s.Reserved2, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 2 Limit Address", "", &s.Reserved3, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Post IBB Hash", "", &s.PostIBBHash, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "IBB Entry Point", "", &s.IBBEntryPoint, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Digest", "", &s.Digest, opts...)...) - // ManifestFieldType is list - lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("IBBSegments: Array of \"IBB Segments Element\" of length %d", len(s.IBBSegments)), s.IBBSegments)) - for i := 0; i < len(s.IBBSegments); i++ { - lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.IBBSegments[i].PrettyString(depth+2, true))) - } - if depth < 1 { - lines = append(lines, "") - } - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v CachingType) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - return v.String() -} - -// TotalSize returns the total size measured through binary.Size. -func (v CachingType) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the CachingType into 'w' in binary format. -func (v CachingType) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the CachingType from 'r' in binary format. -func (v CachingType) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v PBETValue) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "PBET Value", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "PBET Value", "", v.PBETValue(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v PBETValue) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the PBETValue into 'w' in binary format. -func (v PBETValue) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the PBETValue from 'r' in binary format. -func (v PBETValue) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v SEFlags) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "SE Flags", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", v.Reserved0(), opts...)...) - if v.SupportsTopSwapRemediation() { - lines = append(lines, pretty.SubValue(depth+1, "Supports Top Swap Remediation", "BIOS supports Top Swap remediation action", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Supports Top Swap Remediation", "BIOS does not support Top Swap remediation action", false, opts...)...) - } - if v.TPMFailureLeavesHierarchiesEnabled() { - lines = append(lines, pretty.SubValue(depth+1, "TPM Failure Leaves Hierarchies Enabled", "Leave Hierarchies enabled. Cap all PCRs on failure.", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "TPM Failure Leaves Hierarchies Enabled", "Do not leave enabled. Disable all Hierarchies or deactivate on failure.", false, opts...)...) - } - if v.AuthorityMeasure() { - lines = append(lines, pretty.SubValue(depth+1, "Authority Measure", "Extend Authority Measurements into the Authority PCR 7", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Authority Measure", "Do not extend into the Authority PCR 7", false, opts...)...) - } - if v.Locality3Startup() { - lines = append(lines, pretty.SubValue(depth+1, "Locality 3 Startup", "Issue TPM Start-up from Locality 3", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Locality 3 Startup", "Disabled", false, opts...)...) - } - if v.DMAProtection() { - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection", "Enable DMA Protection", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection", "Disable DMA Protection", false, opts...)...) - } - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v SEFlags) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the SEFlags into 'w' in binary format. -func (v SEFlags) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the SEFlags from 'r' in binary format. -func (v SEFlags) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/signature.go b/pkg/intel/metadata/bg/bgbootpolicy/signature.go deleted file mode 100644 index 8e971f73..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/signature.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bgbootpolicy - -import "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - -// Signature contains the signature of the BPM. -type Signature struct { - StructInfo `id:"__PMSG__" version:"0x10"` - bg.KeySignature `json:"sigKeySignature"` -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/signature_manifestcodegen.go b/pkg/intel/metadata/bg/bgbootpolicy/signature_manifestcodegen.go deleted file mode 100644 index f3ee9732..00000000 --- a/pkg/intel/metadata/bg/bgbootpolicy/signature_manifestcodegen.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy - -package bgbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = bg.StructInfo{} -) - -// NewSignature returns a new instance of Signature with -// all default values set. -func NewSignature() *Signature { - s := &Signature{} - copy(s.StructInfo.ID[:], []byte(StructureIDSignature)) - s.StructInfo.Version = 0x10 - // Recursively initializing a child structure: - s.KeySignature = *bg.NewKeySignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Signature) Validate() error { - // Recursively validating a child structure: - if err := s.KeySignature.Validate(); err != nil { - return fmt.Errorf("error on field 'KeySignature': %w", err) - } - - return nil -} - -// StructureIDSignature is the StructureID (in terms of -// the document #575623) of element 'Signature'. -const StructureIDSignature = "__PMSG__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Signature) GetStructInfo() bg.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Signature) SetStructInfo(newStructInfo bg.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the Signature from 'r' in format defined in the document #575623. -func (s *Signature) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the Signature from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *Signature) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // KeySignature (ManifestFieldType: subStruct) - { - n, err := s.KeySignature.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeySignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Signature) RehashRecursive() { - s.StructInfo.Rehash() - s.KeySignature.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Signature) Rehash() { -} - -// WriteTo writes the Signature into 'w' in format defined in -// the document #575623. -func (s *Signature) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // KeySignature (ManifestFieldType: subStruct) - { - n, err := s.KeySignature.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeySignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *Signature) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// KeySignatureSize returns the size in bytes of the value of field KeySignature -func (s *Signature) KeySignatureTotalSize() uint64 { - return s.KeySignature.TotalSize() -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *Signature) StructInfoOffset() uint64 { - return 0 -} - -// KeySignatureOffset returns the offset in bytes of field KeySignature -func (s *Signature) KeySignatureOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// Size returns the total size of the Signature. -func (s *Signature) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.KeySignatureTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Signature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Signature", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Key Signature", "", &s.KeySignature, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm.bin b/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm.bin deleted file mode 100644 index 0eabfa3f..00000000 Binary files a/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm.bin and /dev/null differ diff --git a/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm2.bin b/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm2.bin deleted file mode 100644 index 7c991661..00000000 Binary files a/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm2.bin and /dev/null differ diff --git a/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm3.bin b/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm3.bin deleted file mode 100644 index e00cbce3..00000000 Binary files a/pkg/intel/metadata/bg/bgbootpolicy/testdata/bpm3.bin and /dev/null differ diff --git a/pkg/intel/metadata/bg/bgkey/manifest.go b/pkg/intel/metadata/bg/bgkey/manifest.go deleted file mode 100644 index 0298f12f..00000000 --- a/pkg/intel/metadata/bg/bgkey/manifest.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bgkey - -import ( - "bytes" - "crypto" - "fmt" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" -) - -// PrettyString: BG Key Manifest -type Manifest struct { - bg.StructInfo `id:"__KEYM__" version:"0x10"` - KMVersion uint8 `json:"kmVersion"` - KMSVN bg.SVN `json:"kmSVN"` - KMID uint8 `json:"kmID"` - BPKey bg.HashStructure `json:"kmBPKey"` - KeyAndSignature bg.KeySignature `json:"kmKeySignature"` -} - -func (m *Manifest) SetSignature( - algo bg.Algorithm, - privKey crypto.Signer, - signedData []byte, -) error { - err := m.KeyAndSignature.SetSignature(algo, privKey, signedData) - if err != nil { - return fmt.Errorf("unable to set the signature: %w", err) - } - - return nil -} - -func (m *Manifest) ValidateBPMKey(bpmKS bg.KeySignature) error { - h, err := m.BPKey.HashAlg.Hash() - if err != nil { - return fmt.Errorf("invalid hash algo %v: %w", m.BPKey.HashAlg, err) - } - - if len(m.BPKey.HashBuffer) != h.Size() { - return fmt.Errorf("invalid hash lenght: actual:%d expected:%d", len(m.BPKey.HashBuffer), h.Size()) - } - - switch bpmKS.Key.KeyAlg { - case bg.AlgRSA: - if _, err := h.Write(bpmKS.Key.Data[4:]); err != nil { - return fmt.Errorf("unable to hash: %w", err) - } - default: - return fmt.Errorf("unsupported key algorithm: %v", bpmKS.Key.KeyAlg) - } - digest := h.Sum(nil) - - if !bytes.Equal(m.BPKey.HashBuffer, digest) { - return fmt.Errorf("BPM key hash does not match the one in KM: actual:%X != in-KM:%X (hash algo: %v)", digest, m.BPKey.HashBuffer, m.BPKey.HashAlg) - } - - return nil -} diff --git a/pkg/intel/metadata/bg/bgkey/manifest_manifestcodegen.go b/pkg/intel/metadata/bg/bgkey/manifest_manifestcodegen.go deleted file mode 100644 index 3b2d4652..00000000 --- a/pkg/intel/metadata/bg/bgkey/manifest_manifestcodegen.go +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgkey - -package bgkey - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = bg.StructInfo{} -) - -// NewManifest returns a new instance of Manifest with -// all default values set. -func NewManifest() *Manifest { - s := &Manifest{} - copy(s.StructInfo.ID[:], []byte(StructureIDManifest)) - s.StructInfo.Version = 0x10 - // Recursively initializing a child structure: - s.BPKey = *bg.NewHashStructure() - // Recursively initializing a child structure: - s.KeyAndSignature = *bg.NewKeySignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Manifest) Validate() error { - // Recursively validating a child structure: - if err := s.BPKey.Validate(); err != nil { - return fmt.Errorf("error on field 'BPKey': %w", err) - } - // Recursively validating a child structure: - if err := s.KeyAndSignature.Validate(); err != nil { - return fmt.Errorf("error on field 'KeyAndSignature': %w", err) - } - - return nil -} - -// StructureIDManifest is the StructureID (in terms of -// the document #575623) of element 'Manifest'. -const StructureIDManifest = "__KEYM__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Manifest) GetStructInfo() bg.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Manifest) SetStructInfo(newStructInfo bg.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. -func (s *Manifest) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the Manifest from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *Manifest) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // KMVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.KMVersion) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KMVersion': %w", err) - } - totalN += int64(n) - } - - // KMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.KMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KMSVN': %w", err) - } - totalN += int64(n) - } - - // KMID (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.KMID) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KMID': %w", err) - } - totalN += int64(n) - } - - // BPKey (ManifestFieldType: subStruct) - { - n, err := s.BPKey.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'BPKey': %w", err) - } - totalN += int64(n) - } - - // KeyAndSignature (ManifestFieldType: subStruct) - { - n, err := s.KeyAndSignature.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeyAndSignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Manifest) RehashRecursive() { - s.StructInfo.Rehash() - s.BPKey.Rehash() - s.KeyAndSignature.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Manifest) Rehash() { -} - -// WriteTo writes the Manifest into 'w' in format defined in -// the document #575623. -func (s *Manifest) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // KMVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.KMVersion) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KMVersion': %w", err) - } - totalN += int64(n) - } - - // KMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.KMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KMSVN': %w", err) - } - totalN += int64(n) - } - - // KMID (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.KMID) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KMID': %w", err) - } - totalN += int64(n) - } - - // BPKey (ManifestFieldType: subStruct) - { - n, err := s.BPKey.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'BPKey': %w", err) - } - totalN += int64(n) - } - - // KeyAndSignature (ManifestFieldType: subStruct) - { - n, err := s.KeyAndSignature.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeyAndSignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *Manifest) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// KMVersionSize returns the size in bytes of the value of field KMVersion -func (s *Manifest) KMVersionTotalSize() uint64 { - return 1 -} - -// KMSVNSize returns the size in bytes of the value of field KMSVN -func (s *Manifest) KMSVNTotalSize() uint64 { - return 1 -} - -// KMIDSize returns the size in bytes of the value of field KMID -func (s *Manifest) KMIDTotalSize() uint64 { - return 1 -} - -// BPKeySize returns the size in bytes of the value of field BPKey -func (s *Manifest) BPKeyTotalSize() uint64 { - return s.BPKey.TotalSize() -} - -// KeyAndSignatureSize returns the size in bytes of the value of field KeyAndSignature -func (s *Manifest) KeyAndSignatureTotalSize() uint64 { - return s.KeyAndSignature.TotalSize() -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *Manifest) StructInfoOffset() uint64 { - return 0 -} - -// KMVersionOffset returns the offset in bytes of field KMVersion -func (s *Manifest) KMVersionOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// KMSVNOffset returns the offset in bytes of field KMSVN -func (s *Manifest) KMSVNOffset() uint64 { - return s.KMVersionOffset() + s.KMVersionTotalSize() -} - -// KMIDOffset returns the offset in bytes of field KMID -func (s *Manifest) KMIDOffset() uint64 { - return s.KMSVNOffset() + s.KMSVNTotalSize() -} - -// BPKeyOffset returns the offset in bytes of field BPKey -func (s *Manifest) BPKeyOffset() uint64 { - return s.KMIDOffset() + s.KMIDTotalSize() -} - -// KeyAndSignatureOffset returns the offset in bytes of field KeyAndSignature -func (s *Manifest) KeyAndSignatureOffset() uint64 { - return s.BPKeyOffset() + s.BPKeyTotalSize() -} - -// Size returns the total size of the Manifest. -func (s *Manifest) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.KMVersionTotalSize() - size += s.KMSVNTotalSize() - size += s.KMIDTotalSize() - size += s.BPKeyTotalSize() - size += s.KeyAndSignatureTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Manifest) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "BG Key Manifest", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "KM Version", "", &s.KMVersion, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "KMSVN", "", &s.KMSVN, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "KMID", "", &s.KMID, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "BP Key", "", &s.BPKey, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Key And Signature", "", &s.KeyAndSignature, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/bgkey/manifest_nocodegen.go b/pkg/intel/metadata/bg/bgkey/manifest_nocodegen.go deleted file mode 100644 index a673f7cc..00000000 --- a/pkg/intel/metadata/bg/bgkey/manifest_nocodegen.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package bgkey - -import ( - "fmt" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -// Print prints the Key Manifest. -func (m *Manifest) Print() { - if m.KeyAndSignature.Signature.DataTotalSize() < 1 { - fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) - fmt.Printf(" --KeyAndSignature--\n\tKey Manifest not signed!\n\n") - } else { - fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) - } -} diff --git a/pkg/intel/metadata/bg/bgkey/manifest_test.go b/pkg/intel/metadata/bg/bgkey/manifest_test.go deleted file mode 100644 index 52952e58..00000000 --- a/pkg/intel/metadata/bg/bgkey/manifest_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package bgkey - -import ( - "testing" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/unittest" -) - -func TestReadWrite(t *testing.T) { - unittest.BGManifestReadWrite(t, &Manifest{}, "testdata/km.bin") -} diff --git a/pkg/intel/metadata/bg/config.go b/pkg/intel/metadata/bg/config.go deleted file mode 100644 index 84d83e93..00000000 --- a/pkg/intel/metadata/bg/config.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package bg - -var ( - // StrictOrderCheck defines if elements order checks should be performed. - // For example in the Boot Policy Manifest elements could be in a wrong - // order. And we still can parse it, but in this way `*Offset` methods - // could be confusing, since they will show the offset as they will - // be written (not as they were parsed). - // - // We require a strict order because it is explicitly required - // in the documentation #575623: - // - // > The order of the elements and the order of the fields within each - // > element are architectural and must be followed. - StrictOrderCheck = true -) diff --git a/pkg/intel/metadata/bg/crypto_routines.go b/pkg/intel/metadata/bg/crypto_routines.go deleted file mode 100644 index 4608ea87..00000000 --- a/pkg/intel/metadata/bg/crypto_routines.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -import ( - "crypto" - "fmt" - "hash" - "strings" - - // Required for hash.Hash return in hashInfo struct - _ "crypto/sha1" - _ "crypto/sha256" - _ "crypto/sha512" -) - -// Algorithm represents a crypto algorithm value. -type Algorithm uint16 - -const ( - AlgUnknown Algorithm = 0x0000 - AlgRSA Algorithm = 0x0001 - AlgSHA1 Algorithm = 0x0004 - AlgSHA256 Algorithm = 0x000B - AlgNull Algorithm = 0x0010 - AlgRSASSA Algorithm = 0x0014 -) - -var hashInfo = []struct { - alg Algorithm - hashFactory func() hash.Hash -}{ - {AlgSHA1, crypto.SHA1.New}, - {AlgSHA256, crypto.SHA256.New}, -} - -// IsNull returns true if a is AlgNull or zero (unset). -func (a Algorithm) IsNull() bool { - return a == AlgNull || a == AlgUnknown -} - -// Hash returns a crypto.Hash based on the given id. -// An error is returned if the given algorithm is not a hash algorithm or is not available. -func (a Algorithm) Hash() (hash.Hash, error) { - for _, info := range hashInfo { - if info.alg == a { - if info.hashFactory == nil { - return nil, fmt.Errorf("go hash algorithm #%snot available", info.alg.String()) - } - return info.hashFactory(), nil - } - } - return nil, fmt.Errorf("hash algorithm not supported: %s", a.String()) -} - -func (a Algorithm) String() string { - var s strings.Builder - var err error - switch a { - case AlgUnknown: - _, err = s.WriteString("AlgUnknown") - case AlgRSA: - _, err = s.WriteString("RSA") - case AlgSHA1: - _, err = s.WriteString("SHA1") - case AlgSHA256: - _, err = s.WriteString("SHA256") - case AlgNull: - _, err = s.WriteString("AlgNull") - case AlgRSASSA: - _, err = s.WriteString("RSASSA") - default: - return fmt.Sprintf("Alg?<%d>", int(a)) - } - if err != nil { - return fmt.Sprintf("Writing to string builder failed: %v", err) - } - return s.String() -} - -func GetAlgFromString(name string) (Algorithm, error) { - n := strings.ToUpper(name) - switch n { - case "ALGUNKNOWN": - return AlgUnknown, nil - case "RSA": - return AlgRSA, nil - case "SHA1": - return AlgSHA1, nil - case "SHA256": - return AlgSHA256, nil - case "ALGNULL": - return AlgNull, nil - case "RSASSA": - return AlgRSASSA, nil - default: - return AlgNull, fmt.Errorf("algorithm name provided unknown") - } -} diff --git a/pkg/intel/metadata/bg/crypto_routines_manifestcodegen.go b/pkg/intel/metadata/bg/crypto_routines_manifestcodegen.go deleted file mode 100644 index c6d0909a..00000000 --- a/pkg/intel/metadata/bg/crypto_routines_manifestcodegen.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg - -package bg - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v Algorithm) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - return v.String() -} - -// TotalSize returns the total size measured through binary.Size. -func (v Algorithm) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the Algorithm into 'w' in binary format. -func (v Algorithm) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the Algorithm from 'r' in binary format. -func (v Algorithm) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/bg/hash.go b/pkg/intel/metadata/bg/hash.go deleted file mode 100644 index e0ec47a3..00000000 --- a/pkg/intel/metadata/bg/hash.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -// HashStructure describes a digest. -type HashStructure struct { - HashAlg Algorithm `default:"0x10" json:"hsAlg"` - HashBuffer []byte `json:"hsBuffer"` -} - -type HashStructureFill struct { - HashAlg Algorithm `default:"0x0b" json:"hsAlg"` - HashBuffer []byte `countValue:"hashSize()" prettyValue:"hashSizePrint()" json:"hsBuffer"` -} - -func (a Algorithm) size() uint16 { - switch a { - case AlgUnknown: - return 0 - case AlgNull: - return 0 - case AlgSHA1: - return 20 - case AlgSHA256: - return 32 - default: - return 0 - } -} - -func (h HashStructureFill) hashSize() uint16 { - const hashSizeFieldLen = 2 - if h.HashAlg.IsNull() { - // Evil hack, more investigation needed - return AlgSHA256.size() + hashSizeFieldLen - } else { - return h.HashAlg.size() + hashSizeFieldLen - } -} - -func (h HashStructureFill) hashSizePrint() interface{} { - if h.HashAlg.IsNull() { - // Evil hack, more investigation needed - return make([]byte, AlgSHA256.size()) - } else { - return h.HashBuffer - } -} diff --git a/pkg/intel/metadata/bg/hash_manifestcodegen.go b/pkg/intel/metadata/bg/hash_manifestcodegen.go deleted file mode 100644 index 4a98f14c..00000000 --- a/pkg/intel/metadata/bg/hash_manifestcodegen.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg - -package bg - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewHashStructure returns a new instance of HashStructure with -// all default values set. -func NewHashStructure() *HashStructure { - s := &HashStructure{} - // Set through tag "default": - s.HashAlg = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *HashStructure) Validate() error { - - return nil -} - -// ReadFrom reads the HashStructure from 'r' in format defined in the document #575623. -func (s *HashStructure) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // HashBuffer (ManifestFieldType: arrayDynamic) - { - var size uint16 - err := binary.Read(r, binary.LittleEndian, &size) - if err != nil { - return totalN, fmt.Errorf("unable to the read size of field 'HashBuffer': %w", err) - } - totalN += int64(binary.Size(size)) - s.HashBuffer = make([]byte, size) - n, err := len(s.HashBuffer), binary.Read(r, binary.LittleEndian, s.HashBuffer) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashBuffer': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *HashStructure) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *HashStructure) Rehash() { -} - -// WriteTo writes the HashStructure into 'w' in format defined in -// the document #575623. -func (s *HashStructure) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // HashBuffer (ManifestFieldType: arrayDynamic) - { - size := uint16(len(s.HashBuffer)) - err := binary.Write(w, binary.LittleEndian, size) - if err != nil { - return totalN, fmt.Errorf("unable to write the size of field 'HashBuffer': %w", err) - } - totalN += int64(binary.Size(size)) - n, err := len(s.HashBuffer), binary.Write(w, binary.LittleEndian, s.HashBuffer) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashBuffer': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// HashAlgSize returns the size in bytes of the value of field HashAlg -func (s *HashStructure) HashAlgTotalSize() uint64 { - return 2 -} - -// HashBufferSize returns the size in bytes of the value of field HashBuffer -func (s *HashStructure) HashBufferTotalSize() uint64 { - size := uint64(binary.Size(uint16(0))) - size += uint64(len(s.HashBuffer)) - return size -} - -// HashAlgOffset returns the offset in bytes of field HashAlg -func (s *HashStructure) HashAlgOffset() uint64 { - return 0 -} - -// HashBufferOffset returns the offset in bytes of field HashBuffer -func (s *HashStructure) HashBufferOffset() uint64 { - return s.HashAlgOffset() + s.HashAlgTotalSize() -} - -// Size returns the total size of the HashStructure. -func (s *HashStructure) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.HashAlgTotalSize() - size += s.HashBufferTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *HashStructure) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Hash Structure", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Hash Alg", "", &s.HashAlg, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Hash Buffer", "", &s.HashBuffer, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// NewHashStructureFill returns a new instance of HashStructureFill with -// all default values set. -func NewHashStructureFill() *HashStructureFill { - s := &HashStructureFill{} - // Set through tag "default": - s.HashAlg = 0x0b - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *HashStructureFill) Validate() error { - - return nil -} - -// ReadFrom reads the HashStructureFill from 'r' in format defined in the document #575623. -func (s *HashStructureFill) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // HashBuffer (ManifestFieldType: arrayDynamic) - { - size := uint16(s.hashSize()) - s.HashBuffer = make([]byte, size) - n, err := len(s.HashBuffer), binary.Read(r, binary.LittleEndian, s.HashBuffer) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashBuffer': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *HashStructureFill) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *HashStructureFill) Rehash() { -} - -// WriteTo writes the HashStructureFill into 'w' in format defined in -// the document #575623. -func (s *HashStructureFill) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // HashBuffer (ManifestFieldType: arrayDynamic) - { - n, err := len(s.HashBuffer), binary.Write(w, binary.LittleEndian, s.HashBuffer) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashBuffer': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// HashAlgSize returns the size in bytes of the value of field HashAlg -func (s *HashStructureFill) HashAlgTotalSize() uint64 { - return 2 -} - -// HashBufferSize returns the size in bytes of the value of field HashBuffer -func (s *HashStructureFill) HashBufferTotalSize() uint64 { - return uint64(len(s.HashBuffer)) -} - -// HashAlgOffset returns the offset in bytes of field HashAlg -func (s *HashStructureFill) HashAlgOffset() uint64 { - return 0 -} - -// HashBufferOffset returns the offset in bytes of field HashBuffer -func (s *HashStructureFill) HashBufferOffset() uint64 { - return s.HashAlgOffset() + s.HashAlgTotalSize() -} - -// Size returns the total size of the HashStructureFill. -func (s *HashStructureFill) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.HashAlgTotalSize() - size += s.HashBufferTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *HashStructureFill) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Hash Structure Fill", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Hash Alg", "", &s.HashAlg, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Hash Buffer", "", s.hashSizePrint(), opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/key.go b/pkg/intel/metadata/bg/key.go deleted file mode 100644 index 4e66a458..00000000 --- a/pkg/intel/metadata/bg/key.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -import ( - "bytes" - "crypto" - "crypto/rsa" - "encoding/binary" - "fmt" - "math/big" -) - -// Key is a public key of an asymmetric crypto keypair. -type Key struct { - KeyAlg Algorithm `json:"keyAlg"` - Version uint8 `require:"0x10" json:"keyVersion"` - KeySize BitSize `json:"keyBitsize"` - Data []byte `countValue:"keyDataSize()" json:"keyData"` -} - -// BitSize is a size in bits. -type BitSize uint16 - -// InBits returns the size in bits. -func (ks BitSize) InBits() uint16 { - return uint16(ks) -} - -// InBytes returns the size in bytes. -func (ks BitSize) InBytes() uint16 { - return uint16(ks >> 3) -} - -// SetInBits sets the size in bits. -func (ks *BitSize) SetInBits(amountOfBits uint16) { - *ks = BitSize(amountOfBits) -} - -// SetInBytes sets the size in bytes. -func (ks *BitSize) SetInBytes(amountOfBytes uint16) { - *ks = BitSize(amountOfBytes << 3) -} - -// keyDataSize returns the expected length of Data for specified -// KeyAlg and KeySize. -func (k Key) keyDataSize() int64 { - switch k.KeyAlg { - case AlgRSA: - return int64(k.KeySize.InBytes()) + 4 - } - return -1 -} - -// PubKey parses Data into crypto.PublicKey. -func (k Key) PubKey() (crypto.PublicKey, error) { - expectedSize := int(k.keyDataSize()) - if expectedSize < 0 { - return nil, fmt.Errorf("unexpected algorithm: %s", k.KeyAlg) - } - if len(k.Data) != expectedSize { - return nil, fmt.Errorf("unexpected size: expected:%d, received %d", expectedSize, len(k.Data)) - } - - switch k.KeyAlg { - case AlgRSA: - result := &rsa.PublicKey{ - N: new(big.Int).SetBytes(reverseBytes(k.Data[4:])), - E: int(binaryOrder.Uint32(k.Data)), - } - return result, nil - } - - return nil, fmt.Errorf("unexpected TPM algorithm: %s", k.KeyAlg) -} - -func reverseBytes(b []byte) []byte { - r := make([]byte, len(b)) - for idx := range b { - r[idx] = b[len(b)-idx-1] - } - return r -} - -// SetPubKey sets Data the value corresponding to passed `key`. -func (k *Key) SetPubKey(key crypto.PublicKey) error { - k.Version = 0x10 - - switch key := key.(type) { - case *rsa.PublicKey: - k.KeyAlg = AlgRSA - n := key.N.Bytes() - k.KeySize.SetInBytes(uint16(len(n))) - k.Data = make([]byte, 4+len(n)) - binaryOrder.PutUint32(k.Data, uint32(key.E)) - copy(k.Data[4:], reverseBytes(n)) - return nil - } - - return fmt.Errorf("unexpected key type: %T", key) -} - -// PrintBPMPubKey prints the BPM public signing key hash to fuse into the Intel ME -func (k *Key) PrintBPMPubKey(bpmAlg Algorithm) error { - buf := new(bytes.Buffer) - if len(k.Data) > 1 { - hash, err := bpmAlg.Hash() - if err != nil { - return err - } - if k.KeyAlg == AlgRSA { - if err := binary.Write(buf, binary.LittleEndian, k.Data[4:]); err != nil { - return err - } - hash.Write(buf.Bytes()) - fmt.Printf(" Boot Policy Manifest Pubkey Hash: 0x%x\n", hash.Sum(nil)) - } else { - fmt.Printf(" Boot Policy Manifest Pubkey Hash: Unknown Algorithm\n") - } - } else { - fmt.Printf(" Boot Policy Pubkey Hash: No km public key set in KM\n") - } - - return nil -} - -// PrintKMPubKey prints the KM public signing key hash to fuse into the Intel ME -func (k *Key) PrintKMPubKey(kmAlg Algorithm) error { - buf := new(bytes.Buffer) - if len(k.Data) > 1 { - if k.KeyAlg == AlgRSA { - if err := binary.Write(buf, binary.LittleEndian, k.Data[4:]); err != nil { - return err - } - if err := binary.Write(buf, binary.LittleEndian, k.Data[:4]); err != nil { - return err - } - if kmAlg != AlgSHA256 { - return fmt.Errorf("KM public key hash algorithm must be SHA256") - } - hash, err := kmAlg.Hash() - if err != nil { - return err - } - hash.Write(buf.Bytes()) - fmt.Printf(" Key Manifest Pubkey Hash: 0x%x\n", hash.Sum(nil)) - // On SKL and KBL the exponent is not included in the KM hash - buf.Truncate(len(k.Data[4:])) - hash.Reset() - hash.Write(buf.Bytes()) - fmt.Printf(" Key Manifest Pubkey Hash (Skylake and Kabylake only): 0x%x\n", hash.Sum(nil)) - } else { - fmt.Printf(" Key Manifest Pubkey Hash: Unsupported Algorithm\n") - } - } else { - fmt.Printf(" Key Manifest Pubkey Hash: No km public key set in KM\n") - } - - return nil -} diff --git a/pkg/intel/metadata/bg/key_manifestcodegen.go b/pkg/intel/metadata/bg/key_manifestcodegen.go deleted file mode 100644 index 1893f1e3..00000000 --- a/pkg/intel/metadata/bg/key_manifestcodegen.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg - -package bg - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewKey returns a new instance of Key with -// all default values set. -func NewKey() *Key { - s := &Key{} - // Set through tag "required": - s.Version = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Key) Validate() error { - // See tag "require" - if s.Version != 0x10 { - return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) - } - - return nil -} - -// ReadFrom reads the Key from 'r' in format defined in the document #575623. -func (s *Key) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // KeyAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeyAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeyAlg': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - size := uint16(s.keyDataSize()) - s.Data = make([]byte, size) - n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Key) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Key) Rehash() { -} - -// WriteTo writes the Key into 'w' in format defined in -// the document #575623. -func (s *Key) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // KeyAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeyAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeyAlg': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// KeyAlgSize returns the size in bytes of the value of field KeyAlg -func (s *Key) KeyAlgTotalSize() uint64 { - return 2 -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *Key) VersionTotalSize() uint64 { - return 1 -} - -// KeySizeSize returns the size in bytes of the value of field KeySize -func (s *Key) KeySizeTotalSize() uint64 { - return 2 -} - -// DataSize returns the size in bytes of the value of field Data -func (s *Key) DataTotalSize() uint64 { - return uint64(len(s.Data)) -} - -// KeyAlgOffset returns the offset in bytes of field KeyAlg -func (s *Key) KeyAlgOffset() uint64 { - return 0 -} - -// VersionOffset returns the offset in bytes of field Version -func (s *Key) VersionOffset() uint64 { - return s.KeyAlgOffset() + s.KeyAlgTotalSize() -} - -// KeySizeOffset returns the offset in bytes of field KeySize -func (s *Key) KeySizeOffset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// DataOffset returns the offset in bytes of field Data -func (s *Key) DataOffset() uint64 { - return s.KeySizeOffset() + s.KeySizeTotalSize() -} - -// Size returns the total size of the Key. -func (s *Key) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.KeyAlgTotalSize() - size += s.VersionTotalSize() - size += s.KeySizeTotalSize() - size += s.DataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Key) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Key", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Alg", "", &s.KeyAlg, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Size", "", &s.KeySize, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v BitSize) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Bit Size", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "In Bits", "", v.InBits(), opts...)...) - lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", v.InBytes(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v BitSize) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the BitSize into 'w' in binary format. -func (v BitSize) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the BitSize from 'r' in binary format. -func (v BitSize) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/bg/key_signature.go b/pkg/intel/metadata/bg/key_signature.go deleted file mode 100644 index 560be54c..00000000 --- a/pkg/intel/metadata/bg/key_signature.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -import ( - "crypto" - "fmt" -) - -// KeySignature -type KeySignature struct { - Version uint8 `require:"0x10" json:"ksVersion,omitempty"` - Key Key `json:"ksKey"` - Signature Signature `json:"ksSignature"` -} - -// Verify verifies the builtin signature with the builtin public key. -func (ks *KeySignature) Verify(signedData []byte) error { - sig, err := ks.Signature.SignatureData() - if err != nil { - return fmt.Errorf("invalid signature: %w", err) - } - pk, err := ks.Key.PubKey() - if err != nil { - return fmt.Errorf("invalid public key: %w", err) - } - err = sig.Verify(pk, signedData) - if err != nil { - return fmt.Errorf("verification failed: %w", err) - } - return nil -} - -// SetSignature generates a signature and sets all the values of KeyManifest, -// accordingly to arguments signAlgo, privKey and signedData. -// -// if signAlgo is zero then it is detected automatically, based on the type -// of the provided private key. -func (ks *KeySignature) SetSignature(signAlgo Algorithm, privKey crypto.Signer, signedData []byte) error { - ks.Version = 0x10 - err := ks.Key.SetPubKey(privKey.Public()) - if err != nil { - return fmt.Errorf("unable to set public key: %w", err) - } - - return ks.Signature.SetSignature(signAlgo, privKey, signedData) -} - -// SetSignatureAuto generates a signature and sets all the values of KeyManifest, -// accordingly to arguments privKey and signedData. -// -// Signing algorithm will be detected automatically based on the type of the -// provided private key. -func (ks *KeySignature) SetSignatureAuto(privKey crypto.Signer, signedData []byte) error { - ks.Version = 0x10 - err := ks.Key.SetPubKey(privKey.Public()) - if err != nil { - return fmt.Errorf("unable to set public key: %w", err) - } - - return ks.SetSignature(0, privKey, signedData) -} - -// FillSignature sets a signature and all the values of KeyManifest, -// accordingly to arguments signAlgo, pubKey and signedData. -// -// if signAlgo is zero then it is detected automatically, based on the type -// of the provided private key. -func (ks *KeySignature) FillSignature(signAlgo Algorithm, pubKey crypto.PublicKey, signedData []byte, hashAlgo Algorithm) error { - ks.Version = 0x10 - err := ks.Key.SetPubKey(pubKey) - if err != nil { - return fmt.Errorf("unable to set public key: %w", err) - } - - return ks.Signature.FillSignature(signAlgo, pubKey, signedData, hashAlgo) -} diff --git a/pkg/intel/metadata/bg/key_signature_manifestcodegen.go b/pkg/intel/metadata/bg/key_signature_manifestcodegen.go deleted file mode 100644 index 807d1ec4..00000000 --- a/pkg/intel/metadata/bg/key_signature_manifestcodegen.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg - -package bg - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewKeySignature returns a new instance of KeySignature with -// all default values set. -func NewKeySignature() *KeySignature { - s := &KeySignature{} - // Set through tag "required": - s.Version = 0x10 - // Recursively initializing a child structure: - s.Key = *NewKey() - // Recursively initializing a child structure: - s.Signature = *NewSignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *KeySignature) Validate() error { - // See tag "require" - if s.Version != 0x10 { - return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) - } - // Recursively validating a child structure: - if err := s.Key.Validate(); err != nil { - return fmt.Errorf("error on field 'Key': %w", err) - } - // Recursively validating a child structure: - if err := s.Signature.Validate(); err != nil { - return fmt.Errorf("error on field 'Signature': %w", err) - } - - return nil -} - -// ReadFrom reads the KeySignature from 'r' in format defined in the document #575623. -func (s *KeySignature) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // Key (ManifestFieldType: subStruct) - { - n, err := s.Key.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Key': %w", err) - } - totalN += int64(n) - } - - // Signature (ManifestFieldType: subStruct) - { - n, err := s.Signature.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Signature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *KeySignature) RehashRecursive() { - s.Key.Rehash() - s.Signature.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *KeySignature) Rehash() { -} - -// WriteTo writes the KeySignature into 'w' in format defined in -// the document #575623. -func (s *KeySignature) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // Key (ManifestFieldType: subStruct) - { - n, err := s.Key.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Key': %w", err) - } - totalN += int64(n) - } - - // Signature (ManifestFieldType: subStruct) - { - n, err := s.Signature.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Signature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *KeySignature) VersionTotalSize() uint64 { - return 1 -} - -// KeySize returns the size in bytes of the value of field Key -func (s *KeySignature) KeyTotalSize() uint64 { - return s.Key.TotalSize() -} - -// SignatureSize returns the size in bytes of the value of field Signature -func (s *KeySignature) SignatureTotalSize() uint64 { - return s.Signature.TotalSize() -} - -// VersionOffset returns the offset in bytes of field Version -func (s *KeySignature) VersionOffset() uint64 { - return 0 -} - -// KeyOffset returns the offset in bytes of field Key -func (s *KeySignature) KeyOffset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// SignatureOffset returns the offset in bytes of field Signature -func (s *KeySignature) SignatureOffset() uint64 { - return s.KeyOffset() + s.KeyTotalSize() -} - -// Size returns the total size of the KeySignature. -func (s *KeySignature) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.VersionTotalSize() - size += s.KeyTotalSize() - size += s.SignatureTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *KeySignature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Key Signature", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Key", "", &s.Key, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Signature", "", &s.Signature, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/signature.go b/pkg/intel/metadata/bg/signature.go deleted file mode 100644 index d3d5a659..00000000 --- a/pkg/intel/metadata/bg/signature.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -import ( - "crypto" - "crypto/rand" - "fmt" -) - -var ( - // RandReader exports the rand.Reader - RandReader = rand.Reader -) - -// Signature exports the Signature structure -type Signature struct { - SigScheme Algorithm `json:"sigScheme"` - Version uint8 `require:"0x10" json:"sigVersion,omitempty"` - KeySize BitSize `json:"sigKeysize,omitempty"` - HashAlg Algorithm `json:"sigHashAlg"` - Data []byte `countValue:"KeySize.InBytes()" prettyValue:"dataPrettyValue()" json:"sigData"` -} - -func (m Signature) dataPrettyValue() interface{} { - r, _ := m.SignatureData() - return r -} - -// SignatureData parses field Data and returns the signature as one of these types: -// * SignatureRSAPSS -// * SignatureRSAASA -// * SignatureECDSA -// * SignatureSM2 -func (m Signature) SignatureData() (SignatureDataInterface, error) { - switch m.SigScheme { - case AlgRSASSA: - return SignatureRSAASA(m.Data), nil - } - - return nil, fmt.Errorf("unexpected signature scheme: %s", m.SigScheme) -} - -// SetSignatureByData sets all the fields of the structure Signature by -// accepting one of these types as the input argument `sig`: -// * SignatureRSAPSS -// * SignatureRSAASA -// * SignatureECDSA -// * SignatureSM2 -func (m *Signature) SetSignatureByData(sig SignatureDataInterface, hashAlgo Algorithm) error { - err := m.SetSignatureData(sig) - if err != nil { - return err - } - - switch sig := sig.(type) { - case SignatureRSAASA: - m.SigScheme = AlgRSASSA - if hashAlgo.IsNull() { - m.HashAlg = AlgSHA256 - } else { - m.HashAlg = hashAlgo - } - m.KeySize.SetInBytes(uint16(len(m.Data))) - default: - return fmt.Errorf("unexpected signature type: %T", sig) - } - return nil -} - -// SetSignatureData sets the value of the field Data by accepting one of these -// types as the input argument `sig`: -// * SignatureRSAPSS -// * SignatureRSAASA -// * SignatureECDSA -// * SignatureSM2 -func (m *Signature) SetSignatureData(sig SignatureDataInterface) error { - switch sig := sig.(type) { - case SignatureRSAASA: - m.Data = sig - default: - return fmt.Errorf("unexpected signature type: %T", sig) - } - return nil -} - -// SetSignature calculates the signature accordingly to arguments signAlgo, -// privKey and signedData; and sets all the fields of the structure Signature. -// -// if signAlgo is zero then it is detected automatically, based on the type -// of the provided private key. -func (m *Signature) SetSignature(signAlgo Algorithm, privKey crypto.Signer, signedData []byte) error { - m.Version = 0x10 - signData, err := NewSignatureData(signAlgo, privKey, signedData) - if err != nil { - return fmt.Errorf("unable to construct the signature data: %w", err) - } - - err = m.SetSignatureByData(signData, AlgNull) - if err != nil { - return fmt.Errorf("unable to set the signature: %w", err) - } - - return nil -} - -// FillSignature sets the signature accordingly to arguments signAlgo, -// pubKey and signedData; and sets all the fields of the structure Signature. -// -// if signAlgo is zero then it is detected automatically, based on the type -// of the provided private key. -func (m *Signature) FillSignature(signAlgo Algorithm, pubKey crypto.PublicKey, signedData []byte, hashAlgo Algorithm) error { - m.Version = 0x10 - signData, err := NewSignatureByData(signAlgo, pubKey, signedData) - if err != nil { - return fmt.Errorf("unable to construct the signature data: %w", err) - } - - err = m.SetSignatureByData(signData, hashAlgo) - if err != nil { - return fmt.Errorf("unable to set the signature: %w", err) - } - - return nil -} diff --git a/pkg/intel/metadata/bg/signature_manifestcodegen.go b/pkg/intel/metadata/bg/signature_manifestcodegen.go deleted file mode 100644 index eba005bf..00000000 --- a/pkg/intel/metadata/bg/signature_manifestcodegen.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg - -package bg - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewSignature returns a new instance of Signature with -// all default values set. -func NewSignature() *Signature { - s := &Signature{} - // Set through tag "required": - s.Version = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Signature) Validate() error { - // See tag "require" - if s.Version != 0x10 { - return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) - } - - return nil -} - -// ReadFrom reads the Signature from 'r' in format defined in the document #575623. -func (s *Signature) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // SigScheme (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.SigScheme) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'SigScheme': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - size := uint16(s.KeySize.InBytes()) - s.Data = make([]byte, size) - n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Signature) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Signature) Rehash() { -} - -// WriteTo writes the Signature into 'w' in format defined in -// the document #575623. -func (s *Signature) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // SigScheme (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.SigScheme) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SigScheme': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// SigSchemeSize returns the size in bytes of the value of field SigScheme -func (s *Signature) SigSchemeTotalSize() uint64 { - return 2 -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *Signature) VersionTotalSize() uint64 { - return 1 -} - -// KeySizeSize returns the size in bytes of the value of field KeySize -func (s *Signature) KeySizeTotalSize() uint64 { - return 2 -} - -// HashAlgSize returns the size in bytes of the value of field HashAlg -func (s *Signature) HashAlgTotalSize() uint64 { - return 2 -} - -// DataSize returns the size in bytes of the value of field Data -func (s *Signature) DataTotalSize() uint64 { - return uint64(len(s.Data)) -} - -// SigSchemeOffset returns the offset in bytes of field SigScheme -func (s *Signature) SigSchemeOffset() uint64 { - return 0 -} - -// VersionOffset returns the offset in bytes of field Version -func (s *Signature) VersionOffset() uint64 { - return s.SigSchemeOffset() + s.SigSchemeTotalSize() -} - -// KeySizeOffset returns the offset in bytes of field KeySize -func (s *Signature) KeySizeOffset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// HashAlgOffset returns the offset in bytes of field HashAlg -func (s *Signature) HashAlgOffset() uint64 { - return s.KeySizeOffset() + s.KeySizeTotalSize() -} - -// DataOffset returns the offset in bytes of field Data -func (s *Signature) DataOffset() uint64 { - return s.HashAlgOffset() + s.HashAlgTotalSize() -} - -// Size returns the total size of the Signature. -func (s *Signature) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.SigSchemeTotalSize() - size += s.VersionTotalSize() - size += s.KeySizeTotalSize() - size += s.HashAlgTotalSize() - size += s.DataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Signature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Signature", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Sig Scheme", "", &s.SigScheme, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Size", "", &s.KeySize, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Hash Alg", "", &s.HashAlg, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Data", "", s.dataPrettyValue(), opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/signature_types.go b/pkg/intel/metadata/bg/signature_types.go deleted file mode 100644 index 9cf98456..00000000 --- a/pkg/intel/metadata/bg/signature_types.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -import ( - "crypto" - "crypto/rsa" - "crypto/sha256" - "fmt" -) - -var SM2UID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38} - -// NewSignatureData returns an implementation of SignatureDataInterface, -// accordingly to signAlgo, privKey and signedData. -// -// if signAlgo is zero then it is detected automatically, based on the type -// of the provided private key. -func NewSignatureData( - signAlgo Algorithm, - privKey crypto.Signer, - signedData []byte, -) (SignatureDataInterface, error) { - if signAlgo == 0 { - // auto-detect the sign algorithm, based on the provided signing key - switch privKey.(type) { - case *rsa.PrivateKey: - signAlgo = AlgRSASSA - } - } - switch signAlgo { - case AlgRSASSA: - rsaPrivateKey, ok := privKey.(*rsa.PrivateKey) - if !ok { - return nil, fmt.Errorf("expected private RSA key (type %T), but received %T", rsaPrivateKey, privKey) - } - h := sha256.New() - _, _ = h.Write(signedData) - bpmHash := h.Sum(nil) - data, err := rsa.SignPKCS1v15(RandReader, rsaPrivateKey, crypto.SHA256, bpmHash) - if err != nil { - return nil, fmt.Errorf("unable to sign with RSASSA the data: %w", err) - } - return SignatureRSAASA(data), nil - } - - return nil, fmt.Errorf("signing algorithm '%s' is not implemented in this library", signAlgo) -} - -// NewSignatureByData returns an implementation of SignatureDataInterface, -// accordingly to signAlgo, publicKey and signedData. -// -// if signAlgo is zero then it is detected automatically, based on the type -// of the provided private key. -func NewSignatureByData( - signAlgo Algorithm, - pubKey crypto.PublicKey, - signedData []byte, -) (SignatureDataInterface, error) { - if signAlgo == 0 { - // auto-detect the sign algorithm, based on the provided signing key - switch pubKey.(type) { - case *rsa.PublicKey: - signAlgo = AlgRSASSA - } - } - switch signAlgo { - case AlgRSASSA: - return SignatureRSAASA(signedData), nil - } - return nil, fmt.Errorf("signing algorithm '%s' is not implemented in this library", signAlgo) -} - -// SignatureDataInterface is the interface which abstracts all the signature data types. -type SignatureDataInterface interface { - fmt.Stringer - - // Verify returns nil if signedData was indeed signed by key pk, and - // returns an appropriate error otherwise. - Verify(pk crypto.PublicKey, signedData []byte) error -} - -// SignatureRSAASA is RSAASA signature bytes. -type SignatureRSAASA []byte - -// String implements fmt.Stringer -func (s SignatureRSAASA) String() string { - return fmt.Sprintf("0x%X", []byte(s)) -} - -// Verify implements SignatureDataInterface. -func (s SignatureRSAASA) Verify(pkIface crypto.PublicKey, signedData []byte) error { - pk, ok := pkIface.(*rsa.PublicKey) - if !ok { - return fmt.Errorf("expected public key of type %T, but received %T", pk, pkIface) - } - - h := sha256.New() - h.Write(signedData) - hash := h.Sum(nil) - - err := rsa.VerifyPKCS1v15(pk, crypto.SHA256, hash, s) - if err != nil { - return fmt.Errorf("data was not signed by the key: %w", err) - } - - return nil -} diff --git a/pkg/intel/metadata/bg/structure.go b/pkg/intel/metadata/bg/structure.go deleted file mode 100644 index 44fbbc68..00000000 --- a/pkg/intel/metadata/bg/structure.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -import ( - "encoding/binary" - "io" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - binaryOrder = binary.LittleEndian -) - -type StructInfo struct { - ID StructureID `json:"StructInfoID"` - Version uint8 `json:"StructInfoVersion"` -} - -func (s StructInfo) StructInfo() StructInfo { - return s -} - -type StructureID [8]byte - -func (s StructureID) String() string { - return string(s[:]) -} - -type Structure interface { - io.ReaderFrom - io.WriterTo - TotalSize() uint64 - PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string -} - -type Element interface { - Structure - ReadDataFrom(r io.Reader) (int64, error) - GetStructInfo() StructInfo - SetStructInfo(StructInfo) -} - -type ElementsContainer interface { - Structure - GetFieldByStructID(structID string) interface{} -} - -type Manifest interface { - Structure -} diff --git a/pkg/intel/metadata/bg/structure_manifestcodegen.go b/pkg/intel/metadata/bg/structure_manifestcodegen.go deleted file mode 100644 index 47d05a67..00000000 --- a/pkg/intel/metadata/bg/structure_manifestcodegen.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg - -package bg - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewStructInfo returns a new instance of StructInfo with -// all default values set. -func NewStructInfo() *StructInfo { - s := &StructInfo{} - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *StructInfo) Validate() error { - - return nil -} - -// ReadFrom reads the StructInfo from 'r' in format defined in the document #575623. -func (s *StructInfo) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // ID (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Read(r, binary.LittleEndian, s.ID[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ID': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *StructInfo) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *StructInfo) Rehash() { -} - -// WriteTo writes the StructInfo into 'w' in format defined in -// the document #575623. -func (s *StructInfo) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // ID (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Write(w, binary.LittleEndian, s.ID[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ID': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// IDSize returns the size in bytes of the value of field ID -func (s *StructInfo) IDTotalSize() uint64 { - return 8 -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *StructInfo) VersionTotalSize() uint64 { - return 1 -} - -// IDOffset returns the offset in bytes of field ID -func (s *StructInfo) IDOffset() uint64 { - return 0 -} - -// VersionOffset returns the offset in bytes of field Version -func (s *StructInfo) VersionOffset() uint64 { - return s.IDOffset() + s.IDTotalSize() -} - -// Size returns the total size of the StructInfo. -func (s *StructInfo) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.IDTotalSize() - size += s.VersionTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *StructInfo) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Struct Info", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "ID", "", &s.ID, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/bg/svn.go b/pkg/intel/metadata/bg/svn.go deleted file mode 100644 index 1b6e7db1..00000000 --- a/pkg/intel/metadata/bg/svn.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package bg - -// SVN represents Security Version Number. -type SVN uint8 - -// SVN returns the Security Version Number of an SVN field -func (svn SVN) SVN() uint8 { - return uint8(svn) & 0x0f -} diff --git a/pkg/intel/metadata/bg/svn_manifestcodegen.go b/pkg/intel/metadata/bg/svn_manifestcodegen.go deleted file mode 100644 index b4e2c18e..00000000 --- a/pkg/intel/metadata/bg/svn_manifestcodegen.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen -package bg github.com/linuxboot/fiano/pkg/intel/metadata/bg - -package bg - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v SVN) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "SVN", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "SVN", "", v.SVN(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v SVN) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the SVN into 'w' in binary format. -func (v SVN) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the SVN from 'r' in binary format. -func (v SVN) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/Reserved.go b/pkg/intel/metadata/cbnt/bootpolicy/Reserved.go new file mode 100644 index 00000000..c9939096 --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/Reserved.go @@ -0,0 +1,146 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "fmt" + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +type Reserved struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__PFRS__" version:"0x21" var0:"0" var1:"uint16(s.TotalSize())"` + ReservedData [32]byte `json:"ReservedData"` +} + +// NewReserved returns a new instance of Reserved with +// all default values set. +func NewReserved() *Reserved { + // Only present in CBnT, thus we assume StructInfoCBNT. + s := &Reserved{} + copy(s.ID[:], []byte(StructureIDReserved)) + s.Version = 0x21 + s.Rehash() + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *Reserved) Validate() error { + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *Reserved) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Reserved Data", + Size: func() uint64 { return 32 }, + Value: func() any { return &s.ReservedData }, + Type: cbnt.ManifestFieldArrayStatic, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *Reserved) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("Reserved: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *Reserved) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("Reserved: %v", err) + } + + return ret, nil +} + +// StructureIDReserved is the StructureID (in terms of +// the document #575623) of element 'Reserved'. +const StructureIDReserved = "__PFRS__" + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *Reserved) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *Reserved) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// Dummy helper to comply with cbnt.Structure interface +func (s *Reserved) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the Reserved from 'r' in format defined in the document #575623. +func (s *Reserved) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *Reserved) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *Reserved) Rehash() { + s.Variable0 = 0 + s.ElementSize = uint16(s.TotalSize()) +} + +// WriteTo writes the Reserved into 'w' in format defined in +// the document #575623. +func (s *Reserved) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the Reserved. +func (s *Reserved) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *Reserved) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "Reserved", opts...) +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/bpmh.go b/pkg/intel/metadata/cbnt/bootpolicy/bpmh.go new file mode 100644 index 00000000..b66f3db8 --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/bpmh.go @@ -0,0 +1,417 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "encoding/binary" + "fmt" + "io" + "strings" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +type BPMH interface { + cbnt.Structure +} + +// NewBPMH returns a new instance of BPMH with +// all default values set. +func NewBPMH(bgv cbnt.BootGuardVersion) (BPMH, error) { + switch bgv { + case cbnt.Version10: + s := &BPMHBG{} + copy(s.ID[:], []byte(StructureIDBPMH)) + s.Version = 0x10 + return s, nil + case cbnt.Version20: + s := &BPMHCBnT{} + copy(s.ID[:], []byte(StructureIDBPMH)) + s.Version = 0x23 + s.Rehash() + return s, nil + case cbnt.Version21: + s := &BPMHCBnT{} + copy(s.ID[:], []byte(StructureIDBPMH)) + s.Version = 0x24 + s.Rehash() + return s, nil + default: + return nil, fmt.Errorf("version not supported") + } +} + +// BPMH is the header of boot policy manifest +type BPMHCBnT struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__ACBP__" version:"0x23" var0:"0x20" var1:"uint16(s.TotalSize())"` + + KeySignatureOffset uint16 `json:"bpmhKeySignatureOffset"` + + BPMRevision uint8 `json:"bpmhRevision"` + + // BPMSVN is BPM security version number + // + // PrettyString: BPM SVN + BPMSVN cbnt.SVN `json:"bpmhSNV"` + + // ACMSVNAuth is authorized ACM security version number + // + // PrettyString: ACM SVN Auth + ACMSVNAuth cbnt.SVN `json:"bpmhACMSVN"` + + Reserved0 [1]byte `require:"0" json:"bpmhReserved0,omitempty"` + + NEMDataStack Size4K `json:"bpmhNEMStackSize"` +} + +// Layout returns the structure's layout descriptor +func (s *BPMHCBnT) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Key Signature Offset", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.KeySignatureOffset }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 2, + Name: "BPM Revision", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.BPMRevision }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 3, + Name: "BPM SVN", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.BPMSVN }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 4, + Name: "ACM SVN Auth", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.ACMSVNAuth }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 5, + Name: "Reserved 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved0 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 6, + Name: "NEM Data Stack", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.NEMDataStack }, + Type: cbnt.ManifestFieldEndValue, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *BPMHCBnT) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("BPMH: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *BPMHCBnT) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("BPMH: %v", err) + } + + return ret, nil +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *BPMHCBnT) Validate() error { + // See tag "require" + for idx := range s.Reserved0 { + if s.Reserved0[idx] != 0 { + return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) + } + } + + return nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *BPMHCBnT) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *BPMHCBnT) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// Has to be here to fullfil Structure interface requirements. +// Reads the whole data. +func (s *BPMHCBnT) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the BPMH from 'r' in format defined in the document #575623. +func (s *BPMHCBnT) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *BPMHCBnT) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *BPMHCBnT) Rehash() { + s.Variable0 = 0x20 + s.ElementSize = uint16(s.Common.TotalSize(s)) +} + +// WriteTo writes the BPMH into 'w' in format defined in +// the document #575623. +func (s *BPMHCBnT) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the BPMH. +func (s *BPMHCBnT) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *BPMHCBnT) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "BPMH", opts...) +} + +type BPMHBG struct { + cbnt.StructInfoBG `id:"__ACBP__" version:"0x10"` + + HdrStructVersion uint8 `json:"HdrStructVersion"` + + PMBPMVersion uint8 `json:"bpmhRevision"` + + // PrettyString: BPM SVN + BPMSVN cbnt.SVN `json:"bpmhSNV"` + // PrettyString: ACM SVN Auth + ACMSVNAuth cbnt.SVN `json:"bpmhACMSVN"` + + Reserved0 [1]byte `require:"0" json:"bpmhReserved0,omitempty"` + + NEMDataStack Size4K `json:"bpmhNEMStackSize"` +} + +// Layout returns the structure's layout descriptor +func (s *BPMHBG) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoBG.TotalSize() }, + Value: func() any { return &s.StructInfoBG }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Hdr Structure Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.HdrStructVersion }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 2, + Name: "PMBPM Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.PMBPMVersion }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 3, + Name: "BPM SVN", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.BPMSVN }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 4, + Name: "ACM SVN Auth", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.ACMSVNAuth }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 5, + Name: "Reserved 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved0 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 6, + Name: "NEM Data Stack", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.NEMDataStack }, + Type: cbnt.ManifestFieldEndValue, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *BPMHBG) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("BPMH: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *BPMHBG) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("BPMH: %v", err) + } + + return ret, nil +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *BPMHBG) Validate() error { + for idx := range s.Reserved0 { + if s.Reserved0[idx] != 0 { + return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) + } + } + + return nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *BPMHBG) GetStructInfo() cbnt.StructInfo { + return s.StructInfoBG +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *BPMHBG) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoBG = newStructInfo.(cbnt.StructInfoBG) +} + +// Has to be here to fullfil Structure interface requirements. +// Reads the whole data. +func (s *BPMHBG) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the BPMH from 'r' in format defined in the document #575623. +func (s *BPMHBG) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// WriteTo writes the BPMH into 'w' in format defined in +// the document #575623. +func (s *BPMHBG) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the BPMH. +func (s *BPMHBG) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *BPMHBG) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "BPMH", opts...) +} + +type Size4K uint16 + +// InBytes returns the size in bytes. +func (v Size4K) InBytes() uint32 { + return uint32(v) * 4096 +} + +// NewSize4K returns the given size as multiple of 4K +func NewSize4K(size uint32) Size4K { + return Size4K(size / 4096) +} + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (v Size4K) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "Size 4 K", v)) + } + lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", v.InBytes(), opts...)...) + return strings.Join(lines, "\n") +} + +// TotalSize returns the total size measured through binary.Size. +func (v Size4K) TotalSize() uint64 { + return uint64(binary.Size(v)) +} + +// WriteTo writes the Size4K into 'w' in binary format. +func (v Size4K) WriteTo(w io.Writer) (int64, error) { + return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) +} + +// ReadFrom reads the Size4K from 'r' in binary format. +func (v Size4K) ReadFrom(r io.Reader) (int64, error) { + return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/consts.go b/pkg/intel/metadata/cbnt/bootpolicy/consts.go new file mode 100644 index 00000000..98cea6ae --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/consts.go @@ -0,0 +1,39 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +const ( + // StructureIDBPMH is the StructureID (in terms of + // the document #575623) of element 'BPMH'. + StructureIDBPMH = "__ACBP__" + + // StructureIDPCD is the StructureID (in terms of + // the document #575623) of element 'PCD'. + StructureIDPCD = "__PCDS__" + + // StructureIDPDRS is the StructureID of the PDRS + // record embedded in PCD data. + StructureIDPDRS = "__PDRS__" + + // StructureIDCNBS is the StructureID of the CNBS + // record embedded in PCD data. + StructureIDCNBS = "__CNBS__" + + // StructureIDPM is the StructureID (in terms of + // the document #575623) of element 'PM'. + StructureIDPM = "__PMDA__" + + // StructureIDSE is the StructureID (in terms of + // the document #575623) of element 'SE'. + StructureIDSE = "__IBBS__" + + // StructureIDSignature is the StructureID (in terms of + // the document #575623) of element 'Signature'. + StructureIDSignature = "__PMSG__" + + // StructureIDTXT is the StructureID (in terms of + // the document #575623) of element 'TXT'. + StructureIDTXT = "__TXTS__" +) diff --git a/pkg/intel/metadata/cbnt/bootpolicy/manifest.go b/pkg/intel/metadata/cbnt/bootpolicy/manifest.go new file mode 100644 index 00000000..3a1f88e7 --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/manifest.go @@ -0,0 +1,1176 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cbntbootpolicy provides Boot Policy Manifest and its child +// structures representation +package cbntbootpolicy + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "strings" + + pkgbytes "github.com/linuxboot/fiano/pkg/bytes" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" + "github.com/linuxboot/fiano/pkg/uefi" +) + +type Manifest interface { + cbnt.Manifest +} + +// NewManifest returns a new instance of Manifest with +// all default values set. +func NewManifest(bgv cbnt.BootGuardVersion) (Manifest, error) { + bpmh, err := NewBPMH(bgv) + if err != nil { + return nil, err + } + + pmse, err := NewSignature(bgv) + if err != nil { + return nil, err + } + + switch bgv { + case cbnt.Version10: + bgBPMH, ok := bpmh.(*BPMHBG) + if !ok { + return nil, fmt.Errorf("unexpected BPMH type %T for BG", bpmh) + } + m := &ManifestBG{BPMHBG: *bgBPMH, PMSE: *pmse} + m.Rehash() + return m, nil + case cbnt.Version20, cbnt.Version21: + cbntBPMH, ok := bpmh.(*BPMHCBnT) + if !ok { + return nil, fmt.Errorf("unexpected BPMH type %T for CBnT", bpmh) + } + m := &ManifestCBnT{BPMHCBnT: *cbntBPMH, PMSE: *pmse} + m.Rehash() + return m, nil + default: + return nil, fmt.Errorf("version not supported") + } +} + +// PrettyString: Boot Policy Manifest +type ManifestBG struct { + cbnt.Common + // PrettyString: BPMH: Header + BPMHBG `rehashValue:"rehashedBPMH()" json:"bpmHeader"` + SE []SEBG `json:"bpmSE"` + // PrettyString: PME: Platform Manufacturer + PME *PMBG `json:"bpmPME,omitempty"` + // PrettyString: PMSE: Signature + PMSE Signature `json:"bpmSignature"` +} + +// MarshalJSON implements json.Marshaler to serialize the interface-typed fields. +func (s ManifestBG) MarshalJSON() ([]byte, error) { + type signatureJSON struct { + StructInfoID cbnt.StructureID `json:"StructInfoID"` + StructInfoVersion uint8 `json:"StructInfoVersion"` + SigKeySignature cbnt.KeySignature `json:"sigKeySignature"` + } + + type manifestBGJSON struct { + BPMHBG BPMHBG `json:"bpmHeader"` + SE []SEBG `json:"bpmSE"` + PME *PMBG `json:"bpmPME,omitempty"` + PMSE signatureJSON `json:"bpmSignature"` + } + + sigInfo := cbnt.StructInfoBG{} + if info, ok := s.PMSE.StructInfo.(cbnt.StructInfoBG); ok { + sigInfo = info + } + + out := manifestBGJSON{ + BPMHBG: s.BPMHBG, + SE: s.SE, + PME: s.PME, + PMSE: signatureJSON{ + StructInfoID: sigInfo.ID, + StructInfoVersion: sigInfo.Version, + SigKeySignature: s.PMSE.KeySignature, + }, + } + + return json.Marshal(out) +} + +// UnmarshalJSON implements json.Unmarshaler to deserialize the +// interface-typed fields. +func (s *ManifestBG) UnmarshalJSON(data []byte) error { + type signatureJSON struct { + StructInfoID cbnt.StructureID `json:"StructInfoID"` + StructInfoVersion uint8 `json:"StructInfoVersion"` + SigKeySignature cbnt.KeySignature `json:"sigKeySignature"` + } + + type manifestBGJSON struct { + BPMHBG BPMHBG `json:"bpmHeader"` + SE []SEBG `json:"bpmSE"` + PME *PMBG `json:"bpmPME,omitempty"` + PMSE signatureJSON `json:"bpmSignature"` + } + + var in manifestBGJSON + if err := json.Unmarshal(data, &in); err != nil { + return err + } + + s.BPMHBG = in.BPMHBG + s.SE = in.SE + s.PME = in.PME + + structInfo := cbnt.StructInfoBG{ + ID: in.PMSE.StructInfoID, + Version: in.PMSE.StructInfoVersion, + } + if structInfo.ID == (cbnt.StructureID{}) { + copy(structInfo.ID[:], []byte(StructureIDSignature)) + } + if structInfo.Version == 0 { + structInfo.Version = 0x10 + } + + s.PMSE = Signature{ + StructInfo: structInfo, + KeySignature: in.PMSE.SigKeySignature, + } + + return nil +} + +// PrettyString: Boot Policy Manifest +type ManifestCBnT struct { + cbnt.Common + // BPMH is the header of the boot policy manifest + // + // PrettyString: BPMH: Header + BPMHCBnT `rehashValue:"rehashedBPMH()" json:"bpmHeader"` + + SE []SECBnT `json:"bpmSE"` + TXTE *TXT `json:"bpmTXTE,omitempty"` + Res *Reserved `json:"bpmReserved,omitempty"` + + // PCDE is the platform configuration data element + // + // PrettyString: PCDE: Platform Config Data + PCDE *PCD `json:"bpmPCDE,omitempty"` + + // PME is the platform manufacturer element + // + // PrettyString: PME: Platform Manufacturer + PME *PMCBnT `json:"bpmPME,omitempty"` + + // PMSE is the signature element + // + // PrettyString: PMSE: Signature + PMSE Signature `json:"bpmSignature"` +} + +// MarshalJSON implements json.Marshaler to serialize the interface-typed fields. +func (s ManifestCBnT) MarshalJSON() ([]byte, error) { + type signatureJSON struct { + StructInfoID cbnt.StructureID `json:"StructInfoID"` + StructInfoVersion uint8 `json:"StructInfoVersion"` + StructInfoVariable0 uint8 `json:"StructInfoVariable0"` + StructInfoElementSize uint16 `json:"StructInfoElementSize"` + SigKeySignature cbnt.KeySignature `json:"sigKeySignature"` + } + + type manifestCBnTJSON struct { + BPMHCBnT BPMHCBnT `json:"bpmHeader"` + SE []SECBnT `json:"bpmSE"` + TXTE *TXT `json:"bpmTXTE,omitempty"` + Res *Reserved `json:"bpmReserved,omitempty"` + PCDE *PCD `json:"bpmPCDE,omitempty"` + PME *PMCBnT `json:"bpmPME,omitempty"` + PMSE signatureJSON `json:"bpmSignature"` + } + + sigInfo := cbnt.StructInfoCBNT{} + if info, ok := s.PMSE.StructInfo.(cbnt.StructInfoCBNT); ok { + sigInfo = info + } + + out := manifestCBnTJSON{ + BPMHCBnT: s.BPMHCBnT, + SE: s.SE, + TXTE: s.TXTE, + Res: s.Res, + PCDE: s.PCDE, + PME: s.PME, + PMSE: signatureJSON{ + StructInfoID: sigInfo.ID, + StructInfoVersion: sigInfo.Version, + StructInfoVariable0: sigInfo.Variable0, + StructInfoElementSize: sigInfo.ElementSize, + SigKeySignature: s.PMSE.KeySignature, + }, + } + + return json.Marshal(out) +} + +// UnmarshalJSON implements json.Unmarshaler to deserialize the +// interface-typed fields. +func (s *ManifestCBnT) UnmarshalJSON(data []byte) error { + type signatureJSON struct { + StructInfoID cbnt.StructureID `json:"StructInfoID"` + StructInfoVersion uint8 `json:"StructInfoVersion"` + StructInfoVariable0 uint8 `json:"StructInfoVariable0"` + StructInfoElementSize uint16 `json:"StructInfoElementSize"` + SigKeySignature cbnt.KeySignature `json:"sigKeySignature"` + } + + type manifestCBnTJSON struct { + BPMHCBnT BPMHCBnT `json:"bpmHeader"` + SE []SECBnT `json:"bpmSE"` + TXTE *TXT `json:"bpmTXTE,omitempty"` + Res *Reserved `json:"bpmReserved,omitempty"` + PCDE *PCD `json:"bpmPCDE,omitempty"` + PME *PMCBnT `json:"bpmPME,omitempty"` + PMSE signatureJSON `json:"bpmSignature"` + } + + var in manifestCBnTJSON + if err := json.Unmarshal(data, &in); err != nil { + return err + } + + s.BPMHCBnT = in.BPMHCBnT + s.SE = in.SE + s.TXTE = in.TXTE + s.Res = in.Res + s.PCDE = in.PCDE + s.PME = in.PME + + structInfo := cbnt.StructInfoCBNT{ + ID: in.PMSE.StructInfoID, + Version: in.PMSE.StructInfoVersion, + Variable0: in.PMSE.StructInfoVariable0, + ElementSize: in.PMSE.StructInfoElementSize, + } + if structInfo.ID == (cbnt.StructureID{}) { + copy(structInfo.ID[:], []byte(StructureIDSignature)) + } + if structInfo.Version == 0 { + structInfo.Version = 0x20 + } + + s.PMSE = Signature{ + StructInfo: structInfo, + KeySignature: in.PMSE.SigKeySignature, + } + + return nil +} + +// fieldIndexByStructID returns the position index within +// structure Manifest of the field by its StructureID +// (see document #575623, an example of StructureID value is "__KEYM__"). +func (ManifestBG) fieldIndexByStructID(structID string) int { + switch structID { + case StructureIDBPMH: + return 0 + case StructureIDSE: + return 1 + case StructureIDPM: + return 2 + case StructureIDSignature: + return 3 + } + + return -1 +} + +// fieldNameByIndex returns the name of the field by its position number +// within structure Manifest. +func (ManifestBG) fieldNameByIndex(fieldIndex int) string { + switch fieldIndex { + case 0: + return "BPMH" + case 1: + return "SE" + case 2: + return "PME" + case 3: + return "PMSE" + } + + return fmt.Sprintf("invalidFieldIndex_%d", fieldIndex) +} + +// Validate (recursively) checks the structure if there are any unexpected values. +func (s *ManifestBG) Validate() error { + if err := s.BPMHBG.Validate(); err != nil { + return fmt.Errorf("error on field 'BPMH': %w", err) + } + expectedValue := s.rehashedBPMH() + if s.BPMHBG != expectedValue { + return fmt.Errorf("field 'BPMH' expects write-value '%v', but has %v", expectedValue, s.BPMHBG) + } + if err := s.PMSE.Validate(); err != nil { + return fmt.Errorf("error on field 'PMSE': %w", err) + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *ManifestBG) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "BPMH", + Size: func() uint64 { return s.BPMHBG.TotalSize() }, + Value: func() any { return &s.BPMHBG }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: fmt.Sprintf("SE: Array of \"Boot Policy Manifest\" of length %d", len(s.SE)), + Size: func() uint64 { + var size uint64 + for idx := range s.SE { + size += s.SE[idx].TotalSize() + } + return size + }, + Value: func() any { return &s.SE }, + Type: cbnt.ManifestFieldList, + WriteList: func(w io.Writer) (int64, error) { + totalN := int64(0) + for idx := range s.SE { + n, err := s.SE[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'SE[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + }, + { + ID: 2, + Name: "PME", + Size: func() uint64 { + if s.PME == nil { + return 0 + } + return s.PME.TotalSize() + }, + Value: func() any { + if s.PME == nil { + return nil + } + return s.PME + }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 3, + Name: "PMSE", + Size: func() uint64 { return s.PMSE.TotalSize() }, + Value: func() any { return &s.PMSE }, + Type: cbnt.ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *ManifestBG) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("Manifest: %v", err) + } + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *ManifestBG) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("Manifest: %v", err) + } + return ret, nil +} + +// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. +// Note that the BPM is a special case: we do not use common way of handling the reading here. +func (s *ManifestBG) ReadFrom(r io.Reader) (int64, error) { + var missingFieldsByIndices = [4]bool{ + 0: true, + 3: true, + } + + var err error + defer func() { + for fieldIndex, v := range missingFieldsByIndices { + if v { + err = fmt.Errorf("field '%s' is missing", s.fieldNameByIndex(fieldIndex)) + break + } + } + }() + var totalN int64 + previousFieldIndex := int(-1) + for { + var structInfo cbnt.StructInfoBG + err = binary.Read(r, binary.LittleEndian, &structInfo) + if err == io.EOF || err == io.ErrUnexpectedEOF { + return totalN, nil + } + if err != nil { + return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) + } + structID := structInfo.ID.String() + fieldIndex := s.fieldIndexByStructID(structID) + if fieldIndex < 0 { + // Let's just warn about unknown struct, no need to fail completely + fmt.Printf("warning: unknown structure ID: %s\n", structID) + continue + } + totalN += int64(binary.Size(structInfo)) + if cbnt.StrictOrderCheck && fieldIndex < previousFieldIndex { + return totalN, fmt.Errorf("invalid order of fields (%d < %d): structure '%s' is out of order", fieldIndex, previousFieldIndex, structID) + } + missingFieldsByIndices[fieldIndex] = false + + var n int64 + switch structID { + case StructureIDBPMH: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'BPMH' is not a slice, but multiple elements found") + } + s.SetStructInfo(structInfo) + n, err = s.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field BPMH at %d: %w", totalN, err) + } + case StructureIDSE: + var el SEBG + el.SetStructInfo(structInfo) + n, err = el.ReadFromHelper(r, false) + s.SE = append(s.SE, el) + if err != nil { + return totalN, fmt.Errorf("unable to read field SE at %d: %w", totalN, err) + } + case StructureIDPM: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'PME' is not a slice, but multiple elements found") + } + s.PME = &PMBG{} + s.PME.SetStructInfo(structInfo) + n, err = s.PME.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field PME at %d: %w", totalN, err) + } + case StructureIDSignature: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'PMSE' is not a slice, but multiple elements found") + } + s.PMSE.SetStructInfo(structInfo) + n, err = s.PMSE.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field PMSE at %d: %w", totalN, err) + } + default: + return totalN, fmt.Errorf("there is no field with structure ID '%s' in Manifest", structInfo.ID) + } + totalN += n + previousFieldIndex = fieldIndex + } + +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *ManifestBG) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *ManifestBG) Rehash() { + s.BPMHBG = s.rehashedBPMH() +} + +// WriteTo writes the Manifest into 'w' in format defined in +// the document #575623. +func (s *ManifestBG) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the manifest +func (s *ManifestBG) TotalSize() uint64 { + if s == nil { + return 0 + } + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *ManifestBG) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "Boot Policy Manifest", s)) + } + if s == nil { + return strings.Join(lines, "\n") + } + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "BPMH: Header", "", &s.BPMHBG, opts...)...) + // ManifestFieldType is elementList + lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("SE: Array of \"Boot Policy Manifest\" of length %d", len(s.SE)), s.SE)) + for i := 0; i < len(s.SE); i++ { + lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.SE[i].PrettyString(depth+2, true))) + } + if depth < 1 { + lines = append(lines, "") + } + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "PME: Platform Manufacturer", "", s.PME, opts...)...) + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "PMSE: Signature", "", &s.PMSE, opts...)...) + if depth < 2 { + lines = append(lines, "") + } + return strings.Join(lines, "\n") +} + +// StructInfo just returns StructInfo. +func (s *ManifestBG) StructInfo() cbnt.StructInfo { + return s.StructInfoBG +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *ManifestBG) GetStructInfo() cbnt.StructInfo { + return s.StructInfoBG +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *ManifestBG) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoBG = newStructInfo.(cbnt.StructInfoBG) +} + +// ValidateIBB returns an error if IBB segments does not match the signature +func (s *ManifestBG) ValidateIBB(firmware uefi.Firmware) error { + if s.SE[0].Digest.TotalSize() == 0 { + return fmt.Errorf("no IBB hashes") + } + + digest := s.SE[0].Digest + h, err := digest.HashAlg.Hash() + if err != nil { + return fmt.Errorf("invalid hash function: %v", digest.HashAlg) + } + + imgSize := uint64((len(firmware.Buf()))) + if ifdSize, ifdErr := FlashSizeIFD(firmware.Buf()); ifdErr == nil && ifdSize > 0 && ifdSize <= imgSize { + imgSize = ifdSize + } + + for _, r := range s.IBBDataRanges(imgSize) { + if _, err := h.Write(firmware.Buf()[r.Offset:r.End()]); err != nil { + return fmt.Errorf("unable to hash: %w", err) + } + } + hashValue := h.Sum(nil) + + if !bytes.Equal(hashValue, digest.HashBuffer) { + return fmt.Errorf("IBB %s hash mismatch: %X != %X", digest.HashAlg, hashValue, digest.HashBuffer) + } + + return nil +} + +// IBBDataRanges returns data ranges of IBB. +func (s *ManifestBG) IBBDataRanges(firmwareSize uint64) pkgbytes.Ranges { + return ibbDataRanges(s.SE[0].IBBSegments, firmwareSize) +} + +func (s *ManifestBG) rehashedBPMH() BPMHBG { + return s.BPMHBG +} + +// Print prints the Manifest +func (s ManifestBG) Print() { + fmt.Printf("%v", s.BPMHBG.PrettyString(1, true)) + for _, item := range s.SE { + fmt.Printf("%v", item.PrettyString(1, true)) + } + + if s.PME != nil { + fmt.Printf("%v\n", s.PME.PrettyString(1, true)) + } else { + fmt.Println(" --PME--\n\tnot set!(optional)") + } + + if len(s.PMSE.Signature.Data) < 1 { + fmt.Printf("%v\n", s.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) + fmt.Printf(" --PMSE--\n\tBoot Policy Manifest not signed!\n\n") + } else { + fmt.Printf("%v\n", s.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) + } +} + +// fieldIndexByStructID returns the position index within +// structure Manifest of the field by its StructureID +// (see document #575623, an example of StructureID value is "__KEYM__"). +func (ManifestCBnT) fieldIndexByStructID(structID string) int { + switch structID { + case StructureIDBPMH: + return 0 + case StructureIDSE: + return 1 + case StructureIDTXT: + return 2 + case StructureIDReserved: + return 3 + case StructureIDPCD: + return 4 + case StructureIDPM: + return 5 + case StructureIDSignature: + return 6 + } + + return -1 +} + +// fieldNameByIndex returns the name of the field by its position number +// within structure Manifest. +func (ManifestCBnT) fieldNameByIndex(fieldIndex int) string { + switch fieldIndex { + case 0: + return "BPMH" + case 1: + return "SE" + case 2: + return "TXTE" + case 3: + return "Res" + case 4: + return "PCDE" + case 5: + return "PME" + case 6: + return "PMSE" + } + + return fmt.Sprintf("invalidFieldIndex_%d", fieldIndex) +} + +// Validate (recursively) checks the structure if there are any unexpected values. +func (s *ManifestCBnT) Validate() error { + if err := s.BPMHCBnT.Validate(); err != nil { + return fmt.Errorf("error on field 'BPMH': %w", err) + } + if s.BPMHCBnT != s.rehashedBPMH() { + return fmt.Errorf("field 'BPMH' expects write-value '%v', but has %v", s.rehashedBPMH(), s.BPMHCBnT) + } + if err := s.PMSE.Validate(); err != nil { + return fmt.Errorf("error on field 'PMSE': %w", err) + } + + if s.PCDE != nil { + if err := s.PCDE.Validate(); err != nil { + return fmt.Errorf("error on field 'PCDE': %w", err) + } + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *ManifestCBnT) Layout() []cbnt.LayoutField { + // All fields marked with omitempty have to be checked for being + // empty in the clousure for Value. Otherwise we risk some nasty errors + // even with valid (i.e. compliant with the spec) Manifests. + return []cbnt.LayoutField{ + { + ID: 0, + Name: "BPMH: Header", + Size: func() uint64 { return s.BPMHCBnT.TotalSize() }, + Value: func() any { return &s.BPMHCBnT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: fmt.Sprintf("SE: Array of \"Boot Policy Manifest\" of length %d", len(s.SE)), + Size: func() uint64 { + var size uint64 + for idx := range s.SE { + size += s.SE[idx].TotalSize() + } + return size + }, + Value: func() any { return &s.SE }, + Type: cbnt.ManifestFieldList, + WriteList: func(w io.Writer) (int64, error) { + totalN := int64(0) + for idx := range s.SE { + n, err := s.SE[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'SE[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + }, + { + ID: 2, + Name: "TXTE", + Size: func() uint64 { + if s.TXTE == nil { + return 0 + } + return s.TXTE.TotalSize() + }, + Value: func() any { + if s.TXTE == nil { + return nil + } + return s.TXTE + }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 3, + Name: "Res", + Size: func() uint64 { + if s.Res == nil { + return 0 + } + return s.Res.TotalSize() + }, + Value: func() any { + if s.Res == nil { + return nil + } + return s.Res + }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 4, + Name: "PCDE: Platform Config Data", + Size: func() uint64 { + if s.PCDE == nil { + return 0 + } + return s.PCDE.TotalSize() + }, + Value: func() any { + if s.PCDE == nil { + return nil + } + return s.PCDE + }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 5, + Name: "PME: Platform Manufacturer", + Size: func() uint64 { + if s.PME == nil { + return 0 + } + return s.PME.TotalSize() + }, + Value: func() any { + if s.PME == nil { + return nil + } + return s.PME + }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 6, + Name: "PMSE: Signature", + Size: func() uint64 { return s.PMSE.TotalSize() }, + Value: func() any { return &s.PMSE }, + Type: cbnt.ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *ManifestCBnT) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("Manifest: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *ManifestCBnT) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("Manifest: %v", err) + } + + return ret, nil +} + +// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. +// Same note as above: this is an exception from the rule of using common approach. +func (s *ManifestCBnT) ReadFrom(r io.Reader) (int64, error) { + var missingFieldsByIndices = [7]bool{ + 0: true, + 6: true, + } + var err error + defer func() { + for fieldIndex, v := range missingFieldsByIndices { + if v { + err = fmt.Errorf("field '%s' is missing", s.fieldNameByIndex(fieldIndex)) + break + } + } + }() + var totalN int64 + previousFieldIndex := int(-1) + for { + var structInfo cbnt.StructInfoCBNT + err = binary.Read(r, binary.LittleEndian, &structInfo) + if err == io.EOF || err == io.ErrUnexpectedEOF { + return totalN, nil + } + if err != nil { + return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) + } + structID := structInfo.ID.String() + fieldIndex := s.fieldIndexByStructID(structID) + if fieldIndex < 0 { + fmt.Printf("warning: unknown structure ID: %s\n", structID) + continue + } + totalN += int64(binary.Size(structInfo)) + if cbnt.StrictOrderCheck && fieldIndex < previousFieldIndex { + return totalN, fmt.Errorf("invalid order of fields (%d < %d): structure '%s' is out of order", fieldIndex, previousFieldIndex, structID) + } + missingFieldsByIndices[fieldIndex] = false + + var n int64 + switch structID { + case StructureIDBPMH: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'BPMH' is not a slice, but multiple elements found") + } + s.SetStructInfo(structInfo) + n, err = s.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field BPMH at %d: %w", totalN, err) + } + case StructureIDSE: + var el SECBnT + el.SetStructInfo(structInfo) + n, err = el.ReadFromHelper(r, false) + s.SE = append(s.SE, el) + if err != nil { + return totalN, fmt.Errorf("unable to read field SE at %d: %w", totalN, err) + } + case StructureIDTXT: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'TXTE' is not a slice, but multiple elements found") + } + s.TXTE = &TXT{} + s.TXTE.SetStructInfo(structInfo) + n, err = s.TXTE.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field TXTE at %d: %w", totalN, err) + } + case StructureIDReserved: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'Res' is not a slice, but multiple elements found") + } + s.Res = &Reserved{} + s.Res.SetStructInfo(structInfo) + n, err = s.Res.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field Res at %d: %w", totalN, err) + } + case StructureIDPCD: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'PCDE' is not a slice, but multiple elements found") + } + s.PCDE = &PCD{} + s.PCDE.SetStructInfo(structInfo) + n, err = s.PCDE.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field PCDE at %d: %w", totalN, err) + } + case StructureIDPM: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'PME' is not a slice, but multiple elements found") + } + s.PME = &PMCBnT{} + s.PME.SetStructInfo(structInfo) + n, err = s.PME.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field PME at %d: %w", totalN, err) + } + case StructureIDSignature: + if fieldIndex == previousFieldIndex { + return totalN, fmt.Errorf("field 'PMSE' is not a slice, but multiple elements found") + } + s.PMSE.SetStructInfo(structInfo) + n, err = s.PMSE.ReadFromHelper(r, false) + if err != nil { + return totalN, fmt.Errorf("unable to read field PMSE at %d: %w", totalN, err) + } + default: + return totalN, fmt.Errorf("there is no field with structure ID '%s' in Manifest", structInfo.ID) + } + totalN += n + previousFieldIndex = fieldIndex + } +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *ManifestCBnT) RehashRecursive() { + s.BPMHCBnT.Rehash() + for idx := range s.SE { + s.SE[idx].RehashRecursive() + } + if s.TXTE != nil { + s.TXTE.Rehash() + } + if s.Res != nil { + s.Res.Rehash() + } + if s.PCDE != nil { + s.PCDE.Rehash() + } + if s.PME != nil { + s.PME.Rehash() + } + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *ManifestCBnT) Rehash() { + s.BPMHCBnT = s.rehashedBPMH() +} + +// WriteTo writes the Manifest into 'w' in format defined in +// the document #575623. +func (s *ManifestCBnT) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the manifest +func (s *ManifestCBnT) TotalSize() uint64 { + if s == nil { + return 0 + } + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *ManifestCBnT) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "Boot Policy Manifest", s)) + } + if s == nil { + return strings.Join(lines, "\n") + } + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "BPMH: Header", "", &s.BPMHCBnT, opts...)...) + // ManifestFieldType is elementList + lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("SE: Array of \"Boot Policy Manifest\" of length %d", len(s.SE)), s.SE)) + for i := 0; i < len(s.SE); i++ { + lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.SE[i].PrettyString(depth+2, true))) + } + if depth < 1 { + lines = append(lines, "") + } + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "TXTE", "", s.TXTE, opts...)...) + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "Res", "", s.Res, opts...)...) + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "PCDE: Platform Config Data", "", s.PCDE, opts...)...) + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "PME: Platform Manufacturer", "", s.PME, opts...)...) + // ManifestFieldType is element + lines = append(lines, pretty.SubValue(depth+1, "PMSE: Signature", "", &s.PMSE, opts...)...) + if depth < 2 { + lines = append(lines, "") + } + return strings.Join(lines, "\n") +} + +// StructInfo just returns StructInfo. +func (s *ManifestCBnT) StructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *ManifestCBnT) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *ManifestCBnT) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// ValidateIBB returns an error if IBB segments does not match the signature. +func (s *ManifestCBnT) ValidateIBB(firmware uefi.Firmware) error { + if len(s.SE[0].DigestList.List) == 0 { + return fmt.Errorf("no IBB hashes") + } + + digest := s.SE[0].DigestList.List[0] + h, err := digest.HashAlg.Hash() + if err != nil { + return fmt.Errorf("invalid hash function: %v", digest.HashAlg) + } + + imgSize := uint64((len(firmware.Buf()))) + if ifdSize, ifdErr := FlashSizeIFD(firmware.Buf()); ifdErr == nil && ifdSize > 0 && ifdSize <= imgSize { + imgSize = ifdSize + } + + for _, r := range s.IBBDataRanges(imgSize) { + if _, err := h.Write(firmware.Buf()[r.Offset:r.End()]); err != nil { + return fmt.Errorf("unable to hash: %w", err) + } + } + hashValue := h.Sum(nil) + + if !bytes.Equal(hashValue, digest.HashBuffer) { + return fmt.Errorf("IBB %s hash mismatch: %X != %X", digest.HashAlg, hashValue, digest.HashBuffer) + } + + return nil +} + +// IBBDataRanges returns data ranges of IBB. +func (s *ManifestCBnT) IBBDataRanges(firmwareSize uint64) pkgbytes.Ranges { + return ibbDataRanges(s.SE[0].IBBSegments, firmwareSize) +} + +// Helper for IBBDataRanges. Moved to the separate func cause the logic is shared between +// CBnT and BG. +func ibbDataRanges(segments []IBBSegment, firmwareSize uint64) pkgbytes.Ranges { + var result pkgbytes.Ranges + + for _, seg := range segments { + if seg.Flags&1 == 1 { + continue + } + startIdx := CalculateOffsetFromPhysAddr(uint64(seg.Base), firmwareSize) + result = append(result, pkgbytes.Range{Offset: startIdx, Length: uint64(seg.Size)}) + } + + return result +} + +// CalculateOffsetFromPhysAddr calculates the offset within an image of a physical address. +func CalculateOffsetFromPhysAddr(physAddr uint64, imageSize uint64) uint64 { + const basePhysAddr = 1 << 32 + startAddr := basePhysAddr - imageSize + return physAddr - startAddr +} + +// FlashSizeIFD returns the size of the BIOS flash area calculated from IFD entries. +func FlashSizeIFD(buf []byte) (uint64, error) { + if uint64(len(buf)) < uefi.FlashDescriptorLength { + return 0, fmt.Errorf("buffer too small for flash descriptior: %d", len(buf)) + } + + fd := uefi.FlashDescriptor{} + fd.SetBuf(buf[:uefi.FlashDescriptorLength]) + if err := fd.ParseFlashDescriptor(); err != nil { + return 0, err + } + + var maxEnd uint64 + for _, fr := range fd.Region.FlashRegions { + if !fr.Valid() { + continue + } + end := uint64(fr.EndOffset()) + if end > maxEnd { + maxEnd = end + } + } + if maxEnd == 0 { + return 0, fmt.Errorf("no valid regions in flash descriptor") + } + + return maxEnd, nil + +} + +func (s *ManifestCBnT) rehashedBPMH() BPMHCBnT { + bpmh := s.BPMHCBnT + pmseOffs, _ := s.OffsetOf(6) + keySigOffs, _ := s.PMSE.OffsetOf(1) + bpmh.KeySignatureOffset = uint16(pmseOffs + keySigOffs) + return bpmh +} + +// Print prints the Manifest +func (s ManifestCBnT) Print() { + fmt.Printf("%v", s.BPMHCBnT.PrettyString(1, true)) + for _, item := range s.SE { + fmt.Printf("%v", item.PrettyString(1, true)) + } + if s.TXTE != nil { + fmt.Printf("%v\n", s.TXTE.PrettyString(1, true)) + } else { + fmt.Printf(" --TXTE--\n\t not set!(optional)\n") + } + + if s.PCDE != nil { + fmt.Printf("%v\n", s.PCDE.PrettyString(1, true)) + } else { + fmt.Println(" --PCDE-- \n\tnot set!(optional)") + } + + if s.PME != nil { + fmt.Printf("%v\n", s.PME.PrettyString(1, true)) + } else { + fmt.Println(" --PME--\n\tnot set!(optional)") + } + + if len(s.PMSE.Signature.Data) < 1 { + fmt.Printf("%v\n", s.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) + fmt.Printf(" --PMSE--\n\tBoot Policy Manifest not signed!\n\n") + } else { + fmt.Printf("%v\n", s.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) + } +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/manifest_test.go b/pkg/intel/metadata/cbnt/bootpolicy/manifest_test.go new file mode 100644 index 00000000..a2996b8b --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/manifest_test.go @@ -0,0 +1,36 @@ +// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "testing" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/integration" +) + +func TestReadWriteBG(t *testing.T) { + m, err := NewManifest(cbnt.Version10) + if err != nil { + t.Fatalf("%v", err) + } + integration.ManifestReadWrite(t, m, "testdata/bpm10.bin") +} + +func TestReadWriteCBNT(t *testing.T) { + m, err := NewManifest(cbnt.Version20) + if err != nil { + t.Fatalf("%v", err) + } + integration.ManifestReadWrite(t, m, "testdata/bpm20.bin") +} + +func TestReadWriteCBNT21(t *testing.T) { + m, err := NewManifest(cbnt.Version21) + if err != nil { + t.Fatalf("%v", err) + } + integration.ManifestReadWrite(t, m, "testdata/bpm21.bin") +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/pcd.go b/pkg/intel/metadata/cbnt/bootpolicy/pcd.go new file mode 100644 index 00000000..008ed8b5 --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/pcd.go @@ -0,0 +1,534 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "strings" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +// PCD holds various Platform Config Data. +type PCD struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__PCDS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` + Reserved0 [2]byte `json:"pcdReserved0,omitempty"` + SizeOfData [2]byte `json:"pcdSizeOfData,omitempty"` + Data []byte `json:"pcdData"` + PDRS *PDRS `json:"pcdPDRS,omitempty"` + CNBS *CNBS `json:"pcdCNBS,omitempty"` +} + +// NewPCD returns a new instance of PCD with +// all default values set. +func NewPCD() *PCD { + s := &PCD{} + copy(s.ID[:], []byte(StructureIDPCD)) + s.Version = 0x20 + s.Rehash() + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *PCD) Validate() error { + if s.Version < 0x22 { + return nil + } + + if s.PDRS != nil { + if err := s.PDRS.Validate(); err != nil { + return fmt.Errorf("error on field 'PDRS': %w", err) + } + } + if s.CNBS != nil { + if err := s.CNBS.Validate(); err != nil { + return fmt.Errorf("error on field 'CNBS': %w", err) + } + } + return nil +} + +// Layout returns the structure's layout descriptor +func (s *PCD) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Reserved 0", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.Reserved0 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 2, + Name: "Size Of Data", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.SizeOfData }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 3, + Name: "Data", + Size: func() uint64 { + size := binary.LittleEndian.Uint16(s.SizeOfData[:]) + if size == 0 && len(s.Data) != 0 { + size = uint16(len(s.Data)) + } + if s.ElementSize != 0 { + resSize, err := s.SizeOf(1) + if err != nil { + return uint64(size) + } + + sodSize, err := s.SizeOf(2) + if err != nil { + return uint64(size) + } + base := s.StructInfoCBNT.TotalSize() + resSize + sodSize + guessedSize := base + uint64(size) + if guessedSize != uint64(s.ElementSize) { + size = s.StructInfoCBNT.ElementSize - uint16(s.StructInfoCBNT.TotalSize()) - uint16(resSize) - uint16(sodSize) + } + } + return uint64(size) + }, + Value: func() any { return &s.Data }, + Type: cbnt.ManifestFieldArrayDynamicWithSize, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *PCD) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("PCD: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *PCD) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("PCD: %v", err) + } + + return ret, nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PCD) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PCD) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// Dummy helper to comply with cbnt.Structure interface +func (s *PCD) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the PCD from 'r' in format defined in the document #575623. +func (s *PCD) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + n, err := s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) + if err != nil { + return n, err + } + + if s.Version > 0x21 { + rn := bytes.NewReader(s.Data) + structInfoSize := binary.Size(cbnt.StructInfoCBNT{}) + + for rn.Len() >= structInfoSize { + var structInfo cbnt.StructInfoCBNT + if err := binary.Read(rn, binary.LittleEndian, &structInfo); err != nil { + return n, err + } + + switch structInfo.ID.String() { + case StructureIDPDRS: + p := NewPDRS() + p.SetStructInfo(structInfo) + if _, err := p.ReadFromHelper(rn, false); err != nil { + return n, err + } + s.PDRS = p + case StructureIDCNBS: + c := NewCNBS() + c.SetStructInfo(structInfo) + if _, err := c.ReadFromHelper(rn, false); err != nil { + return n, err + } + s.CNBS = c + default: + return n, nil + } + } + } + + return n, nil + +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *PCD) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *PCD) Rehash() { + if s.Version > 0x21 && len(s.Data) == 0 { + var out bytes.Buffer + if s.PDRS != nil { + _, _ = s.PDRS.WriteTo(&out) + } + if s.CNBS != nil { + _, _ = s.CNBS.WriteTo(&out) + } + if out.Len() > 0 { + s.Data = out.Bytes() + binary.LittleEndian.PutUint16(s.SizeOfData[:], uint16(len(s.Data))) + } + } + s.Variable0 = 0 + s.ElementSize = uint16(s.StructInfoCBNT.TotalSize() + 2 + 2 + uint64(len(s.Data))) +} + +// WriteTo writes the PCD into 'w' in format defined in +// the document #575623. +func (s *PCD) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the PCD. +func (s *PCD) TotalSize() uint64 { + if s == nil { + return 0 + } + + if s.ElementSize != 0 { + return uint64(s.ElementSize) + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *PCD) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + base := s.Common.PrettyString(depth, withHeader, s, "PCD", opts...) + + var lines []string + lines = append(lines, base) + if s.Version > 0x21 && s.PDRS != nil { + lines = append(lines, s.PDRS.PrettyString(depth, true, opts...)) + } + if s.Version > 0x21 && s.CNBS != nil { + lines = append(lines, s.CNBS.PrettyString(depth, true, opts...)) + } + + return strings.Join(lines, "\n") +} + +// PDRS +type PDRS struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__PDRS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` + Data []byte `json:"pdrsData"` +} + +// NewPDRS returns a new instance of PDRS with all default values set. +func NewPDRS() *PDRS { + s := &PDRS{} + copy(s.ID[:], []byte(StructureIDPDRS)) + s.Version = 0x20 + s.Rehash() + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *PDRS) Validate() error { + return nil +} + +// Layout returns the structure's layout descriptor +func (s *PDRS) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Data", + Size: func() uint64 { + if s.ElementSize != 0 { + return uint64(s.ElementSize) + } + return uint64(len(s.Data)) + }, + Value: func() any { return &s.Data }, + Type: cbnt.ManifestFieldArrayDynamicWithSize, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *PDRS) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("PDRS: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *PDRS) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("PDRS: %v", err) + } + + return ret, nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PDRS) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PDRS) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// ReadFrom reads the PDRS from 'r' in format defined in the document #575623. +func (s *PDRS) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the PDRS from 'r' in format defined in the document #575623. +func (s *PDRS) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *PDRS) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *PDRS) Rehash() { + s.Variable0 = 0 + s.ElementSize = uint16(len(s.Data)) +} + +// WriteTo writes the PDRS into 'w' in format defined in +// the document #575623. +func (s *PDRS) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the PDRS. +func (s *PDRS) TotalSize() uint64 { + if s == nil { + return 0 + } + + if s.ElementSize != 0 { + return uint64(s.StructInfo().TotalSize()) + uint64(s.ElementSize) + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *PDRS) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "PDRS", opts...) +} + +// CNBS +type CNBS struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__CNBS__" version:"0x20" var0:"0" var1:"12"` + BufferData IBBSegment `json:"seIBBSegment"` +} + +// NewCNBS returns a new instance of CNBS with all default values set. +func NewCNBS() *CNBS { + s := &CNBS{} + copy(s.ID[:], []byte(StructureIDCNBS)) + s.Version = 0x20 + s.Rehash() + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *CNBS) Validate() error { + if err := s.BufferData.Validate(); err != nil { + return fmt.Errorf("error on field 'BufferData': %w", err) + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *CNBS) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Buffer Data", + Size: func() uint64 { return s.BufferData.TotalSize() }, + Value: func() any { return &s.BufferData }, + Type: cbnt.ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *CNBS) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("CNBS: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *CNBS) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("CNBS: %v", err) + } + + return ret, nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *CNBS) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *CNBS) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// ReadFrom reads the CNBS from 'r' in format defined in the document #575623. +func (s *CNBS) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the CNBS from 'r' in format defined in the document #575623. +func (s *CNBS) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *CNBS) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *CNBS) Rehash() { + s.Variable0 = 0 + s.ElementSize = uint16(s.BufferData.TotalSize()) +} + +// WriteTo writes the CNBS into 'w' in format defined in +// the document #575623. +func (s *CNBS) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the CNBS. +func (s *CNBS) TotalSize() uint64 { + if s == nil { + return 0 + } + + if s.ElementSize != 0 { + return uint64(s.StructInfo().TotalSize()) + uint64(s.ElementSize) + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *CNBS) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "CNBS", opts...) +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/pm.go b/pkg/intel/metadata/cbnt/bootpolicy/pm.go new file mode 100644 index 00000000..b6d7d307 --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/pm.go @@ -0,0 +1,291 @@ +// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +type PM interface { + cbnt.Structure +} + +// NewPM returns a new instance of PM with +// all default values set. +func NewPM(bgv cbnt.BootGuardVersion) (PM, error) { + switch bgv { + case cbnt.Version10: + s := &PMBG{} + copy(s.ID[:], []byte(StructureIDPM)) + s.Version = 0x10 + return s, nil + case cbnt.Version20, cbnt.Version21: + s := &PMCBnT{} + copy(s.ID[:], []byte(StructureIDPM)) + s.Version = 0x20 + s.Rehash() + return s, nil + default: + return nil, fmt.Errorf("version not supported") + } +} + +type PMCBnT struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__PMDA__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` + Reserved0 [2]byte `require:"0" json:"pcReserved0,omitempty"` + DataSize [2]byte `json:"pcDataSize"` + Data []byte `json:"pcData"` +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *PMCBnT) Validate() error { + // See tag "require" + for idx := range s.Reserved0 { + if s.Reserved0[idx] != 0 { + return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) + } + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *PMCBnT) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Reserved 0", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.Reserved0 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 2, + Name: "Data Size", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.DataSize }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 3, + Name: "Data", + Size: func() uint64 { return uint64(binary.Size(uint16(0))) + uint64(binary.Size(s.DataSize)) }, + Value: func() any { return &s.Data }, + Type: cbnt.ManifestFieldArrayDynamicWithPrefix, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *PMCBnT) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("PM: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *PMCBnT) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("PM: %v", err) + } + + return ret, nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PMCBnT) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PMCBnT) SetStructInfo(newStructInfo cbnt.StructInfoCBNT) { + s.StructInfoCBNT = newStructInfo +} + +// Has to be here to fullfil Structure interface requirements. +// Reads the whole data. +func (s *PMCBnT) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the PM from 'r' in format defined in the document #575623. +func (s *PMCBnT) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *PMCBnT) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *PMCBnT) Rehash() { + s.Variable0 = 0 + s.ElementSize = uint16(s.Common.TotalSize(s)) +} + +// WriteTo writes the PM into 'w' in format defined in +// the document #575623. +func (s *PMCBnT) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the PM. +func (s *PMCBnT) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *PMCBnT) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "PM", opts...) +} + +type PMBG struct { + cbnt.StructInfoBG `id:"__PMDA__" version:"0x10"` + DataSize uint16 `json:"pcDataSize"` + Data []byte `json:"pcData"` +} + +// Layout returns the structure's layout descriptor +func (s *PMBG) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoBG.TotalSize() }, + Value: func() any { return &s.StructInfoBG }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Data Size", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.DataSize }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 2, + Name: "Data", + Size: func() uint64 { return uint64(binary.Size(uint16(0))) + uint64(s.DataSize) }, + Value: func() any { return &s.Data }, + Type: cbnt.ManifestFieldArrayDynamicWithPrefix, + }, + } +} + +// Validate implements Structure.Validate() +func (s *PMBG) Validate() error { + // dummy + return nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PMBG) GetStructInfo() cbnt.StructInfo { + return s.StructInfoBG +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *PMBG) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoBG = newStructInfo.(cbnt.StructInfoBG) +} + +// Has to be here to fullfil Structure interface requirements. +// Reads the whole data. +func (s *PMBG) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the PM from 'r' in format defined in the document #575623. +func (s *PMBG) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// WriteTo writes the PM into 'w' in format defined in +// the document #575623. +func (s *PMBG) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *PMBG) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("PM: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *PMBG) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("PM: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the PM. +func (s *PMBG) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *PMBG) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "PM", opts...) +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/se.go b/pkg/intel/metadata/cbnt/bootpolicy/se.go new file mode 100644 index 00000000..351ef37f --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/se.go @@ -0,0 +1,986 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "encoding/binary" + "fmt" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" + "io" + "math" + "strings" + "time" +) + +// IBBSegment defines a single IBB segment +type IBBSegment struct { + cbnt.Common + Reserved [2]byte `require:"0" json:"ibbSegReserved"` + Flags uint16 `json:"ibbSegFlags"` + Base uint32 `json:"ibbSegBase"` + Size uint32 `json:"ibbSegSize"` +} + +// NewIBBSegment returns a new instance of IBBSegment with +// all default values set. +func NewIBBSegment() *IBBSegment { + s := &IBBSegment{} + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *IBBSegment) Validate() error { + // See tag "require" + for idx := range s.Reserved { + if s.Reserved[idx] != 0 { + return fmt.Errorf("'Reserved[%d]' is expected to be 0, but it is %v", idx, s.Reserved[idx]) + } + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *IBBSegment) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Reserved", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.Reserved }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 1, + Name: "Flags", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.Flags }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Base", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.Base }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 3, + Name: "Size", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.Size }, + Type: cbnt.ManifestFieldEndValue, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *IBBSegment) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("IBBSegment: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *IBBSegment) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("IBBSegment: %v", err) + } + + return ret, nil +} + +// ReadFrom reads the IBBSegment from 'r' in format defined in the document #575623. +func (s *IBBSegment) ReadFrom(r io.Reader) (int64, error) { + return s.Common.ReadFrom(r, s) +} + +// WriteTo writes the IBBSegment into 'w' in format defined in +// the document #575623. +func (s *IBBSegment) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the IBBSegment. +func (s *IBBSegment) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *IBBSegment) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "IBB Segment", opts...) +} + +type SE interface { + cbnt.Structure +} + +// SE is an IBB segments element +// +// PrettyString: IBB Segments Element +type SECBnT struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__IBBS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` + Reserved0 [1]byte `require:"0" json:"seReserved0,omitempty"` + SetNumber uint8 `require:"0" json:"seSetNumber,omitempty"` + Reserved1 [1]byte `require:"0" json:"seReserved1,omitempty"` + PBETValue PBETValue `json:"sePBETValue"` + Flags SEFlags `json:"seFlags"` + + // IBBMCHBAR + // PrettyString: IBB MCHBAR + IBBMCHBAR uint64 `json:"seIBBMCHBAR"` + + // VTdBAR + // PrettyString: VT-d BAR + VTdBAR uint64 `json:"seVTdBAR"` + + // DMAProtBase0 + // PrettyString: DMA Protection 0 Base Address + DMAProtBase0 uint32 `json:"seDMAProtBase0"` + + // DMAProtLimit0 + // PrettyString: DMA Protection 0 Limit Address + DMAProtLimit0 uint32 `json:"seDMAProtLimit0"` + + // DMAProtBase1 + // PrettyString: DMA Protection 1 Base Address + DMAProtBase1 uint64 `json:"seDMAProtBase1"` + + // DMAProtLimit1 + // PrettyString: DMA Protection 2 Limit Address + DMAProtLimit1 uint64 `json:"seDMAProtLimit1"` + + PostIBBHash cbnt.HashStructure `json:"sePostIBBHash"` + + IBBEntryPoint uint32 `json:"seIBBEntry"` + + DigestList cbnt.HashList `json:"seDigestList"` + + OBBHash cbnt.HashStructure `json:"seOBBHash"` + + Reserved2 [3]byte `require:"0" json:"seReserved2,omitempty"` + + IBBSegments []IBBSegment `countType:"uint8" json:"seIBBSegments,omitempty"` +} + +// NewSE returns a new instance of SE with +// all default values set. +func NewSE(bgv cbnt.BootGuardVersion) (SE, error) { + switch bgv { + case cbnt.Version10: + s := &SEBG{} + // See 'default' in HashStructure for BG in legacy package + hashAlg := 0x0b + copy(s.ID[:], []byte(StructureIDSE)) + s.Version = 0x10 + // Recursively initializing a child structure: + s.PostIBBHash = *cbnt.NewHashStructureFill(cbnt.Algorithm(hashAlg)) + // Recursively initializing a child structure: + s.Digest = *cbnt.NewHashStructure(cbnt.Algorithm(hashAlg)) + return s, nil + case cbnt.Version20, cbnt.Version21: + s := &SECBnT{} + // See 'default' in HashStructure for CBNT + hashAlg := 0x10 + copy(s.ID[:], []byte(StructureIDSE)) + // Yes, conditional statement inside of switch case + // seems hacky. But it saves us from revriting the whole + // block an changing just one value (version) + if bgv == cbnt.Version20 { + s.Version = 0x20 + } else { + s.Version = 0x21 + } + // Set through tag "required": + s.SetNumber = 0 + // Recursively initializing a child structure: + s.PostIBBHash = *cbnt.NewHashStructure(cbnt.Algorithm(hashAlg)) + // Recursively initializing a child structure: + s.DigestList = *cbnt.NewHashList() + // Recursively initializing a child structure: + s.OBBHash = *cbnt.NewHashStructure(cbnt.Algorithm(hashAlg)) + s.Rehash() + return s, nil + + default: + return nil, fmt.Errorf("version not supported") + } +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *SECBnT) Validate() error { + // See tag "require" + for idx := range s.Reserved0 { + if s.Reserved0[idx] != 0 { + return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) + } + } + // See tag "require" + if s.SetNumber != 0 { + return fmt.Errorf("field 'SetNumber' expects value '0', but has %v", s.SetNumber) + } + // See tag "require" + for idx := range s.Reserved1 { + if s.Reserved1[idx] != 0 { + return fmt.Errorf("'Reserved1[%d]' is expected to be 0, but it is %v", idx, s.Reserved1[idx]) + } + } + // Recursively validating a child structure: + if err := s.DigestList.Validate(); err != nil { + return fmt.Errorf("error on field 'DigestList': %w", err) + } + // See tag "require" + for idx := range s.Reserved2 { + if s.Reserved2[idx] != 0 { + return fmt.Errorf("'Reserved2[%d]' is expected to be 0, but it is %v", idx, s.Reserved2[idx]) + } + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *SECBnT) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Reserved 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved0 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 2, + Name: "Set Number", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.SetNumber }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 3, + Name: "Reserved 1", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved1 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 4, + Name: "PBET Value", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.PBETValue }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 5, + Name: "Flags", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.Flags }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 6, + Name: "IBB MCHBAR", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.IBBMCHBAR }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 7, + Name: "VT-d BAR", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.VTdBAR }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 8, + Name: "DMA Protection 0 Base Address", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.DMAProtBase0 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 9, + Name: "DMA Protection 0 Limit Address", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.DMAProtLimit0 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 10, + Name: "DMA Protection 1 Base Address", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.DMAProtBase1 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 11, + Name: "DMA Protection 2 Limit Address", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.DMAProtLimit1 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 12, + Name: "Post IBB Hash", + Size: func() uint64 { return s.PostIBBHash.TotalSize() }, + Value: func() any { return &s.PostIBBHash }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 13, + Name: "IBB Entry Point", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.IBBEntryPoint }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 14, + Name: "Digest List", + Size: func() uint64 { return s.DigestList.TotalSize() }, + Value: func() any { return &s.DigestList }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 15, + Name: "OBB Hash", + Size: func() uint64 { return s.OBBHash.TotalSize() }, + Value: func() any { return &s.OBBHash }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 16, + Name: "Reserved 2", + Size: func() uint64 { return 3 }, + Value: func() any { return &s.Reserved2 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 17, + Name: fmt.Sprintf("IBBSegments: Array of \"IBB Segments Element\" of length %d", len(s.IBBSegments)), + Size: func() uint64 { + size := uint64(binary.Size(uint8(0))) + for idx := range s.IBBSegments { + size += s.IBBSegments[idx].TotalSize() + } + return size + }, + Value: func() any { return &s.IBBSegments }, + Type: cbnt.ManifestFieldList, + ReadList: func(r io.Reader) (int64, error) { + var count uint8 + if err := binary.Read(r, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to read the count for field 'IBBSegments': %w", err) + } + totalN := int64(binary.Size(count)) + s.IBBSegments = make([]IBBSegment, count) + for idx := range s.IBBSegments { + n, err := s.IBBSegments[idx].ReadFrom(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field 'IBBSegments[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + WriteList: func(w io.Writer) (int64, error) { + count := uint8(len(s.IBBSegments)) + if err := binary.Write(w, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to write the count for field 'IBBSegments': %w", err) + } + totalN := int64(binary.Size(count)) + for idx := range s.IBBSegments { + n, err := s.IBBSegments[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'IBBSegments[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *SECBnT) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("SE: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *SECBnT) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("SE: %v", err) + } + + return ret, nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *SECBnT) GetStructInfo() cbnt.StructInfo { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *SECBnT) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// Has to be here to fullfil the reuirements of cbnt.Structure +func (s *SECBnT) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the SE from 'r' in format defined in the document #575623. +func (s *SECBnT) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *SECBnT) RehashRecursive() { + s.DigestList.Rehash() + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *SECBnT) Rehash() { + s.Variable0 = 0 + s.ElementSize = uint16(s.TotalSize()) +} + +// WriteTo writes the SE into 'w' in format defined in +// the document #575623. +func (s *SECBnT) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the SE. +func (s *SECBnT) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *SECBnT) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + base := s.Common.PrettyString(depth, withHeader, s, "IBB Segments Element", opts...) + var lines []string + lines = append(lines, base) + + lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("IBBSegments: Array of \"IBB Segments Element\" of length %d", len(s.IBBSegments)), s.IBBSegments)) + for i := 0; i < len(s.IBBSegments); i++ { + lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.IBBSegments[i].PrettyString(depth+2, true))) + } + + if depth < 1 { + lines = append(lines, "") + } + if depth < 2 { + lines = append(lines, "") + } + + return strings.Join(lines, "\n") +} + +// SE for BG +// PrettyString: IBB Segments Element +type SEBG struct { + cbnt.Common + cbnt.StructInfoBG `id:"__IBBS__" version:"0x10"` + Reserved0 [1]byte `require:"0" json:"seReserved0,omitempty"` + Reserved1 [1]byte `require:"0" json:"seReserved1,omitempty"` + PBETValue PBETValue `json:"sePBETValue"` + Flags SEFlags `json:"seFlags"` + // PrettyString: IBB MCHBAR + IBBMCHBAR uint64 `json:"seIBBMCHBAR"` + // PrettyString: VT-d BAR + VTdBAR uint64 `json:"seVTdBAR"` + // PrettyString: DMA Protection 0 Base Address + PMRLBase uint32 `json:"seDMAProtBase0"` + // PrettyString: DMA Protection 0 Limit Address + PMRLLimit uint32 `json:"seDMAProtLimit0"` + // PrettyString: DMA Protection 1 Base Address + Reserved2 [8]byte `json:"seDMAProtBase1"` + // PrettyString: DMA Protection 2 Limit Address + Reserved3 [8]byte `json:"seDMAProtLimit1"` + + PostIBBHash cbnt.HashStructureFill `json:"sePostIBBHash"` + + IBBEntryPoint uint32 `json:"seIBBEntry"` + + Digest cbnt.HashStructure `json:"seDigestList"` + + IBBSegments []IBBSegment `countType:"uint8" json:"seIBBSegments,omitempty"` +} + +// Layout returns the structure's layout descriptor +func (s *SEBG) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoBG.TotalSize() }, + Value: func() any { return &s.StructInfoBG }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Reserved 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved0 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 2, + Name: "Reserved 1", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved1 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 3, + Name: "PBET Value", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.PBETValue }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 4, + Name: "Flags", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.Flags }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 5, + Name: "IBB MCHBAR", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.IBBMCHBAR }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 6, + Name: "VT-d BAR", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.VTdBAR }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 7, + Name: "PMRL Base", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.PMRLBase }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 8, + Name: "PMRL Limit", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.PMRLLimit }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 9, + Name: "Reserved 2", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.Reserved2 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 10, + Name: "Reserved 3", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.Reserved3 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 11, + Name: "Post IBB Hash", + Size: func() uint64 { return s.PostIBBHash.TotalSize() }, + Value: func() any { return &s.PostIBBHash }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 12, + Name: "IBB Entry Point", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.IBBEntryPoint }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 13, + Name: "Digest", + Size: func() uint64 { return s.Digest.TotalSize() }, + Value: func() any { return &s.Digest }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 14, + Name: fmt.Sprintf("IBBSegments: Array of \"IBB Segments Element\" of length %d", len(s.IBBSegments)), + Size: func() uint64 { + size := uint64(binary.Size(uint8(0))) + for idx := range s.IBBSegments { + size += s.IBBSegments[idx].TotalSize() + } + return size + }, + Value: func() any { return &s.IBBSegments }, + Type: cbnt.ManifestFieldList, + ReadList: func(r io.Reader) (int64, error) { + var count uint8 + if err := binary.Read(r, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to read the count for field 'IBBSegments': %w", err) + } + totalN := int64(binary.Size(count)) + s.IBBSegments = make([]IBBSegment, count) + for idx := range s.IBBSegments { + n, err := s.IBBSegments[idx].ReadFrom(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field 'IBBSegments[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + WriteList: func(w io.Writer) (int64, error) { + count := uint8(len(s.IBBSegments)) + if err := binary.Write(w, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to write the count for field 'IBBSegments': %w", err) + } + totalN := int64(binary.Size(count)) + for idx := range s.IBBSegments { + n, err := s.IBBSegments[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'IBBSegments[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + }, + } +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *SEBG) GetStructInfo() cbnt.StructInfo { + return s.StructInfoBG +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *SEBG) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfoBG = newStructInfo.(cbnt.StructInfoBG) +} + +// Has to be here to fullfil the reuirements of cbnt.Structure +func (s *SEBG) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the SE from 'r' in format defined in the document #575623. +func (s *SEBG) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// WriteTo writes the SE into 'w' in format defined in +// the document #575623. +func (s *SEBG) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *SEBG) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("SE: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *SEBG) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("SE: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the SE. +func (s *SEBG) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *SEBG) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + base := s.Common.PrettyString(depth, withHeader, s, "IBB Segments Element", opts...) + var lines []string + lines = append(lines, base) + + lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("IBBSegments: Array of \"IBB Segments Element\" of length %d", len(s.IBBSegments)), s.IBBSegments)) + for i := 0; i < len(s.IBBSegments); i++ { + lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.IBBSegments[i].PrettyString(depth+2, true))) + } + + if depth < 1 { + lines = append(lines, "") + } + if depth < 2 { + lines = append(lines, "") + } + + return strings.Join(lines, "\n") +} + +// CachingType +type CachingType uint8 + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (v CachingType) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return v.String() +} + +// TotalSize returns the total size measured through binary.Size. +func (v CachingType) TotalSize() uint64 { + return uint64(binary.Size(v)) +} + +// WriteTo writes the CachingType into 'w' in binary format. +func (v CachingType) WriteTo(w io.Writer) (int64, error) { + return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) +} + +// ReadFrom reads the CachingType from 'r' in binary format. +func (v CachingType) ReadFrom(r io.Reader) (int64, error) { + return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) +} + +// PBETValue +type PBETValue uint8 + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (pbet PBETValue) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "PBET Value", pbet)) + } + lines = append(lines, pretty.SubValue(depth+1, "PBET Value", "", pbet.PBETValue(), opts...)...) + return strings.Join(lines, "\n") +} + +// TotalSize returns the total size measured through binary.Size. +func (pbet PBETValue) TotalSize() uint64 { + return uint64(binary.Size(pbet)) +} + +// WriteTo writes the PBETValue into 'w' in binary format. +func (pbet PBETValue) WriteTo(w io.Writer) (int64, error) { + return int64(pbet.TotalSize()), binary.Write(w, binary.LittleEndian, pbet) +} + +// ReadFrom reads the PBETValue from 'r' in binary format. +func (pbet PBETValue) ReadFrom(r io.Reader) (int64, error) { + return int64(pbet.TotalSize()), binary.Read(r, binary.LittleEndian, pbet) +} + +// SEFlags +type SEFlags uint32 + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (flags SEFlags) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "SE Flags", flags)) + } + lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", flags.Reserved0(), opts...)...) + if flags.SupportsTopSwapRemediation() { + lines = append(lines, pretty.SubValue(depth+1, "Supports Top Swap Remediation", "BIOS supports Top Swap remediation action", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "Supports Top Swap Remediation", "BIOS does not support Top Swap remediation action", false, opts...)...) + } + if flags.TPMFailureLeavesHierarchiesEnabled() { + lines = append(lines, pretty.SubValue(depth+1, "TPM Failure Leaves Hierarchies Enabled", "Leave Hierarchies enabled. Cap all PCRs on failure.", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "TPM Failure Leaves Hierarchies Enabled", "Do not leave enabled. Disable all Hierarchies or deactivate on failure.", false, opts...)...) + } + if flags.AuthorityMeasure() { + lines = append(lines, pretty.SubValue(depth+1, "Authority Measure", "Extend Authority Measurements into the Authority PCR 7", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "Authority Measure", "Do not extend into the Authority PCR 7", false, opts...)...) + } + if flags.Locality3Startup() { + lines = append(lines, pretty.SubValue(depth+1, "Locality 3 Startup", "Issue TPM Start-up from Locality 3", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "Locality 3 Startup", "Disabled", false, opts...)...) + } + if flags.DMAProtection() { + lines = append(lines, pretty.SubValue(depth+1, "DMA Protection", "Enable DMA Protection", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "DMA Protection", "Disable DMA Protection", false, opts...)...) + } + return strings.Join(lines, "\n") +} + +// TotalSize returns the total size measured through binary.Size. +func (flags SEFlags) TotalSize() uint64 { + return uint64(binary.Size(flags)) +} + +// WriteTo writes the SEFlags into 'w' in binary format. +func (flags SEFlags) WriteTo(w io.Writer) (int64, error) { + return int64(flags.TotalSize()), binary.Write(w, binary.LittleEndian, flags) +} + +// ReadFrom reads the SEFlags from 'r' in binary format. +func (flags SEFlags) ReadFrom(r io.Reader) (int64, error) { + return int64(flags.TotalSize()), binary.Read(r, binary.LittleEndian, flags) +} + +// PBETValue returns the raw value of the timer setting. +func (pbet PBETValue) PBETValue() uint8 { + return uint8(pbet) & 0x0f +} + +// Duration returns the value as time.Duration. +func (pbet PBETValue) Duration() time.Duration { + v := pbet.PBETValue() + if v == 0 { + return math.MaxInt64 + } + return time.Second * time.Duration(5+v) +} + +// SetDuration sets the value using standard time.Duration as the input. +func (pbet *PBETValue) SetDuration(duration time.Duration) time.Duration { + v := duration.Nanoseconds()/time.Second.Nanoseconds() - 5 + if v <= 0 { + v = 1 + } + if v >= 16 { + v = 0 + } + *pbet = PBETValue(v) + + return pbet.Duration() +} + +// Reserved0 +func (flags SEFlags) Reserved0() uint32 { + return uint32(flags & 0xffffffe0) +} + +// SupportsTopSwapRemediation +// +// PrettyString-true: BIOS supports Top Swap remediation action +// PrettyString-false: BIOS does not support Top Swap remediation action +func (flags SEFlags) SupportsTopSwapRemediation() bool { + return flags&0x10 != 0 +} + +// TPMFailureLeavesHierarchiesEnabled +// +// PrettyString-true: Leave Hierarchies enabled. Cap all PCRs on failure. +// PrettyString-false: Do not leave enabled. Disable all Hierarchies or deactivate on failure. +func (flags SEFlags) TPMFailureLeavesHierarchiesEnabled() bool { + return flags&0x08 != 0 +} + +// AuthorityMeasure +// +// NOTE: PCR[7] is disabled from MTL onwards +// +// PrettyString-true: Extend Authority Measurements into the Authority PCR 7 +// PrettyString-false: Do not extend into the Authority PCR 7 +func (flags SEFlags) AuthorityMeasure() bool { + return flags&0x04 != 0 +} + +// Locality3Startup +// +// PrettyString-true: Issue TPM Start-up from Locality 3 +// PrettyString-false: Disabled +func (flags SEFlags) Locality3Startup() bool { + return flags&0x02 != 0 +} + +// DMAProtection +// +// PrettyString-true: Enable DMA Protection +// PrettyString-false: Disable DMA Protection +func (flags SEFlags) DMAProtection() bool { + return flags&0x01 != 0 +} + +// String implements fmt.Stringer. +func (v CachingType) String() string { + switch v { + case CachingTypeWriteProtect: + return "write_protect" + case CachingTypeWriteBack: + return "write_back" + case CachingTypeReserved0: + return "value_0x02" + case CachingTypeReserved1: + return "value_0x03" + } + return fmt.Sprintf("unexpected_value_0x%02X", uint8(v)) +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/signature.go b/pkg/intel/metadata/cbnt/bootpolicy/signature.go new file mode 100644 index 00000000..6085a0a1 --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/signature.go @@ -0,0 +1,147 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "fmt" + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +type Signature struct { + cbnt.Common + cbnt.StructInfo `id:"__PMSG__" version:"0x20"` + cbnt.KeySignature `json:"sigKeySignature"` +} + +// NewSignature returns a new instance of Signature with +// all default values set. +func NewSignature(bgv cbnt.BootGuardVersion) (*Signature, error) { + switch bgv { + case cbnt.Version10: + s := &Signature{StructInfo: cbnt.NewStructInfo(bgv)} + copy(s.StructInfo.(*cbnt.StructInfoBG).ID[:], []byte(StructureIDSignature)) + s.StructInfo.(*cbnt.StructInfoBG).Version = 0x10 + // Recursively initializing a child structure: + s.KeySignature = *cbnt.NewKeySignature() + return s, nil + case cbnt.Version20, cbnt.Version21: + s := &Signature{StructInfo: cbnt.NewStructInfo(bgv)} + copy(s.StructInfo.(*cbnt.StructInfoCBNT).ID[:], []byte(StructureIDSignature)) + s.StructInfo.(*cbnt.StructInfoCBNT).Version = 0x20 + s.StructInfo.(*cbnt.StructInfoCBNT).ElementSize = 0 + s.StructInfo.(*cbnt.StructInfoCBNT).Variable0 = 0 + // Recursively initializing a child structure: + s.KeySignature = *cbnt.NewKeySignature() + return s, nil + default: + return nil, fmt.Errorf("version not supported") + } +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *Signature) Validate() error { + // Recursively validating a child structure: + if err := s.KeySignature.Validate(); err != nil { + return fmt.Errorf("error on field 'KeySignature': %w", err) + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *Signature) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfo.TotalSize() }, + Value: func() any { return s.StructInfo }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Key Signature", + Size: func() uint64 { return s.KeySignature.TotalSize() }, + Value: func() any { return &s.KeySignature }, + Type: cbnt.ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *Signature) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("Signature: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *Signature) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("Signature: %v", err) + } + + return ret, nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *Signature) GetStructInfo() cbnt.StructInfo { + return s.StructInfo +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *Signature) SetStructInfo(newStructInfo cbnt.StructInfo) { + s.StructInfo = newStructInfo +} + +// Dummy helper to satisfy cbnt.Structure Interface +func (s *Signature) ReadFrom(r io.Reader, info bool) (int64, error) { + return s.ReadFromHelper(r, info) +} + +// ReadFrom reads the Signature from 'r' in format defined in the document #575623. +func (s *Signature) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// WriteTo writes the Signature into 'w' in format defined in +// the document #575623. +func (s *Signature) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the Signature. +func (s *Signature) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *Signature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "Signature", opts...) +} diff --git a/pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm10.bin b/pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm10.bin new file mode 100644 index 00000000..bdf6301e Binary files /dev/null and b/pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm10.bin differ diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/testdata/bpm.bin b/pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm20.bin similarity index 100% rename from pkg/intel/metadata/cbnt/cbntbootpolicy/testdata/bpm.bin rename to pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm20.bin diff --git a/pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm21.bin b/pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm21.bin new file mode 100644 index 00000000..92be3049 Binary files /dev/null and b/pkg/intel/metadata/cbnt/bootpolicy/testdata/bpm21.bin differ diff --git a/pkg/intel/metadata/cbnt/bootpolicy/txt.go b/pkg/intel/metadata/cbnt/bootpolicy/txt.go new file mode 100644 index 00000000..5cf5ebdf --- /dev/null +++ b/pkg/intel/metadata/cbnt/bootpolicy/txt.go @@ -0,0 +1,333 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntbootpolicy + +import ( + "encoding/binary" + "fmt" + "io" + "time" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +// TXT is the TXT element +type TXT struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__TXTS__" version:"0x21" var0:"0" var1:"uint16(s.TotalSize())"` + Reserved0 [1]byte `require:"0" json:"txtReserved0,omitempty"` + SetNumber [1]byte `require:"0" json:"txtSetNumer,omitempty"` + SInitMinSVNAuth uint8 `default:"0" json:"txtSVN"` + Reserved1 [1]byte `require:"0" json:"txtReserved1,omitempty"` + ControlFlags TXTControlFlags `json:"txtFlags"` + PwrDownInterval Duration16In5Sec `json:"txtPwrDownInterval"` + // PrettyString: PTT CMOS Offset 0 + PTTCMOSOffset0 uint8 `default:"126" json:"txtPTTCMOSOffset0"` + // PrettyString: PTT CMOS Offset 1 + PTTCMOSOffset1 uint8 `default:"127" json:"txtPTTCMOSOffset1"` + ACPIBaseOffset uint16 `default:"0x400" json:"txtACPIBaseOffset,omitempty"` + Reserved2 [2]byte `json:"txtReserved2,omitempty"` + // PrettyString: ACPI MMIO Offset + PwrMBaseOffset uint32 `default:"0xFE000000" json:"txtPwrMBaseOffset,omitempty"` + DigestList cbnt.HashList `json:"txtDigestList"` + Reserved3 [3]byte `require:"0" json:"txtReserved3,omitempty"` + + SegmentCount uint8 `require:"0" json:"txtSegmentCount,omitempty"` +} + +// NewTXT returns a new instance of TXT with +// all default values set. +func NewTXT() *TXT { + s := &TXT{} + copy(s.ID[:], []byte(StructureIDTXT)) + s.Version = 0x21 + // Set through tag "default": + s.SInitMinSVNAuth = 0 + // Set through tag "default": + s.PTTCMOSOffset0 = 126 + // Set through tag "default": + s.PTTCMOSOffset1 = 127 + // Set through tag "default": + s.ACPIBaseOffset = 0x400 + // Set through tag "default": + s.PwrMBaseOffset = 0xFE000000 + // Recursively initializing a child structure: + s.DigestList = *cbnt.NewHashList() + // Set through tag "required": + s.SegmentCount = 0 + s.Rehash() + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *TXT) Validate() error { + // See tag "require" + for idx := range s.Reserved0 { + if s.Reserved0[idx] != 0 { + return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) + } + } + // See tag "require" + for idx := range s.SetNumber { + if s.SetNumber[idx] != 0 { + return fmt.Errorf("'SetNumber[%d]' is expected to be 0, but it is %v", idx, s.SetNumber[idx]) + } + } + // See tag "require" + for idx := range s.Reserved1 { + if s.Reserved1[idx] != 0 { + return fmt.Errorf("'Reserved1[%d]' is expected to be 0, but it is %v", idx, s.Reserved1[idx]) + } + } + // Recursively validating a child structure: + if err := s.DigestList.Validate(); err != nil { + return fmt.Errorf("error on field 'DigestList': %w", err) + } + // See tag "require" + for idx := range s.Reserved3 { + if s.Reserved3[idx] != 0 { + return fmt.Errorf("'Reserved3[%d]' is expected to be 0, but it is %v", idx, s.Reserved3[idx]) + } + } + // See tag "require" + if s.SegmentCount != 0 { + return fmt.Errorf("field 'SegmentCount' expects value '0', but has %v", s.SegmentCount) + } + + return nil +} + +// Layout returns the structure's layout descriptor +func (s *TXT) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return s.StructInfoCBNT.TotalSize() }, + Value: func() any { return &s.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Reserved 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved0 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 2, + Name: "Set Number", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.SetNumber }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 3, + Name: "S Init Min SVN Auth", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.SInitMinSVNAuth }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 4, + Name: "Reserved 1", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Reserved1 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 5, + Name: "Control Flags", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.ControlFlags }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 6, + Name: "Pwr Down Interval", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.PwrDownInterval }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 7, + Name: "PTT CMOS Offset 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.PTTCMOSOffset0 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 8, + Name: "PTT CMOS Offset 1", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.PTTCMOSOffset1 }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 9, + Name: "ACPI Base Offset", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.ACPIBaseOffset }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 10, + Name: "Reserved 2", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.Reserved2 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 11, + Name: "ACPI MMIO Offset", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.PwrMBaseOffset }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 12, + Name: "Digest List", + Size: func() uint64 { return s.DigestList.TotalSize() }, + Value: func() any { return &s.DigestList }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 13, + Name: "Reserved 3", + Size: func() uint64 { return 3 }, + Value: func() any { return &s.Reserved3 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 14, + Name: "Segment Count", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.SegmentCount }, + Type: cbnt.ManifestFieldEndValue, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *TXT) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("TXT: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *TXT) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("TXT: %v", err) + } + + return ret, nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *TXT) GetStructInfo() cbnt.StructInfoCBNT { + return s.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (s *TXT) SetStructInfo(newStructInfo cbnt.StructInfoCBNT) { + s.StructInfoCBNT = newStructInfo +} + +// Dummy helper to comply with requirements of cbnt.Structure interface +func (s *TXT) ReadFrom(r io.Reader) (int64, error) { + return s.ReadFromHelper(r, true) +} + +// ReadFrom reads the TXT from 'r' in format defined in the document #575623. +func (s *TXT) ReadFromHelper(r io.Reader, info bool) (int64, error) { + l := s.Layout() + + if !info { + l = l[1:] + } + + return s.Common.ReadFrom(r, cbnt.DummyLayout{Fields: l}) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *TXT) RehashRecursive() { + s.DigestList.Rehash() + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *TXT) Rehash() { + s.Variable0 = 0 + s.ElementSize = uint16(s.TotalSize()) +} + +// WriteTo writes the TXT into 'w' in format defined in +// the document #575623. +func (s *TXT) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Size returns the total size of the TXT. +func (s *TXT) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *TXT) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "TXT", opts...) +} + +type Duration16In5Sec uint16 + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (d Duration16In5Sec) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return d.String() +} + +// TotalSize returns the total size measured through binary.Size. +func (d Duration16In5Sec) TotalSize() uint64 { + return uint64(binary.Size(d)) +} + +// WriteTo writes the Duration16In5Sec into 'w' in binary format. +func (d Duration16In5Sec) WriteTo(w io.Writer) (int64, error) { + return int64(d.TotalSize()), binary.Write(w, binary.LittleEndian, d) +} + +// ReadFrom reads the Duration16In5Sec from 'r' in binary format. +func (d Duration16In5Sec) ReadFrom(r io.Reader) (int64, error) { + return int64(d.TotalSize()), binary.Read(r, binary.LittleEndian, d) +} + +// Duration calculates a given time in multiple of 5 seconds. +func (d Duration16In5Sec) Duration() time.Duration { + return time.Second * 5 * time.Duration(d) +} + +func (d Duration16In5Sec) String() string { + if d == 0 { + return "0 (infinite)" + } + return fmt.Sprintf("%d (%s)", d, d.Duration().String()) +} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_control_flags_manifestcodegen.go b/pkg/intel/metadata/cbnt/bootpolicy/txt_control_flags.go similarity index 52% rename from pkg/intel/metadata/cbnt/cbntbootpolicy/txt_control_flags_manifestcodegen.go rename to pkg/intel/metadata/cbnt/bootpolicy/txt_control_flags.go index 89b2361b..17054eb2 100644 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_control_flags_manifestcodegen.go +++ b/pkg/intel/metadata/cbnt/bootpolicy/txt_control_flags.go @@ -1,13 +1,7 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - package cbntbootpolicy import ( @@ -16,20 +10,42 @@ import ( "io" "strings" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" ) -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} +const ( + // + CachingTypeWriteProtect = CachingType(iota) + CachingTypeWriteBack + CachingTypeReserved0 + CachingTypeReserved1 +) + +const ( + ExecutionProfileA = ExecutionProfile(iota) + ExecutionProfileB + ExecutionProfileC +) + +const ( + MemoryScrubbingPolicyDefault = MemoryScrubbingPolicy(iota) + MemoryScrubbingPolicyBIOS + MemoryScrubbingPolicySACM +) + +const ( + BackupActionPolicyDefault = BackupActionPolicy(iota) + BackupActionPolicyForceMemoryPowerDown + BackupActionPolicyForceBtGUnbreakableShutdown +) + +const ( + ResetAUXControlResetAUXIndex = ResetAUXControl(iota) + ResetAUXControlDeleteAUXIndex ) +type BackupActionPolicy uint8 + // PrettyString returns the bits of the flags in an easy-to-read format. func (v BackupActionPolicy) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { return v.String() @@ -55,6 +71,8 @@ func (v ExecutionProfile) PrettyString(depth uint, withHeader bool, opts ...pret return v.String() } +type ExecutionProfile uint8 + // TotalSize returns the total size measured through binary.Size. func (v ExecutionProfile) TotalSize() uint64 { return uint64(binary.Size(v)) @@ -70,6 +88,8 @@ func (v ExecutionProfile) ReadFrom(r io.Reader) (int64, error) { return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) } +type MemoryScrubbingPolicy uint8 + // PrettyString returns the bits of the flags in an easy-to-read format. func (v MemoryScrubbingPolicy) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { return v.String() @@ -90,6 +110,8 @@ func (v MemoryScrubbingPolicy) ReadFrom(r io.Reader) (int64, error) { return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) } +type ResetAUXControl uint8 + // PrettyString returns the bits of the flags in an easy-to-read format. func (v ResetAUXControl) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { return v.String() @@ -110,35 +132,109 @@ func (v ResetAUXControl) ReadFrom(r io.Reader) (int64, error) { return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) } +type TXTControlFlags uint32 + // PrettyString returns the bits of the flags in an easy-to-read format. -func (v TXTControlFlags) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { +func (flags TXTControlFlags) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { var lines []string if withHeader { - lines = append(lines, pretty.Header(depth, "TXT Control Flags", v)) + lines = append(lines, pretty.Header(depth, "TXT Control Flags", flags)) } - lines = append(lines, pretty.SubValue(depth+1, "Execution Profile", "", v.ExecutionProfile(), opts...)...) - lines = append(lines, pretty.SubValue(depth+1, "Memory Scrubbing Policy", "", v.MemoryScrubbingPolicy(), opts...)...) - lines = append(lines, pretty.SubValue(depth+1, "Backup Action Policy", "", v.BackupActionPolicy(), opts...)...) - if v.IsSACMRequestedToExtendStaticPCRs() { + lines = append(lines, pretty.SubValue(depth+1, "Execution Profile", "", flags.ExecutionProfile(), opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "Memory Scrubbing Policy", "", flags.MemoryScrubbingPolicy(), opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "Backup Action Policy", "", flags.BackupActionPolicy(), opts...)...) + if flags.IsSACMRequestedToExtendStaticPCRs() { lines = append(lines, pretty.SubValue(depth+1, "Is SACM Requested To Extend Static PC Rs", "Default setting. S-ACM is requested to extend static PCRs", true, opts...)...) } else { lines = append(lines, pretty.SubValue(depth+1, "Is SACM Requested To Extend Static PC Rs", "S-ACM is not requested to extend static PCRs", false, opts...)...) } - lines = append(lines, pretty.SubValue(depth+1, "Reset AUX Control", "", v.ResetAUXControl(), opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "Reset AUX Control", "", flags.ResetAUXControl(), opts...)...) return strings.Join(lines, "\n") } // TotalSize returns the total size measured through binary.Size. -func (v TXTControlFlags) TotalSize() uint64 { - return uint64(binary.Size(v)) +func (flags TXTControlFlags) TotalSize() uint64 { + return uint64(binary.Size(flags)) } // WriteTo writes the TXTControlFlags into 'w' in binary format. -func (v TXTControlFlags) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) +func (flags TXTControlFlags) WriteTo(w io.Writer) (int64, error) { + return int64(flags.TotalSize()), binary.Write(w, binary.LittleEndian, flags) } // ReadFrom reads the TXTControlFlags from 'r' in binary format. -func (v TXTControlFlags) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) +func (flags TXTControlFlags) ReadFrom(r io.Reader) (int64, error) { + return int64(flags.TotalSize()), binary.Read(r, binary.LittleEndian, flags) +} + +func (flags TXTControlFlags) ExecutionProfile() ExecutionProfile { + return ExecutionProfile(flags & 0x1f) +} + +// String just implements fmt.Stringer. +func (v ExecutionProfile) String() string { + switch v { + case ExecutionProfileA: + return `A (use default selection based on differentation between clients, UP, and MP servers)` + case ExecutionProfileB: + return `B (use "Server model": rely on BIOS to configure topoligy; do not use ACHECK)` + case ExecutionProfileC: + return `C (use "Client model": do not measure BIOS into D-PCRs; use ACHECK-based alias check)` + } + return fmt.Sprintf("unexpected_execution_profile_value_0x%02X", uint8(v)) +} + +func (flags TXTControlFlags) MemoryScrubbingPolicy() MemoryScrubbingPolicy { + return MemoryScrubbingPolicy((flags >> 5) & 0x3) +} + +// String implements fmt.Stringer. +func (v MemoryScrubbingPolicy) String() string { + switch v { + case MemoryScrubbingPolicyDefault: + return "BIOS if verified or backup action othersize" + case MemoryScrubbingPolicyBIOS: + return "BIOS" + case MemoryScrubbingPolicySACM: + return "S-ACM" + } + return fmt.Sprintf("unexpected_value_0x%02X", uint8(v)) +} + +func (flags TXTControlFlags) BackupActionPolicy() BackupActionPolicy { + return BackupActionPolicy((flags >> 7) & 0x3) +} + +// String implements fmt.Stringer. +func (v BackupActionPolicy) String() string { + switch v { + case BackupActionPolicyDefault: + return "memory power down if profile D or BtG unbreakable shutdown otherwise" + case BackupActionPolicyForceMemoryPowerDown: + return "memory power down" + case BackupActionPolicyForceBtGUnbreakableShutdown: + return "BtG unbreakable shutdown" + } + return fmt.Sprintf("unexpected_value_0x%02X", uint8(v)) +} + +// PrettyString-true: Default setting. S-ACM is requested to extend static PCRs +// PrettyString-false: S-ACM is not requested to extend static PCRs +func (flags TXTControlFlags) IsSACMRequestedToExtendStaticPCRs() bool { + return (flags>>9)&0x01 == 0 +} + +func (flags TXTControlFlags) ResetAUXControl() ResetAUXControl { + return ResetAUXControl((flags >> 31) & 0x01) +} + +// String implements fmt.Stringer. +func (v ResetAUXControl) String() string { + switch v { + case ResetAUXControlResetAUXIndex: + return "AUX reset leaf will reset AUX index" + case ResetAUXControlDeleteAUXIndex: + return "AUX reset leaf will delete AUX index" + } + return fmt.Sprintf("unexpected_value_0x%02X", uint8(v)) } diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/Reserved.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/Reserved.go deleted file mode 100644 index 7ce11f6f..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/Reserved.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -// Reserved is reducted -type Reserved struct { - StructInfo `id:"__PFRS__" version:"0x21" var0:"0" var1:"uint16(s.TotalSize())"` - ReservedData [32]byte `json:"ReservedData"` -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/Reserved_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/Reserved_manifestcodegen.go deleted file mode 100644 index a3ed4475..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/Reserved_manifestcodegen.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewReserved returns a new instance of Reserved with -// all default values set. -func NewReserved() *Reserved { - s := &Reserved{} - copy(s.StructInfo.ID[:], []byte(StructureIDReserved)) - s.StructInfo.Version = 0x21 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Reserved) Validate() error { - - return nil -} - -// StructureIDReserved is the StructureID (in terms of -// the document #575623) of element 'Reserved'. -const StructureIDReserved = "__PFRS__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Reserved) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Reserved) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the Reserved from 'r' in format defined in the document #575623. -func (s *Reserved) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the Reserved from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *Reserved) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // ReservedData (ManifestFieldType: arrayStatic) - { - n, err := 32, binary.Read(r, binary.LittleEndian, s.ReservedData[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ReservedData': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Reserved) RehashRecursive() { - s.StructInfo.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Reserved) Rehash() { - s.Variable0 = 0 - s.ElementSize = uint16(s.TotalSize()) -} - -// WriteTo writes the Reserved into 'w' in format defined in -// the document #575623. -func (s *Reserved) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // ReservedData (ManifestFieldType: arrayStatic) - { - n, err := 32, binary.Write(w, binary.LittleEndian, s.ReservedData[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ReservedData': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *Reserved) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// ReservedDataSize returns the size in bytes of the value of field ReservedData -func (s *Reserved) ReservedDataTotalSize() uint64 { - return 32 -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *Reserved) StructInfoOffset() uint64 { - return 0 -} - -// ReservedDataOffset returns the offset in bytes of field ReservedData -func (s *Reserved) ReservedDataOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// Size returns the total size of the Reserved. -func (s *Reserved) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.ReservedDataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Reserved) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Reserved", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved Data", "", &s.ReservedData, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/bpmh.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/bpmh.go deleted file mode 100644 index 291ab883..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/bpmh.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -import ( - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" -) - -// BPMH is the header of boot policy manifest -type BPMH struct { - StructInfo `id:"__ACBP__" version:"0x23" var0:"0x20" var1:"uint16(s.TotalSize())"` - - KeySignatureOffset uint16 `json:"bpmhKeySignatureOffset"` - - BPMRevision uint8 `json:"bpmhRevision"` - - // BPMSVN is BPM security version number - // - // PrettyString: BPM SVN - BPMSVN cbnt.SVN `json:"bpmhSNV"` - - // ACMSVNAuth is authorized ACM security version number - // - // PrettyString: ACM SVN Auth - ACMSVNAuth cbnt.SVN `json:"bpmhACMSVN"` - - Reserved0 [1]byte `require:"0" json:"bpmhReserved0,omitempty"` - - NEMDataStack Size4K `json:"bpmhNEMStackSize"` -} - -// Size4K is a size in units of 4096 bytes. -type Size4K uint16 - -// InBytes returns the size in bytes. -func (s Size4K) InBytes() uint32 { - return uint32(s) * 4096 -} - -// NewSize4K returns the given size as multiple of 4K -func NewSize4K(size uint32) Size4K { - return Size4K(size / 4096) -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/bpmh_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/bpmh_manifestcodegen.go deleted file mode 100644 index c336b58e..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/bpmh_manifestcodegen.go +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewBPMH returns a new instance of BPMH with -// all default values set. -func NewBPMH() *BPMH { - s := &BPMH{} - copy(s.StructInfo.ID[:], []byte(StructureIDBPMH)) - s.StructInfo.Version = 0x23 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *BPMH) Validate() error { - // See tag "require" - for idx := range s.Reserved0 { - if s.Reserved0[idx] != 0 { - return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) - } - } - - return nil -} - -// StructureIDBPMH is the StructureID (in terms of -// the document #575623) of element 'BPMH'. -const StructureIDBPMH = "__ACBP__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *BPMH) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *BPMH) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the BPMH from 'r' in format defined in the document #575623. -func (s *BPMH) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the BPMH from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *BPMH) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // KeySignatureOffset (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySignatureOffset) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeySignatureOffset': %w", err) - } - totalN += int64(n) - } - - // BPMRevision (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.BPMRevision) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'BPMRevision': %w", err) - } - totalN += int64(n) - } - - // BPMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.BPMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'BPMSVN': %w", err) - } - totalN += int64(n) - } - - // ACMSVNAuth (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.ACMSVNAuth) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ACMSVNAuth': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // NEMDataStack (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.NEMDataStack) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'NEMDataStack': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *BPMH) RehashRecursive() { - s.StructInfo.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *BPMH) Rehash() { - s.Variable0 = 0x20 - s.ElementSize = uint16(s.TotalSize()) -} - -// WriteTo writes the BPMH into 'w' in format defined in -// the document #575623. -func (s *BPMH) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // KeySignatureOffset (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySignatureOffset) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeySignatureOffset': %w", err) - } - totalN += int64(n) - } - - // BPMRevision (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.BPMRevision) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'BPMRevision': %w", err) - } - totalN += int64(n) - } - - // BPMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.BPMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'BPMSVN': %w", err) - } - totalN += int64(n) - } - - // ACMSVNAuth (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.ACMSVNAuth) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ACMSVNAuth': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // NEMDataStack (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.NEMDataStack) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'NEMDataStack': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *BPMH) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// KeySignatureOffsetSize returns the size in bytes of the value of field KeySignatureOffset -func (s *BPMH) KeySignatureOffsetTotalSize() uint64 { - return 2 -} - -// BPMRevisionSize returns the size in bytes of the value of field BPMRevision -func (s *BPMH) BPMRevisionTotalSize() uint64 { - return 1 -} - -// BPMSVNSize returns the size in bytes of the value of field BPMSVN -func (s *BPMH) BPMSVNTotalSize() uint64 { - return 1 -} - -// ACMSVNAuthSize returns the size in bytes of the value of field ACMSVNAuth -func (s *BPMH) ACMSVNAuthTotalSize() uint64 { - return 1 -} - -// Reserved0Size returns the size in bytes of the value of field Reserved0 -func (s *BPMH) Reserved0TotalSize() uint64 { - return 1 -} - -// NEMDataStackSize returns the size in bytes of the value of field NEMDataStack -func (s *BPMH) NEMDataStackTotalSize() uint64 { - return 2 -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *BPMH) StructInfoOffset() uint64 { - return 0 -} - -// KeySignatureOffsetOffset returns the offset in bytes of field KeySignatureOffset -func (s *BPMH) KeySignatureOffsetOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// BPMRevisionOffset returns the offset in bytes of field BPMRevision -func (s *BPMH) BPMRevisionOffset() uint64 { - return s.KeySignatureOffsetOffset() + s.KeySignatureOffsetTotalSize() -} - -// BPMSVNOffset returns the offset in bytes of field BPMSVN -func (s *BPMH) BPMSVNOffset() uint64 { - return s.BPMRevisionOffset() + s.BPMRevisionTotalSize() -} - -// ACMSVNAuthOffset returns the offset in bytes of field ACMSVNAuth -func (s *BPMH) ACMSVNAuthOffset() uint64 { - return s.BPMSVNOffset() + s.BPMSVNTotalSize() -} - -// Reserved0Offset returns the offset in bytes of field Reserved0 -func (s *BPMH) Reserved0Offset() uint64 { - return s.ACMSVNAuthOffset() + s.ACMSVNAuthTotalSize() -} - -// NEMDataStackOffset returns the offset in bytes of field NEMDataStack -func (s *BPMH) NEMDataStackOffset() uint64 { - return s.Reserved0Offset() + s.Reserved0TotalSize() -} - -// Size returns the total size of the BPMH. -func (s *BPMH) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.KeySignatureOffsetTotalSize() - size += s.BPMRevisionTotalSize() - size += s.BPMSVNTotalSize() - size += s.ACMSVNAuthTotalSize() - size += s.Reserved0TotalSize() - size += s.NEMDataStackTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *BPMH) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "BPMH", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Signature Offset", "", &s.KeySignatureOffset, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "BPM Revision", "", &s.BPMRevision, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "BPM SVN", "", &s.BPMSVN, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "ACM SVN Auth", "", &s.ACMSVNAuth, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "NEM Data Stack", "", &s.NEMDataStack, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v Size4K) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Size 4 K", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", v.InBytes(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v Size4K) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the Size4K into 'w' in binary format. -func (v Size4K) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the Size4K from 'r' in binary format. -func (v Size4K) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest.go deleted file mode 100644 index d595c27f..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -import ( - "bytes" - "fmt" - - pkgbytes "github.com/linuxboot/fiano/pkg/bytes" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/uefi" -) - -// StructInfo is the common header of any element. -type StructInfo = cbnt.StructInfo - -// Manifest is a boot policy manifest -// -// PrettyString: Boot Policy Manifest -type Manifest struct { - // BPMH is the header of the boot policy manifest - // - // PrettyString: BPMH: Header - BPMH `rehashValue:"rehashedBPMH()" json:"bpmHeader"` - - SE []SE `json:"bpmSE"` - TXTE *TXT `json:"bpmTXTE,omitempty"` - Res *Reserved `json:"bpmReserved,omitempty"` - - // PCDE is the platform configuration data element - // - // PrettyString: PCDE: Platform Config Data - PCDE *PCD `json:"bpmPCDE,omitempty"` - - // PME is the platform manufacturer element - // - // PrettyString: PME: Platform Manufacturer - PME *PM `json:"bpmPME,omitempty"` - - // PMSE is the signature element - // - // PrettyString: PMSE: Signature - PMSE Signature `json:"bpmSignature"` -} - -// StructInfo is the information about how to parse the structure. -func (bpm Manifest) StructInfo() StructInfo { - return bpm.BPMH.StructInfo -} - -// ValidateIBB returns an error if IBB segments does not match the signature -func (bpm *Manifest) ValidateIBB(firmware uefi.Firmware) error { - if len(bpm.SE[0].DigestList.List) == 0 { - return fmt.Errorf("no IBB hashes") - } - - digest := bpm.SE[0].DigestList.List[0] // [0] instead of range -- is intentionally - - h, err := digest.HashAlg.Hash() - if err != nil { - return fmt.Errorf("invalid hash function: %v", digest.HashAlg) - } - - for _, _range := range bpm.IBBDataRanges(uint64(len(firmware.Buf()))) { - if _, err := h.Write(firmware.Buf()[_range.Offset:_range.End()]); err != nil { - return fmt.Errorf("unable to hash: %w", err) - } - } - hashValue := h.Sum(nil) - - if !bytes.Equal(hashValue, digest.HashBuffer) { - return fmt.Errorf("IBB %s hash mismatch: %X != %X", digest.HashAlg, hashValue, digest.HashBuffer) - } - - return nil -} - -// IBBDataRanges returns data ranges of IBB. -func (bpm *Manifest) IBBDataRanges(firmwareSize uint64) pkgbytes.Ranges { - var result pkgbytes.Ranges - - for _, seg := range bpm.SE[0].IBBSegments { - if seg.Flags&1 == 1 { - continue - } - startIdx := calculateOffsetFromPhysAddr(uint64(seg.Base), firmwareSize) - result = append(result, pkgbytes.Range{Offset: startIdx, Length: uint64(seg.Size)}) - } - - return result -} - -// calculateOffsetFromPhysAddr calculates the offset within an image -// of the physical address (address to a region mapped from -// the SPI chip). -// -// Examples: -// -// calculateOffsetFromPhysAddr(0xffffffff, 0x1000) == 0xfff -// calculateOffsetFromPhysAddr(0xffffffc0, 0x1000) == 0xfc0 -func calculateOffsetFromPhysAddr(physAddr uint64, imageSize uint64) uint64 { - const basePhysAddr = 1 << 32 // "4GiB" - startAddr := basePhysAddr - imageSize - return physAddr - startAddr -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_manifestcodegen.go deleted file mode 100644 index 89a53843..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_manifestcodegen.go +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewManifest returns a new instance of Manifest with -// all default values set. -func NewManifest() *Manifest { - s := &Manifest{} - // Recursively initializing a child structure: - s.BPMH = *NewBPMH() - // Recursively initializing a child structure: - s.PMSE = *NewSignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Manifest) Validate() error { - // Recursively validating a child structure: - if err := s.BPMH.Validate(); err != nil { - return fmt.Errorf("error on field 'BPMH': %w", err) - } - // See tag "rehashValue" - { - expectedValue := BPMH(s.rehashedBPMH()) - if s.BPMH != expectedValue { - return fmt.Errorf("field 'BPMH' expects write-value '%v', but has %v", expectedValue, s.BPMH) - } - } - // Recursively validating a child structure: - if err := s.PMSE.Validate(); err != nil { - return fmt.Errorf("error on field 'PMSE': %w", err) - } - - return nil -} - -// fieldIndexByStructID returns the position index within -// structure Manifest of the field by its StructureID -// (see document #575623, an example of StructureID value is "__KEYM__"). -func (_ Manifest) fieldIndexByStructID(structID string) int { - switch structID { - case StructureIDBPMH: - return 0 - case StructureIDSE: - return 1 - case StructureIDTXT: - return 2 - case StructureIDReserved: - return 3 - case StructureIDPCD: - return 4 - case StructureIDPM: - return 5 - case StructureIDSignature: - return 6 - } - - return -1 -} - -// fieldNameByIndex returns the name of the field by its position number -// within structure Manifest. -func (_ Manifest) fieldNameByIndex(fieldIndex int) string { - switch fieldIndex { - case 0: - return "BPMH" - case 1: - return "SE" - case 2: - return "TXTE" - case 3: - return "Res" - case 4: - return "PCDE" - case 5: - return "PME" - case 6: - return "PMSE" - } - - return fmt.Sprintf("invalidFieldIndex_%d", fieldIndex) -} - -// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. -func (s *Manifest) ReadFrom(r io.Reader) (returnN int64, returnErr error) { - var missingFieldsByIndices = [7]bool{ - 0: true, - 6: true, - } - defer func() { - if returnErr != nil { - return - } - for fieldIndex, v := range missingFieldsByIndices { - if v { - returnErr = fmt.Errorf("field '%s' is missing", s.fieldNameByIndex(fieldIndex)) - break - } - } - }() - var totalN int64 - previousFieldIndex := int(-1) - for { - var structInfo cbnt.StructInfo - err := binary.Read(r, binary.LittleEndian, &structInfo) - if err == io.EOF || err == io.ErrUnexpectedEOF { - return totalN, nil - } - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(structInfo)) - - structID := structInfo.ID.String() - fieldIndex := s.fieldIndexByStructID(structID) - if fieldIndex < 0 { - // TODO: report error "unknown structure ID: '"+structID+"'" - continue - } - if cbnt.StrictOrderCheck && fieldIndex < previousFieldIndex { - return totalN, fmt.Errorf("invalid order of fields (%d < %d): structure '%s' is out of order", fieldIndex, previousFieldIndex, structID) - } - missingFieldsByIndices[fieldIndex] = false - - var n int64 - switch structID { - case StructureIDBPMH: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'BPMH' is not a slice, but multiple elements found") - } - s.BPMH.SetStructInfo(structInfo) - n, err = s.BPMH.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field BPMH at %d: %w", totalN, err) - } - case StructureIDSE: - var el SE - el.SetStructInfo(structInfo) - n, err = el.ReadDataFrom(r) - s.SE = append(s.SE, el) - if err != nil { - return totalN, fmt.Errorf("unable to read field SE at %d: %w", totalN, err) - } - case StructureIDTXT: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'TXTE' is not a slice, but multiple elements found") - } - s.TXTE = &TXT{} - s.TXTE.SetStructInfo(structInfo) - n, err = s.TXTE.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field TXTE at %d: %w", totalN, err) - } - case StructureIDReserved: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'Res' is not a slice, but multiple elements found") - } - s.Res = &Reserved{} - s.Res.SetStructInfo(structInfo) - n, err = s.Res.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field Res at %d: %w", totalN, err) - } - case StructureIDPCD: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'PCDE' is not a slice, but multiple elements found") - } - s.PCDE = &PCD{} - s.PCDE.SetStructInfo(structInfo) - n, err = s.PCDE.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field PCDE at %d: %w", totalN, err) - } - case StructureIDPM: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'PME' is not a slice, but multiple elements found") - } - s.PME = &PM{} - s.PME.SetStructInfo(structInfo) - n, err = s.PME.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field PME at %d: %w", totalN, err) - } - case StructureIDSignature: - if fieldIndex == previousFieldIndex { - return totalN, fmt.Errorf("field 'PMSE' is not a slice, but multiple elements found") - } - s.PMSE.SetStructInfo(structInfo) - n, err = s.PMSE.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field PMSE at %d: %w", totalN, err) - } - default: - return totalN, fmt.Errorf("there is no field with structure ID '%s' in Manifest", structInfo.ID) - } - totalN += n - previousFieldIndex = fieldIndex - } -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Manifest) RehashRecursive() { - s.BPMH.Rehash() - if s.TXTE != nil { - s.TXTE.Rehash() - } - if s.Res != nil { - s.Res.Rehash() - } - if s.PCDE != nil { - s.PCDE.Rehash() - } - if s.PME != nil { - s.PME.Rehash() - } - s.PMSE.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Manifest) Rehash() { - s.BPMH = BPMH(s.rehashedBPMH()) -} - -// WriteTo writes the Manifest into 'w' in format defined in -// the document #575623. -func (s *Manifest) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // BPMH (ManifestFieldType: element) - { - n, err := s.BPMH.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'BPMH': %w", err) - } - totalN += int64(n) - } - - // SE (ManifestFieldType: elementList) - { - for idx := range s.SE { - n, err := s.SE[idx].WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SE[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - // TXTE (ManifestFieldType: element) - if s.TXTE != nil { - n, err := s.TXTE.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'TXTE': %w", err) - } - totalN += int64(n) - } - - // Res (ManifestFieldType: element) - if s.Res != nil { - n, err := s.Res.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Res': %w", err) - } - totalN += int64(n) - } - - // PCDE (ManifestFieldType: element) - if s.PCDE != nil { - n, err := s.PCDE.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PCDE': %w", err) - } - totalN += int64(n) - } - - // PME (ManifestFieldType: element) - if s.PME != nil { - n, err := s.PME.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PME': %w", err) - } - totalN += int64(n) - } - - // PMSE (ManifestFieldType: element) - { - n, err := s.PMSE.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PMSE': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// BPMHSize returns the size in bytes of the value of field BPMH -func (s *Manifest) BPMHTotalSize() uint64 { - return s.BPMH.TotalSize() -} - -// SESize returns the size in bytes of the value of field SE -func (s *Manifest) SETotalSize() uint64 { - var size uint64 - for idx := range s.SE { - size += s.SE[idx].TotalSize() - } - return size -} - -// TXTESize returns the size in bytes of the value of field TXTE -func (s *Manifest) TXTETotalSize() uint64 { - return s.TXTE.TotalSize() -} - -// ResSize returns the size in bytes of the value of field Res -func (s *Manifest) ResTotalSize() uint64 { - return s.Res.TotalSize() -} - -// PCDESize returns the size in bytes of the value of field PCDE -func (s *Manifest) PCDETotalSize() uint64 { - return s.PCDE.TotalSize() -} - -// PMESize returns the size in bytes of the value of field PME -func (s *Manifest) PMETotalSize() uint64 { - return s.PME.TotalSize() -} - -// PMSESize returns the size in bytes of the value of field PMSE -func (s *Manifest) PMSETotalSize() uint64 { - return s.PMSE.TotalSize() -} - -// BPMHOffset returns the offset in bytes of field BPMH -func (s *Manifest) BPMHOffset() uint64 { - return 0 -} - -// SEOffset returns the offset in bytes of field SE -func (s *Manifest) SEOffset() uint64 { - return s.BPMHOffset() + s.BPMHTotalSize() -} - -// TXTEOffset returns the offset in bytes of field TXTE -func (s *Manifest) TXTEOffset() uint64 { - return s.SEOffset() + s.SETotalSize() -} - -// ResOffset returns the offset in bytes of field Res -func (s *Manifest) ResOffset() uint64 { - return s.TXTEOffset() + s.TXTETotalSize() -} - -// PCDEOffset returns the offset in bytes of field PCDE -func (s *Manifest) PCDEOffset() uint64 { - return s.ResOffset() + s.ResTotalSize() -} - -// PMEOffset returns the offset in bytes of field PME -func (s *Manifest) PMEOffset() uint64 { - return s.PCDEOffset() + s.PCDETotalSize() -} - -// PMSEOffset returns the offset in bytes of field PMSE -func (s *Manifest) PMSEOffset() uint64 { - return s.PMEOffset() + s.PMETotalSize() -} - -// Size returns the total size of the Manifest. -func (s *Manifest) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.BPMHTotalSize() - size += s.SETotalSize() - size += s.TXTETotalSize() - size += s.ResTotalSize() - size += s.PCDETotalSize() - size += s.PMETotalSize() - size += s.PMSETotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Manifest) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Boot Policy Manifest", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "BPMH: Header", "", &s.BPMH, opts...)...) - // ManifestFieldType is elementList - lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("SE: Array of \"Boot Policy Manifest\" of length %d", len(s.SE)), s.SE)) - for i := 0; i < len(s.SE); i++ { - lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.SE[i].PrettyString(depth+2, true))) - } - if depth < 1 { - lines = append(lines, "") - } - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "TXTE", "", s.TXTE, opts...)...) - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "Res", "", s.Res, opts...)...) - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "PCDE: Platform Config Data", "", s.PCDE, opts...)...) - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "PME: Platform Manufacturer", "", s.PME, opts...)...) - // ManifestFieldType is element - lines = append(lines, pretty.SubValue(depth+1, "PMSE: Signature", "", &s.PMSE, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_nocodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_nocodegen.go deleted file mode 100644 index 271b7b70..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_nocodegen.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// -// To avoid errors "bpm.KeySignatureOffsetTotalSize undefined" and -// "bpm.BPMH.PrettyString undefined" we place these functions to a file -// with a build tag "!manifestcodegen" - -package cbntbootpolicy - -import ( - "fmt" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -func (bpm *Manifest) rehashedBPMH() BPMH { - bpmh := bpm.BPMH - bpmh.KeySignatureOffset = uint16(bpm.PMSEOffset() + bpm.PMSE.KeySignatureOffset()) - return bpmh -} - -// Print prints the Manifest -func (bpm Manifest) Print() { - fmt.Printf("%v", bpm.BPMH.PrettyString(1, true)) - for _, item := range bpm.SE { - fmt.Printf("%v", item.PrettyString(1, true)) - } - if bpm.TXTE != nil { - fmt.Printf("%v\n", bpm.TXTE.PrettyString(1, true)) - } else { - fmt.Printf(" --TXTE--\n\t not set!(optional)\n") - } - - if bpm.PCDE != nil { - fmt.Printf("%v\n", bpm.PCDE.PrettyString(1, true)) - } else { - fmt.Println(" --PCDE-- \n\tnot set!(optional)") - } - - if bpm.PME != nil { - fmt.Printf("%v\n", bpm.PME.PrettyString(1, true)) - } else { - fmt.Println(" --PME--\n\tnot set!(optional)") - } - - if bpm.PMSE.Signature.DataTotalSize() < 1 { - fmt.Printf("%v\n", bpm.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) - fmt.Printf(" --PMSE--\n\tBoot Policy Manifest not signed!\n\n") - } else { - fmt.Printf("%v\n", bpm.PMSE.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) - } -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_test.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_test.go deleted file mode 100644 index 0b7de9a1..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/manifest_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cbntbootpolicy - -import ( - "testing" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/unittest" -) - -func TestReadWrite(t *testing.T) { - unittest.CBNTManifestReadWrite(t, &Manifest{}, "testdata/bpm.bin") -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/pcd.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/pcd.go deleted file mode 100644 index 466812b7..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/pcd.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -// PCD holds various Platform Config Data. -type PCD struct { - StructInfo `id:"__PCDS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` - Reserved0 [2]byte `json:"pcdReserved0,omitempty"` - Data []byte `json:"pcdData"` -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/pcd_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/pcd_manifestcodegen.go deleted file mode 100644 index 63ad3e3a..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/pcd_manifestcodegen.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewPCD returns a new instance of PCD with -// all default values set. -func NewPCD() *PCD { - s := &PCD{} - copy(s.StructInfo.ID[:], []byte(StructureIDPCD)) - s.StructInfo.Version = 0x20 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *PCD) Validate() error { - - return nil -} - -// StructureIDPCD is the StructureID (in terms of -// the document #575623) of element 'PCD'. -const StructureIDPCD = "__PCDS__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *PCD) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *PCD) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the PCD from 'r' in format defined in the document #575623. -func (s *PCD) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the PCD from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *PCD) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - var size uint16 - err := binary.Read(r, binary.LittleEndian, &size) - if err != nil { - return totalN, fmt.Errorf("unable to the read size of field 'Data': %w", err) - } - totalN += int64(binary.Size(size)) - s.Data = make([]byte, size) - n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *PCD) RehashRecursive() { - s.StructInfo.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *PCD) Rehash() { - s.Variable0 = 0 - s.ElementSize = uint16(s.TotalSize()) -} - -// WriteTo writes the PCD into 'w' in format defined in -// the document #575623. -func (s *PCD) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - size := uint16(len(s.Data)) - err := binary.Write(w, binary.LittleEndian, size) - if err != nil { - return totalN, fmt.Errorf("unable to write the size of field 'Data': %w", err) - } - totalN += int64(binary.Size(size)) - n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *PCD) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// Reserved0Size returns the size in bytes of the value of field Reserved0 -func (s *PCD) Reserved0TotalSize() uint64 { - return 2 -} - -// DataSize returns the size in bytes of the value of field Data -func (s *PCD) DataTotalSize() uint64 { - size := uint64(binary.Size(uint16(0))) - size += uint64(len(s.Data)) - return size -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *PCD) StructInfoOffset() uint64 { - return 0 -} - -// Reserved0Offset returns the offset in bytes of field Reserved0 -func (s *PCD) Reserved0Offset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// DataOffset returns the offset in bytes of field Data -func (s *PCD) DataOffset() uint64 { - return s.Reserved0Offset() + s.Reserved0TotalSize() -} - -// Size returns the total size of the PCD. -func (s *PCD) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.Reserved0TotalSize() - size += s.DataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *PCD) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "PCD", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/pm.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/pm.go deleted file mode 100644 index 4e339ce5..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/pm.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -// PM is the platform manufacturer data element -type PM struct { - StructInfo `id:"__PMDA__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` - Reserved0 [2]byte `require:"0" json:"pcReserved0,omitempty"` - Data []byte `json:"pcData"` -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/pm_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/pm_manifestcodegen.go deleted file mode 100644 index 6054707c..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/pm_manifestcodegen.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewPM returns a new instance of PM with -// all default values set. -func NewPM() *PM { - s := &PM{} - copy(s.StructInfo.ID[:], []byte(StructureIDPM)) - s.StructInfo.Version = 0x20 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *PM) Validate() error { - // See tag "require" - for idx := range s.Reserved0 { - if s.Reserved0[idx] != 0 { - return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) - } - } - - return nil -} - -// StructureIDPM is the StructureID (in terms of -// the document #575623) of element 'PM'. -const StructureIDPM = "__PMDA__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *PM) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *PM) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the PM from 'r' in format defined in the document #575623. -func (s *PM) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the PM from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *PM) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - var size uint16 - err := binary.Read(r, binary.LittleEndian, &size) - if err != nil { - return totalN, fmt.Errorf("unable to the read size of field 'Data': %w", err) - } - totalN += int64(binary.Size(size)) - s.Data = make([]byte, size) - n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *PM) RehashRecursive() { - s.StructInfo.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *PM) Rehash() { - s.Variable0 = 0 - s.ElementSize = uint16(s.TotalSize()) -} - -// WriteTo writes the PM into 'w' in format defined in -// the document #575623. -func (s *PM) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - size := uint16(len(s.Data)) - err := binary.Write(w, binary.LittleEndian, size) - if err != nil { - return totalN, fmt.Errorf("unable to write the size of field 'Data': %w", err) - } - totalN += int64(binary.Size(size)) - n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *PM) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// Reserved0Size returns the size in bytes of the value of field Reserved0 -func (s *PM) Reserved0TotalSize() uint64 { - return 2 -} - -// DataSize returns the size in bytes of the value of field Data -func (s *PM) DataTotalSize() uint64 { - size := uint64(binary.Size(uint16(0))) - size += uint64(len(s.Data)) - return size -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *PM) StructInfoOffset() uint64 { - return 0 -} - -// Reserved0Offset returns the offset in bytes of field Reserved0 -func (s *PM) Reserved0Offset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// DataOffset returns the offset in bytes of field Data -func (s *PM) DataOffset() uint64 { - return s.Reserved0Offset() + s.Reserved0TotalSize() -} - -// Size returns the total size of the PM. -func (s *PM) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.Reserved0TotalSize() - size += s.DataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *PM) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "PM", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/se.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/se.go deleted file mode 100644 index 5e122c62..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/se.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -import ( - "fmt" - "math" - "time" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" -) - -// SE is an IBB segments element -// -// PrettyString: IBB Segments Element -type SE struct { - StructInfo `id:"__IBBS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"` - Reserved0 [1]byte `require:"0" json:"seReserved0,omitempty"` - SetNumber uint8 `require:"0" json:"seSetNumber,omitempty"` - Reserved1 [1]byte `require:"0" json:"seReserved1,omitempty"` - PBETValue PBETValue `json:"sePBETValue"` - Flags SEFlags `json:"seFlags"` - - // IBBMCHBAR - // PrettyString: IBB MCHBAR - IBBMCHBAR uint64 `json:"seIBBMCHBAR"` - - // VTdBAR - // PrettyString: VT-d BAR - VTdBAR uint64 `json:"seVTdBAR"` - - // DMAProtBase0 - // PrettyString: DMA Protection 0 Base Address - DMAProtBase0 uint32 `json:"seDMAProtBase0"` - - // DMAProtLimit0 - // PrettyString: DMA Protection 0 Limit Address - DMAProtLimit0 uint32 `json:"seDMAProtLimit0"` - - // DMAProtBase1 - // PrettyString: DMA Protection 1 Base Address - DMAProtBase1 uint64 `json:"seDMAProtBase1"` - - // DMAProtLimit1 - // PrettyString: DMA Protection 2 Limit Address - DMAProtLimit1 uint64 `json:"seDMAProtLimit1"` - - PostIBBHash cbnt.HashStructure `json:"sePostIBBHash"` - - IBBEntryPoint uint32 `json:"seIBBEntry"` - - DigestList cbnt.HashList `json:"seDigestList"` - - OBBHash cbnt.HashStructure `json:"seOBBHash"` - - Reserved2 [3]byte `require:"0" json:"seReserved2,omitempty"` - - IBBSegments []IBBSegment `countType:"uint8" json:"seIBBSegments,omitempty"` -} - -// PBETValue -type PBETValue uint8 - -// PBETValue returns the raw value of the timer setting. -func (pbet PBETValue) PBETValue() uint8 { - return uint8(pbet) & 0x0f -} - -// Duration returns the value as time.Duration. -func (pbet PBETValue) Duration() time.Duration { - v := pbet.PBETValue() - if v == 0 { - return math.MaxInt64 - } - return time.Second * time.Duration(5+v) -} - -// SetDuration sets the value using standard time.Duration as the input. -func (pbet *PBETValue) SetDuration(duration time.Duration) time.Duration { - v := duration.Nanoseconds()/time.Second.Nanoseconds() - 5 - if v <= 0 { - v = 1 - } - if v >= 16 { - v = 0 - } - *pbet = PBETValue(v) - - return pbet.Duration() -} - -// SEFlags -type SEFlags uint32 - -// Reserved0 -func (flags SEFlags) Reserved0() uint32 { - return uint32(flags & 0xffffffe0) -} - -// SupportsTopSwapRemediation -// -// PrettyString-true: BIOS supports Top Swap remediation action -// PrettyString-false: BIOS does not support Top Swap remediation action -func (flags SEFlags) SupportsTopSwapRemediation() bool { - return flags&0x10 != 0 -} - -// TPMFailureLeavesHierarchiesEnabled -// -// PrettyString-true: Leave Hierarchies enabled. Cap all PCRs on failure. -// PrettyString-false: Do not leave enabled. Disable all Hierarchies or deactivate on failure. -func (flags SEFlags) TPMFailureLeavesHierarchiesEnabled() bool { - return flags&0x08 != 0 -} - -// AuthorityMeasure -// -// PrettyString-true: Extend Authority Measurements into the Authority PCR 7 -// PrettyString-false: Do not extend into the Authority PCR 7 -func (flags SEFlags) AuthorityMeasure() bool { - return flags&0x04 != 0 -} - -// Locality3Startup -// -// PrettyString-true: Issue TPM Start-up from Locality 3 -// PrettyString-false: Disabled -func (flags SEFlags) Locality3Startup() bool { - return flags&0x02 != 0 -} - -// DMAProtection -// -// PrettyString-true: Enable DMA Protection -// PrettyString-false: Disable DMA Protection -func (flags SEFlags) DMAProtection() bool { - return flags&0x01 != 0 -} - -// IBBSegment defines a single IBB segment -type IBBSegment struct { - Reserved [2]byte `require:"0" json:"ibbSegReserved"` - Flags uint16 `json:"ibbSegFlags"` - Base uint32 `json:"ibbSegBase"` - Size uint32 `json:"ibbSegSize"` -} - -// CachingType -type CachingType uint8 - -// -const ( - CachingTypeWriteProtect = CachingType(iota) - CachingTypeWriteBack - CachingTypeReserved0 - CachingTypeReserved1 -) - -// String implements fmt.Stringer. -func (c CachingType) String() string { - switch c { - case CachingTypeWriteProtect: - return "write_protect" - case CachingTypeWriteBack: - return "write_back" - case CachingTypeReserved0: - return "value_0x02" - case CachingTypeReserved1: - return "value_0x03" - } - return fmt.Sprintf("unexpected_value_0x%02X", uint8(c)) -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/se_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/se_manifestcodegen.go deleted file mode 100644 index ce5b82da..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/se_manifestcodegen.go +++ /dev/null @@ -1,1061 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewIBBSegment returns a new instance of IBBSegment with -// all default values set. -func NewIBBSegment() *IBBSegment { - s := &IBBSegment{} - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *IBBSegment) Validate() error { - // See tag "require" - for idx := range s.Reserved { - if s.Reserved[idx] != 0 { - return fmt.Errorf("'Reserved[%d]' is expected to be 0, but it is %v", idx, s.Reserved[idx]) - } - } - - return nil -} - -// ReadFrom reads the IBBSegment from 'r' in format defined in the document #575623. -func (s *IBBSegment) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Reserved (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Flags': %w", err) - } - totalN += int64(n) - } - - // Base (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Base) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Base': %w", err) - } - totalN += int64(n) - } - - // Size (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Size) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Size': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *IBBSegment) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *IBBSegment) Rehash() { -} - -// WriteTo writes the IBBSegment into 'w' in format defined in -// the document #575623. -func (s *IBBSegment) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Reserved (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Flags': %w", err) - } - totalN += int64(n) - } - - // Base (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Base) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Base': %w", err) - } - totalN += int64(n) - } - - // Size (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Size) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Size': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// ReservedSize returns the size in bytes of the value of field Reserved -func (s *IBBSegment) ReservedTotalSize() uint64 { - return 2 -} - -// FlagsSize returns the size in bytes of the value of field Flags -func (s *IBBSegment) FlagsTotalSize() uint64 { - return 2 -} - -// BaseSize returns the size in bytes of the value of field Base -func (s *IBBSegment) BaseTotalSize() uint64 { - return 4 -} - -// SizeSize returns the size in bytes of the value of field Size -func (s *IBBSegment) SizeTotalSize() uint64 { - return 4 -} - -// ReservedOffset returns the offset in bytes of field Reserved -func (s *IBBSegment) ReservedOffset() uint64 { - return 0 -} - -// FlagsOffset returns the offset in bytes of field Flags -func (s *IBBSegment) FlagsOffset() uint64 { - return s.ReservedOffset() + s.ReservedTotalSize() -} - -// BaseOffset returns the offset in bytes of field Base -func (s *IBBSegment) BaseOffset() uint64 { - return s.FlagsOffset() + s.FlagsTotalSize() -} - -// SizeOffset returns the offset in bytes of field Size -func (s *IBBSegment) SizeOffset() uint64 { - return s.BaseOffset() + s.BaseTotalSize() -} - -// Size returns the total size of the IBBSegment. -func (s *IBBSegment) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.ReservedTotalSize() - size += s.FlagsTotalSize() - size += s.BaseTotalSize() - size += s.SizeTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *IBBSegment) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "IBB Segment", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved", "", &s.Reserved, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Flags", "", &s.Flags, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Base", "", &s.Base, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Size", "", &s.Size, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// NewSE returns a new instance of SE with -// all default values set. -func NewSE() *SE { - s := &SE{} - copy(s.StructInfo.ID[:], []byte(StructureIDSE)) - s.StructInfo.Version = 0x20 - // Set through tag "required": - s.SetNumber = 0 - // Recursively initializing a child structure: - s.PostIBBHash = *cbnt.NewHashStructure() - // Recursively initializing a child structure: - s.DigestList = *cbnt.NewHashList() - // Recursively initializing a child structure: - s.OBBHash = *cbnt.NewHashStructure() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *SE) Validate() error { - // See tag "require" - for idx := range s.Reserved0 { - if s.Reserved0[idx] != 0 { - return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) - } - } - // See tag "require" - if s.SetNumber != 0 { - return fmt.Errorf("field 'SetNumber' expects value '0', but has %v", s.SetNumber) - } - // See tag "require" - for idx := range s.Reserved1 { - if s.Reserved1[idx] != 0 { - return fmt.Errorf("'Reserved1[%d]' is expected to be 0, but it is %v", idx, s.Reserved1[idx]) - } - } - // Recursively validating a child structure: - if err := s.PostIBBHash.Validate(); err != nil { - return fmt.Errorf("error on field 'PostIBBHash': %w", err) - } - // Recursively validating a child structure: - if err := s.DigestList.Validate(); err != nil { - return fmt.Errorf("error on field 'DigestList': %w", err) - } - // Recursively validating a child structure: - if err := s.OBBHash.Validate(); err != nil { - return fmt.Errorf("error on field 'OBBHash': %w", err) - } - // See tag "require" - for idx := range s.Reserved2 { - if s.Reserved2[idx] != 0 { - return fmt.Errorf("'Reserved2[%d]' is expected to be 0, but it is %v", idx, s.Reserved2[idx]) - } - } - - return nil -} - -// StructureIDSE is the StructureID (in terms of -// the document #575623) of element 'SE'. -const StructureIDSE = "__IBBS__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *SE) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *SE) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the SE from 'r' in format defined in the document #575623. -func (s *SE) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the SE from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *SE) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // SetNumber (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.SetNumber) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'SetNumber': %w", err) - } - totalN += int64(n) - } - - // Reserved1 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved1[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved1': %w", err) - } - totalN += int64(n) - } - - // PBETValue (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.PBETValue) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PBETValue': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Flags': %w", err) - } - totalN += int64(n) - } - - // IBBMCHBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Read(r, binary.LittleEndian, &s.IBBMCHBAR) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'IBBMCHBAR': %w", err) - } - totalN += int64(n) - } - - // VTdBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Read(r, binary.LittleEndian, &s.VTdBAR) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'VTdBAR': %w", err) - } - totalN += int64(n) - } - - // DMAProtBase0 (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.DMAProtBase0) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'DMAProtBase0': %w", err) - } - totalN += int64(n) - } - - // DMAProtLimit0 (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.DMAProtLimit0) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'DMAProtLimit0': %w", err) - } - totalN += int64(n) - } - - // DMAProtBase1 (ManifestFieldType: endValue) - { - n, err := 8, binary.Read(r, binary.LittleEndian, &s.DMAProtBase1) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'DMAProtBase1': %w", err) - } - totalN += int64(n) - } - - // DMAProtLimit1 (ManifestFieldType: endValue) - { - n, err := 8, binary.Read(r, binary.LittleEndian, &s.DMAProtLimit1) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'DMAProtLimit1': %w", err) - } - totalN += int64(n) - } - - // PostIBBHash (ManifestFieldType: subStruct) - { - n, err := s.PostIBBHash.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PostIBBHash': %w", err) - } - totalN += int64(n) - } - - // IBBEntryPoint (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.IBBEntryPoint) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'IBBEntryPoint': %w", err) - } - totalN += int64(n) - } - - // DigestList (ManifestFieldType: subStruct) - { - n, err := s.DigestList.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'DigestList': %w", err) - } - totalN += int64(n) - } - - // OBBHash (ManifestFieldType: subStruct) - { - n, err := s.OBBHash.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'OBBHash': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Read(r, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // IBBSegments (ManifestFieldType: list) - { - var count uint8 - err := binary.Read(r, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to read the count for field 'IBBSegments': %w", err) - } - totalN += int64(binary.Size(count)) - s.IBBSegments = make([]IBBSegment, count) - - for idx := range s.IBBSegments { - n, err := s.IBBSegments[idx].ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'IBBSegments[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *SE) RehashRecursive() { - s.StructInfo.Rehash() - s.PostIBBHash.Rehash() - s.DigestList.Rehash() - s.OBBHash.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *SE) Rehash() { - s.Variable0 = 0 - s.ElementSize = uint16(s.TotalSize()) -} - -// WriteTo writes the SE into 'w' in format defined in -// the document #575623. -func (s *SE) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // SetNumber (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.SetNumber) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SetNumber': %w", err) - } - totalN += int64(n) - } - - // Reserved1 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved1[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved1': %w", err) - } - totalN += int64(n) - } - - // PBETValue (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.PBETValue) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PBETValue': %w", err) - } - totalN += int64(n) - } - - // Flags (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Flags) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Flags': %w", err) - } - totalN += int64(n) - } - - // IBBMCHBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Write(w, binary.LittleEndian, &s.IBBMCHBAR) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'IBBMCHBAR': %w", err) - } - totalN += int64(n) - } - - // VTdBAR (ManifestFieldType: endValue) - { - n, err := 8, binary.Write(w, binary.LittleEndian, &s.VTdBAR) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'VTdBAR': %w", err) - } - totalN += int64(n) - } - - // DMAProtBase0 (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.DMAProtBase0) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'DMAProtBase0': %w", err) - } - totalN += int64(n) - } - - // DMAProtLimit0 (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.DMAProtLimit0) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'DMAProtLimit0': %w", err) - } - totalN += int64(n) - } - - // DMAProtBase1 (ManifestFieldType: endValue) - { - n, err := 8, binary.Write(w, binary.LittleEndian, &s.DMAProtBase1) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'DMAProtBase1': %w", err) - } - totalN += int64(n) - } - - // DMAProtLimit1 (ManifestFieldType: endValue) - { - n, err := 8, binary.Write(w, binary.LittleEndian, &s.DMAProtLimit1) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'DMAProtLimit1': %w", err) - } - totalN += int64(n) - } - - // PostIBBHash (ManifestFieldType: subStruct) - { - n, err := s.PostIBBHash.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PostIBBHash': %w", err) - } - totalN += int64(n) - } - - // IBBEntryPoint (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.IBBEntryPoint) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'IBBEntryPoint': %w", err) - } - totalN += int64(n) - } - - // DigestList (ManifestFieldType: subStruct) - { - n, err := s.DigestList.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'DigestList': %w", err) - } - totalN += int64(n) - } - - // OBBHash (ManifestFieldType: subStruct) - { - n, err := s.OBBHash.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'OBBHash': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Write(w, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // IBBSegments (ManifestFieldType: list) - { - count := uint8(len(s.IBBSegments)) - err := binary.Write(w, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to write the count for field 'IBBSegments': %w", err) - } - totalN += int64(binary.Size(count)) - for idx := range s.IBBSegments { - n, err := s.IBBSegments[idx].WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'IBBSegments[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *SE) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// Reserved0Size returns the size in bytes of the value of field Reserved0 -func (s *SE) Reserved0TotalSize() uint64 { - return 1 -} - -// SetNumberSize returns the size in bytes of the value of field SetNumber -func (s *SE) SetNumberTotalSize() uint64 { - return 1 -} - -// Reserved1Size returns the size in bytes of the value of field Reserved1 -func (s *SE) Reserved1TotalSize() uint64 { - return 1 -} - -// PBETValueSize returns the size in bytes of the value of field PBETValue -func (s *SE) PBETValueTotalSize() uint64 { - return 1 -} - -// FlagsSize returns the size in bytes of the value of field Flags -func (s *SE) FlagsTotalSize() uint64 { - return 4 -} - -// IBBMCHBARSize returns the size in bytes of the value of field IBBMCHBAR -func (s *SE) IBBMCHBARTotalSize() uint64 { - return 8 -} - -// VTdBARSize returns the size in bytes of the value of field VTdBAR -func (s *SE) VTdBARTotalSize() uint64 { - return 8 -} - -// DMAProtBase0Size returns the size in bytes of the value of field DMAProtBase0 -func (s *SE) DMAProtBase0TotalSize() uint64 { - return 4 -} - -// DMAProtLimit0Size returns the size in bytes of the value of field DMAProtLimit0 -func (s *SE) DMAProtLimit0TotalSize() uint64 { - return 4 -} - -// DMAProtBase1Size returns the size in bytes of the value of field DMAProtBase1 -func (s *SE) DMAProtBase1TotalSize() uint64 { - return 8 -} - -// DMAProtLimit1Size returns the size in bytes of the value of field DMAProtLimit1 -func (s *SE) DMAProtLimit1TotalSize() uint64 { - return 8 -} - -// PostIBBHashSize returns the size in bytes of the value of field PostIBBHash -func (s *SE) PostIBBHashTotalSize() uint64 { - return s.PostIBBHash.TotalSize() -} - -// IBBEntryPointSize returns the size in bytes of the value of field IBBEntryPoint -func (s *SE) IBBEntryPointTotalSize() uint64 { - return 4 -} - -// DigestListSize returns the size in bytes of the value of field DigestList -func (s *SE) DigestListTotalSize() uint64 { - return s.DigestList.TotalSize() -} - -// OBBHashSize returns the size in bytes of the value of field OBBHash -func (s *SE) OBBHashTotalSize() uint64 { - return s.OBBHash.TotalSize() -} - -// Reserved2Size returns the size in bytes of the value of field Reserved2 -func (s *SE) Reserved2TotalSize() uint64 { - return 3 -} - -// IBBSegmentsSize returns the size in bytes of the value of field IBBSegments -func (s *SE) IBBSegmentsTotalSize() uint64 { - var size uint64 - size += uint64(binary.Size(uint8(0))) - for idx := range s.IBBSegments { - size += s.IBBSegments[idx].TotalSize() - } - return size -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *SE) StructInfoOffset() uint64 { - return 0 -} - -// Reserved0Offset returns the offset in bytes of field Reserved0 -func (s *SE) Reserved0Offset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// SetNumberOffset returns the offset in bytes of field SetNumber -func (s *SE) SetNumberOffset() uint64 { - return s.Reserved0Offset() + s.Reserved0TotalSize() -} - -// Reserved1Offset returns the offset in bytes of field Reserved1 -func (s *SE) Reserved1Offset() uint64 { - return s.SetNumberOffset() + s.SetNumberTotalSize() -} - -// PBETValueOffset returns the offset in bytes of field PBETValue -func (s *SE) PBETValueOffset() uint64 { - return s.Reserved1Offset() + s.Reserved1TotalSize() -} - -// FlagsOffset returns the offset in bytes of field Flags -func (s *SE) FlagsOffset() uint64 { - return s.PBETValueOffset() + s.PBETValueTotalSize() -} - -// IBBMCHBAROffset returns the offset in bytes of field IBBMCHBAR -func (s *SE) IBBMCHBAROffset() uint64 { - return s.FlagsOffset() + s.FlagsTotalSize() -} - -// VTdBAROffset returns the offset in bytes of field VTdBAR -func (s *SE) VTdBAROffset() uint64 { - return s.IBBMCHBAROffset() + s.IBBMCHBARTotalSize() -} - -// DMAProtBase0Offset returns the offset in bytes of field DMAProtBase0 -func (s *SE) DMAProtBase0Offset() uint64 { - return s.VTdBAROffset() + s.VTdBARTotalSize() -} - -// DMAProtLimit0Offset returns the offset in bytes of field DMAProtLimit0 -func (s *SE) DMAProtLimit0Offset() uint64 { - return s.DMAProtBase0Offset() + s.DMAProtBase0TotalSize() -} - -// DMAProtBase1Offset returns the offset in bytes of field DMAProtBase1 -func (s *SE) DMAProtBase1Offset() uint64 { - return s.DMAProtLimit0Offset() + s.DMAProtLimit0TotalSize() -} - -// DMAProtLimit1Offset returns the offset in bytes of field DMAProtLimit1 -func (s *SE) DMAProtLimit1Offset() uint64 { - return s.DMAProtBase1Offset() + s.DMAProtBase1TotalSize() -} - -// PostIBBHashOffset returns the offset in bytes of field PostIBBHash -func (s *SE) PostIBBHashOffset() uint64 { - return s.DMAProtLimit1Offset() + s.DMAProtLimit1TotalSize() -} - -// IBBEntryPointOffset returns the offset in bytes of field IBBEntryPoint -func (s *SE) IBBEntryPointOffset() uint64 { - return s.PostIBBHashOffset() + s.PostIBBHashTotalSize() -} - -// DigestListOffset returns the offset in bytes of field DigestList -func (s *SE) DigestListOffset() uint64 { - return s.IBBEntryPointOffset() + s.IBBEntryPointTotalSize() -} - -// OBBHashOffset returns the offset in bytes of field OBBHash -func (s *SE) OBBHashOffset() uint64 { - return s.DigestListOffset() + s.DigestListTotalSize() -} - -// Reserved2Offset returns the offset in bytes of field Reserved2 -func (s *SE) Reserved2Offset() uint64 { - return s.OBBHashOffset() + s.OBBHashTotalSize() -} - -// IBBSegmentsOffset returns the offset in bytes of field IBBSegments -func (s *SE) IBBSegmentsOffset() uint64 { - return s.Reserved2Offset() + s.Reserved2TotalSize() -} - -// Size returns the total size of the SE. -func (s *SE) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.Reserved0TotalSize() - size += s.SetNumberTotalSize() - size += s.Reserved1TotalSize() - size += s.PBETValueTotalSize() - size += s.FlagsTotalSize() - size += s.IBBMCHBARTotalSize() - size += s.VTdBARTotalSize() - size += s.DMAProtBase0TotalSize() - size += s.DMAProtLimit0TotalSize() - size += s.DMAProtBase1TotalSize() - size += s.DMAProtLimit1TotalSize() - size += s.PostIBBHashTotalSize() - size += s.IBBEntryPointTotalSize() - size += s.DigestListTotalSize() - size += s.OBBHashTotalSize() - size += s.Reserved2TotalSize() - size += s.IBBSegmentsTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *SE) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "IBB Segments Element", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Set Number", "", &s.SetNumber, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 1", "", &s.Reserved1, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "PBET Value", "", &s.PBETValue, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Flags", "", &s.Flags, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "IBB MCHBAR", "", &s.IBBMCHBAR, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "VT-d BAR", "", &s.VTdBAR, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 0 Base Address", "", &s.DMAProtBase0, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 0 Limit Address", "", &s.DMAProtLimit0, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 1 Base Address", "", &s.DMAProtBase1, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection 2 Limit Address", "", &s.DMAProtLimit1, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Post IBB Hash", "", &s.PostIBBHash, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "IBB Entry Point", "", &s.IBBEntryPoint, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Digest List", "", &s.DigestList, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "OBB Hash", "", &s.OBBHash, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 2", "", &s.Reserved2, opts...)...) - // ManifestFieldType is list - lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("IBBSegments: Array of \"IBB Segments Element\" of length %d", len(s.IBBSegments)), s.IBBSegments)) - for i := 0; i < len(s.IBBSegments); i++ { - lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.IBBSegments[i].PrettyString(depth+2, true))) - } - if depth < 1 { - lines = append(lines, "") - } - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v CachingType) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - return v.String() -} - -// TotalSize returns the total size measured through binary.Size. -func (v CachingType) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the CachingType into 'w' in binary format. -func (v CachingType) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the CachingType from 'r' in binary format. -func (v CachingType) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v PBETValue) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "PBET Value", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "PBET Value", "", v.PBETValue(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v PBETValue) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the PBETValue into 'w' in binary format. -func (v PBETValue) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the PBETValue from 'r' in binary format. -func (v PBETValue) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v SEFlags) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "SE Flags", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", v.Reserved0(), opts...)...) - if v.SupportsTopSwapRemediation() { - lines = append(lines, pretty.SubValue(depth+1, "Supports Top Swap Remediation", "BIOS supports Top Swap remediation action", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Supports Top Swap Remediation", "BIOS does not support Top Swap remediation action", false, opts...)...) - } - if v.TPMFailureLeavesHierarchiesEnabled() { - lines = append(lines, pretty.SubValue(depth+1, "TPM Failure Leaves Hierarchies Enabled", "Leave Hierarchies enabled. Cap all PCRs on failure.", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "TPM Failure Leaves Hierarchies Enabled", "Do not leave enabled. Disable all Hierarchies or deactivate on failure.", false, opts...)...) - } - if v.AuthorityMeasure() { - lines = append(lines, pretty.SubValue(depth+1, "Authority Measure", "Extend Authority Measurements into the Authority PCR 7", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Authority Measure", "Do not extend into the Authority PCR 7", false, opts...)...) - } - if v.Locality3Startup() { - lines = append(lines, pretty.SubValue(depth+1, "Locality 3 Startup", "Issue TPM Start-up from Locality 3", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Locality 3 Startup", "Disabled", false, opts...)...) - } - if v.DMAProtection() { - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection", "Enable DMA Protection", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "DMA Protection", "Disable DMA Protection", false, opts...)...) - } - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v SEFlags) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the SEFlags into 'w' in binary format. -func (v SEFlags) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the SEFlags from 'r' in binary format. -func (v SEFlags) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/signature.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/signature.go deleted file mode 100644 index 8c15c8c3..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/signature.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -import ( - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" -) - -// Signature contains the signature of the BPM. -type Signature struct { - StructInfo `id:"__PMSG__" version:"0x20" var0:"0" var1:"0"` - cbnt.KeySignature `json:"sigKeySignature"` -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/signature_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/signature_manifestcodegen.go deleted file mode 100644 index a043fd83..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/signature_manifestcodegen.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewSignature returns a new instance of Signature with -// all default values set. -func NewSignature() *Signature { - s := &Signature{} - copy(s.StructInfo.ID[:], []byte(StructureIDSignature)) - s.StructInfo.Version = 0x20 - // Recursively initializing a child structure: - s.KeySignature = *cbnt.NewKeySignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Signature) Validate() error { - // Recursively validating a child structure: - if err := s.KeySignature.Validate(); err != nil { - return fmt.Errorf("error on field 'KeySignature': %w", err) - } - - return nil -} - -// StructureIDSignature is the StructureID (in terms of -// the document #575623) of element 'Signature'. -const StructureIDSignature = "__PMSG__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Signature) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Signature) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the Signature from 'r' in format defined in the document #575623. -func (s *Signature) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the Signature from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *Signature) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // KeySignature (ManifestFieldType: subStruct) - { - n, err := s.KeySignature.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeySignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Signature) RehashRecursive() { - s.StructInfo.Rehash() - s.KeySignature.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Signature) Rehash() { - s.Variable0 = 0 - s.ElementSize = 0 -} - -// WriteTo writes the Signature into 'w' in format defined in -// the document #575623. -func (s *Signature) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // KeySignature (ManifestFieldType: subStruct) - { - n, err := s.KeySignature.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeySignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *Signature) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// KeySignatureSize returns the size in bytes of the value of field KeySignature -func (s *Signature) KeySignatureTotalSize() uint64 { - return s.KeySignature.TotalSize() -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *Signature) StructInfoOffset() uint64 { - return 0 -} - -// KeySignatureOffset returns the offset in bytes of field KeySignature -func (s *Signature) KeySignatureOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// Size returns the total size of the Signature. -func (s *Signature) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.KeySignatureTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Signature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Signature", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Key Signature", "", &s.KeySignature, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/txt.go deleted file mode 100644 index 7c9c1be0..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -import ( - "fmt" - "time" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" -) - -// TXT is the TXT element -type TXT struct { - StructInfo `id:"__TXTS__" version:"0x21" var0:"0" var1:"uint16(s.TotalSize())"` - Reserved0 [1]byte `require:"0" json:"txtReserved0,omitempty"` - SetNumber [1]byte `require:"0" json:"txtSetNumer,omitempty"` - SInitMinSVNAuth uint8 `default:"0" json:"txtSVN"` - Reserved1 [1]byte `require:"0" json:"txtReserved1,omitempty"` - ControlFlags TXTControlFlags `json:"txtFlags"` - PwrDownInterval Duration16In5Sec `json:"txtPwrDownInterval"` - // PrettyString: PTT CMOS Offset 0 - PTTCMOSOffset0 uint8 `default:"126" json:"txtPTTCMOSOffset0"` - // PrettyString: PTT CMOS Offset 1 - PTTCMOSOffset1 uint8 `default:"127" json:"txtPTTCMOSOffset1"` - ACPIBaseOffset uint16 `default:"0x400" json:"txtACPIBaseOffset,omitempty"` - Reserved2 [2]byte `json:"txtReserved2,omitempty"` - // PrettyString: ACPI MMIO Offset - PwrMBaseOffset uint32 `default:"0xFE000000" json:"txtPwrMBaseOffset,omitempty"` - DigestList cbnt.HashList `json:"txtDigestList"` - Reserved3 [3]byte `require:"0" json:"txtReserved3,omitempty"` - - SegmentCount uint8 `require:"0" json:"txtSegmentCount,omitempty"` -} - -// Duration16In5Sec exports the custom type Duration16In5Sec -type Duration16In5Sec uint16 - -// Duration calculates a given time in multiple of 5 seconds. -func (d Duration16In5Sec) Duration() time.Duration { - return time.Second * 5 * time.Duration(d) -} - -func (d Duration16In5Sec) String() string { - if d == 0 { - return "0 (infinite)" - } - return fmt.Sprintf("%d (%s)", d, d.Duration().String()) -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_control_flags.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_control_flags.go deleted file mode 100644 index 448a0ffd..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_control_flags.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntbootpolicy - -import ( - "fmt" -) - -type TXTControlFlags uint32 - -func (flags TXTControlFlags) ExecutionProfile() ExecutionProfile { - return ExecutionProfile(flags & 0x1f) -} - -type ExecutionProfile uint8 - -const ( - ExecutionProfileA = ExecutionProfile(iota) - ExecutionProfileB - ExecutionProfileC -) - -// String just implements fmt.Stringer. -func (p ExecutionProfile) String() string { - switch p { - case ExecutionProfileA: - return `A (use default selection based on differentation between clients, UP, and MP servers)` - case ExecutionProfileB: - return `B (use "Server model": rely on BIOS to configure topoligy; do not use ACHECK)` - case ExecutionProfileC: - return `C (use "Client model": do not measure BIOS into D-PCRs; use ACHECK-based alias check)` - } - return fmt.Sprintf("unexpected_execution_profile_value_0x%02X", uint8(p)) -} - -func (flags TXTControlFlags) MemoryScrubbingPolicy() MemoryScrubbingPolicy { - return MemoryScrubbingPolicy((flags >> 5) & 0x3) -} - -type MemoryScrubbingPolicy uint8 - -const ( - MemoryScrubbingPolicyDefault = MemoryScrubbingPolicy(iota) - MemoryScrubbingPolicyBIOS - MemoryScrubbingPolicySACM -) - -// String implements fmt.Stringer. -func (policy MemoryScrubbingPolicy) String() string { - switch policy { - case MemoryScrubbingPolicyDefault: - return "BIOS if verified or backup action othersize" - case MemoryScrubbingPolicyBIOS: - return "BIOS" - case MemoryScrubbingPolicySACM: - return "S-ACM" - } - return fmt.Sprintf("unexpected_value_0x%02X", uint8(policy)) -} - -func (flags TXTControlFlags) BackupActionPolicy() BackupActionPolicy { - return BackupActionPolicy((flags >> 7) & 0x3) -} - -type BackupActionPolicy uint8 - -const ( - BackupActionPolicyDefault = BackupActionPolicy(iota) - BackupActionPolicyForceMemoryPowerDown - BackupActionPolicyForceBtGUnbreakableShutdown -) - -// String implements fmt.Stringer. -func (policy BackupActionPolicy) String() string { - switch policy { - case BackupActionPolicyDefault: - return "memory power down if profile D or BtG unbreakable shutdown otherwise" - case BackupActionPolicyForceMemoryPowerDown: - return "memory power down" - case BackupActionPolicyForceBtGUnbreakableShutdown: - return "BtG unbreakable shutdown" - } - return fmt.Sprintf("unexpected_value_0x%02X", uint8(policy)) -} - -// PrettyString-true: Default setting. S-ACM is requested to extend static PCRs -// PrettyString-false: S-ACM is not requested to extend static PCRs -func (flags TXTControlFlags) IsSACMRequestedToExtendStaticPCRs() bool { - return (flags>>9)&0x01 == 0 -} - -func (flags TXTControlFlags) ResetAUXControl() ResetAUXControl { - return ResetAUXControl((flags >> 31) & 0x01) -} - -type ResetAUXControl uint8 - -const ( - ResetAUXControlResetAUXIndex = ResetAUXControl(iota) - ResetAUXControlDeleteAUXIndex -) - -// String implements fmt.Stringer. -func (c ResetAUXControl) String() string { - switch c { - case ResetAUXControlResetAUXIndex: - return "AUX reset leaf will reset AUX index" - case ResetAUXControlDeleteAUXIndex: - return "AUX reset leaf will delete AUX index" - } - return fmt.Sprintf("unexpected_value_0x%02X", uint8(c)) -} diff --git a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_manifestcodegen.go deleted file mode 100644 index 29a08e5e..00000000 --- a/pkg/intel/metadata/cbnt/cbntbootpolicy/txt_manifestcodegen.go +++ /dev/null @@ -1,670 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy - -package cbntbootpolicy - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewTXT returns a new instance of TXT with -// all default values set. -func NewTXT() *TXT { - s := &TXT{} - copy(s.StructInfo.ID[:], []byte(StructureIDTXT)) - s.StructInfo.Version = 0x21 - // Set through tag "default": - s.SInitMinSVNAuth = 0 - // Set through tag "default": - s.PTTCMOSOffset0 = 126 - // Set through tag "default": - s.PTTCMOSOffset1 = 127 - // Set through tag "default": - s.ACPIBaseOffset = 0x400 - // Set through tag "default": - s.PwrMBaseOffset = 0xFE000000 - // Recursively initializing a child structure: - s.DigestList = *cbnt.NewHashList() - // Set through tag "required": - s.SegmentCount = 0 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *TXT) Validate() error { - // See tag "require" - for idx := range s.Reserved0 { - if s.Reserved0[idx] != 0 { - return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx]) - } - } - // See tag "require" - for idx := range s.SetNumber { - if s.SetNumber[idx] != 0 { - return fmt.Errorf("'SetNumber[%d]' is expected to be 0, but it is %v", idx, s.SetNumber[idx]) - } - } - // See tag "require" - for idx := range s.Reserved1 { - if s.Reserved1[idx] != 0 { - return fmt.Errorf("'Reserved1[%d]' is expected to be 0, but it is %v", idx, s.Reserved1[idx]) - } - } - // Recursively validating a child structure: - if err := s.DigestList.Validate(); err != nil { - return fmt.Errorf("error on field 'DigestList': %w", err) - } - // See tag "require" - for idx := range s.Reserved3 { - if s.Reserved3[idx] != 0 { - return fmt.Errorf("'Reserved3[%d]' is expected to be 0, but it is %v", idx, s.Reserved3[idx]) - } - } - // See tag "require" - if s.SegmentCount != 0 { - return fmt.Errorf("field 'SegmentCount' expects value '0', but has %v", s.SegmentCount) - } - - return nil -} - -// StructureIDTXT is the StructureID (in terms of -// the document #575623) of element 'TXT'. -const StructureIDTXT = "__TXTS__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *TXT) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *TXT) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the TXT from 'r' in format defined in the document #575623. -func (s *TXT) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the TXT from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *TXT) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // SetNumber (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.SetNumber[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'SetNumber': %w", err) - } - totalN += int64(n) - } - - // SInitMinSVNAuth (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.SInitMinSVNAuth) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'SInitMinSVNAuth': %w", err) - } - totalN += int64(n) - } - - // Reserved1 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved1[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved1': %w", err) - } - totalN += int64(n) - } - - // ControlFlags (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.ControlFlags) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ControlFlags': %w", err) - } - totalN += int64(n) - } - - // PwrDownInterval (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.PwrDownInterval) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PwrDownInterval': %w", err) - } - totalN += int64(n) - } - - // PTTCMOSOffset0 (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.PTTCMOSOffset0) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PTTCMOSOffset0': %w", err) - } - totalN += int64(n) - } - - // PTTCMOSOffset1 (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.PTTCMOSOffset1) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PTTCMOSOffset1': %w", err) - } - totalN += int64(n) - } - - // ACPIBaseOffset (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.ACPIBaseOffset) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ACPIBaseOffset': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // PwrMBaseOffset (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.PwrMBaseOffset) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PwrMBaseOffset': %w", err) - } - totalN += int64(n) - } - - // DigestList (ManifestFieldType: subStruct) - { - n, err := s.DigestList.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'DigestList': %w", err) - } - totalN += int64(n) - } - - // Reserved3 (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Read(r, binary.LittleEndian, s.Reserved3[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved3': %w", err) - } - totalN += int64(n) - } - - // SegmentCount (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.SegmentCount) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'SegmentCount': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *TXT) RehashRecursive() { - s.StructInfo.Rehash() - s.DigestList.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *TXT) Rehash() { - s.Variable0 = 0 - s.ElementSize = uint16(s.TotalSize()) -} - -// WriteTo writes the TXT into 'w' in format defined in -// the document #575623. -func (s *TXT) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // Reserved0 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved0[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err) - } - totalN += int64(n) - } - - // SetNumber (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.SetNumber[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SetNumber': %w", err) - } - totalN += int64(n) - } - - // SInitMinSVNAuth (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.SInitMinSVNAuth) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SInitMinSVNAuth': %w", err) - } - totalN += int64(n) - } - - // Reserved1 (ManifestFieldType: arrayStatic) - { - n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved1[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved1': %w", err) - } - totalN += int64(n) - } - - // ControlFlags (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.ControlFlags) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ControlFlags': %w", err) - } - totalN += int64(n) - } - - // PwrDownInterval (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.PwrDownInterval) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PwrDownInterval': %w", err) - } - totalN += int64(n) - } - - // PTTCMOSOffset0 (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.PTTCMOSOffset0) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PTTCMOSOffset0': %w", err) - } - totalN += int64(n) - } - - // PTTCMOSOffset1 (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.PTTCMOSOffset1) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PTTCMOSOffset1': %w", err) - } - totalN += int64(n) - } - - // ACPIBaseOffset (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.ACPIBaseOffset) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ACPIBaseOffset': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // PwrMBaseOffset (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.PwrMBaseOffset) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PwrMBaseOffset': %w", err) - } - totalN += int64(n) - } - - // DigestList (ManifestFieldType: subStruct) - { - n, err := s.DigestList.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'DigestList': %w", err) - } - totalN += int64(n) - } - - // Reserved3 (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Write(w, binary.LittleEndian, s.Reserved3[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved3': %w", err) - } - totalN += int64(n) - } - - // SegmentCount (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.SegmentCount) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SegmentCount': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *TXT) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// Reserved0Size returns the size in bytes of the value of field Reserved0 -func (s *TXT) Reserved0TotalSize() uint64 { - return 1 -} - -// SetNumberSize returns the size in bytes of the value of field SetNumber -func (s *TXT) SetNumberTotalSize() uint64 { - return 1 -} - -// SInitMinSVNAuthSize returns the size in bytes of the value of field SInitMinSVNAuth -func (s *TXT) SInitMinSVNAuthTotalSize() uint64 { - return 1 -} - -// Reserved1Size returns the size in bytes of the value of field Reserved1 -func (s *TXT) Reserved1TotalSize() uint64 { - return 1 -} - -// ControlFlagsSize returns the size in bytes of the value of field ControlFlags -func (s *TXT) ControlFlagsTotalSize() uint64 { - return 4 -} - -// PwrDownIntervalSize returns the size in bytes of the value of field PwrDownInterval -func (s *TXT) PwrDownIntervalTotalSize() uint64 { - return 2 -} - -// PTTCMOSOffset0Size returns the size in bytes of the value of field PTTCMOSOffset0 -func (s *TXT) PTTCMOSOffset0TotalSize() uint64 { - return 1 -} - -// PTTCMOSOffset1Size returns the size in bytes of the value of field PTTCMOSOffset1 -func (s *TXT) PTTCMOSOffset1TotalSize() uint64 { - return 1 -} - -// ACPIBaseOffsetSize returns the size in bytes of the value of field ACPIBaseOffset -func (s *TXT) ACPIBaseOffsetTotalSize() uint64 { - return 2 -} - -// Reserved2Size returns the size in bytes of the value of field Reserved2 -func (s *TXT) Reserved2TotalSize() uint64 { - return 2 -} - -// PwrMBaseOffsetSize returns the size in bytes of the value of field PwrMBaseOffset -func (s *TXT) PwrMBaseOffsetTotalSize() uint64 { - return 4 -} - -// DigestListSize returns the size in bytes of the value of field DigestList -func (s *TXT) DigestListTotalSize() uint64 { - return s.DigestList.TotalSize() -} - -// Reserved3Size returns the size in bytes of the value of field Reserved3 -func (s *TXT) Reserved3TotalSize() uint64 { - return 3 -} - -// SegmentCountSize returns the size in bytes of the value of field SegmentCount -func (s *TXT) SegmentCountTotalSize() uint64 { - return 1 -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *TXT) StructInfoOffset() uint64 { - return 0 -} - -// Reserved0Offset returns the offset in bytes of field Reserved0 -func (s *TXT) Reserved0Offset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// SetNumberOffset returns the offset in bytes of field SetNumber -func (s *TXT) SetNumberOffset() uint64 { - return s.Reserved0Offset() + s.Reserved0TotalSize() -} - -// SInitMinSVNAuthOffset returns the offset in bytes of field SInitMinSVNAuth -func (s *TXT) SInitMinSVNAuthOffset() uint64 { - return s.SetNumberOffset() + s.SetNumberTotalSize() -} - -// Reserved1Offset returns the offset in bytes of field Reserved1 -func (s *TXT) Reserved1Offset() uint64 { - return s.SInitMinSVNAuthOffset() + s.SInitMinSVNAuthTotalSize() -} - -// ControlFlagsOffset returns the offset in bytes of field ControlFlags -func (s *TXT) ControlFlagsOffset() uint64 { - return s.Reserved1Offset() + s.Reserved1TotalSize() -} - -// PwrDownIntervalOffset returns the offset in bytes of field PwrDownInterval -func (s *TXT) PwrDownIntervalOffset() uint64 { - return s.ControlFlagsOffset() + s.ControlFlagsTotalSize() -} - -// PTTCMOSOffset0Offset returns the offset in bytes of field PTTCMOSOffset0 -func (s *TXT) PTTCMOSOffset0Offset() uint64 { - return s.PwrDownIntervalOffset() + s.PwrDownIntervalTotalSize() -} - -// PTTCMOSOffset1Offset returns the offset in bytes of field PTTCMOSOffset1 -func (s *TXT) PTTCMOSOffset1Offset() uint64 { - return s.PTTCMOSOffset0Offset() + s.PTTCMOSOffset0TotalSize() -} - -// ACPIBaseOffsetOffset returns the offset in bytes of field ACPIBaseOffset -func (s *TXT) ACPIBaseOffsetOffset() uint64 { - return s.PTTCMOSOffset1Offset() + s.PTTCMOSOffset1TotalSize() -} - -// Reserved2Offset returns the offset in bytes of field Reserved2 -func (s *TXT) Reserved2Offset() uint64 { - return s.ACPIBaseOffsetOffset() + s.ACPIBaseOffsetTotalSize() -} - -// PwrMBaseOffsetOffset returns the offset in bytes of field PwrMBaseOffset -func (s *TXT) PwrMBaseOffsetOffset() uint64 { - return s.Reserved2Offset() + s.Reserved2TotalSize() -} - -// DigestListOffset returns the offset in bytes of field DigestList -func (s *TXT) DigestListOffset() uint64 { - return s.PwrMBaseOffsetOffset() + s.PwrMBaseOffsetTotalSize() -} - -// Reserved3Offset returns the offset in bytes of field Reserved3 -func (s *TXT) Reserved3Offset() uint64 { - return s.DigestListOffset() + s.DigestListTotalSize() -} - -// SegmentCountOffset returns the offset in bytes of field SegmentCount -func (s *TXT) SegmentCountOffset() uint64 { - return s.Reserved3Offset() + s.Reserved3TotalSize() -} - -// Size returns the total size of the TXT. -func (s *TXT) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.Reserved0TotalSize() - size += s.SetNumberTotalSize() - size += s.SInitMinSVNAuthTotalSize() - size += s.Reserved1TotalSize() - size += s.ControlFlagsTotalSize() - size += s.PwrDownIntervalTotalSize() - size += s.PTTCMOSOffset0TotalSize() - size += s.PTTCMOSOffset1TotalSize() - size += s.ACPIBaseOffsetTotalSize() - size += s.Reserved2TotalSize() - size += s.PwrMBaseOffsetTotalSize() - size += s.DigestListTotalSize() - size += s.Reserved3TotalSize() - size += s.SegmentCountTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *TXT) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "TXT", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Set Number", "", &s.SetNumber, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "S Init Min SVN Auth", "", &s.SInitMinSVNAuth, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 1", "", &s.Reserved1, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Control Flags", "", &s.ControlFlags, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Pwr Down Interval", "", &s.PwrDownInterval, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "PTT CMOS Offset 0", "", &s.PTTCMOSOffset0, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "PTT CMOS Offset 1", "", &s.PTTCMOSOffset1, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "ACPI Base Offset", "", &s.ACPIBaseOffset, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 2", "", &s.Reserved2, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "ACPI MMIO Offset", "", &s.PwrMBaseOffset, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Digest List", "", &s.DigestList, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 3", "", &s.Reserved3, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Segment Count", "", &s.SegmentCount, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v Duration16In5Sec) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - return v.String() -} - -// TotalSize returns the total size measured through binary.Size. -func (v Duration16In5Sec) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the Duration16In5Sec into 'w' in binary format. -func (v Duration16In5Sec) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the Duration16In5Sec from 'r' in binary format. -func (v Duration16In5Sec) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/cbntkey/hash.go b/pkg/intel/metadata/cbnt/cbntkey/hash.go deleted file mode 100644 index 477919ab..00000000 --- a/pkg/intel/metadata/cbnt/cbntkey/hash.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntkey - -import ( - "fmt" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" -) - -// Hash is "KM hash Structure" defined in document #575623. -type Hash struct { - // Usage is the digest usage bitmask. - // - // More than one bit can be set to indicate shared digest usage. - // Usage of bit 0 is normative; other usages are informative. - Usage Usage `json:"hashUsage"` - - // Digest is the actual digest. - Digest cbnt.HashStructure `json:"hashStruct"` -} - -// Usage is the digest usage bitmask. -// -// More than one bit can be set to indicate shared digest usage. -// Usage of bit 0 is normative; other usages are informative. -type Usage uint64 - -const ( - // UsageBPMSigningPKD is the bit meaning the digest could be used as - // Boot Policy Manifest signing pubkey digest. - UsageBPMSigningPKD = Usage(1 << iota) - - // UsageFITPatchManifestSigningPKD is the bit meaning the digest could be used as - // FIT Patch Manifest signing pubkey digest. - UsageFITPatchManifestSigningPKD - - // UsageACMManifestSigningPKD is the bit meaning the digest could be used as - // ACM Manifest signing pubkey digest. - UsageACMManifestSigningPKD - - // UsageSDEVSigningPKD is the bit meaning the digest could be used as - // SDEV signing pubkey digest. - UsageSDEVSigningPKD - - // UsageReserved is a reserved bit - UsageReserved -) - -// String implements fmt.Stringer. -func (u Usage) String() string { - var result []string - for i := uint(0); i < 64; i++ { - f := Usage(1 << i) - if !u.IsSet(f) { - continue - } - var descr string - switch f { - case UsageBPMSigningPKD: - descr = "BPM_signing_pubkey_digest" - case UsageFITPatchManifestSigningPKD: - descr = "FIT_patch_manifest_signing_pubkey_digest" - case UsageACMManifestSigningPKD: - descr = "ACM_manifest_signing_pubkey_digest" - case UsageSDEVSigningPKD: - descr = "SDEV_signing_pubkey_digest" - case UsageReserved: - descr = "Reserved" - default: - descr = fmt.Sprintf("unexpected_bit_%d", i) - } - result = append(result, descr) - } - - return strings.Join(result, ",") -} - -// IsSet returns true if bits `f` are set in bitmask `u`. -func (u Usage) IsSet(f Usage) bool { - return u&f != 0 -} - -// Set sets/unsets the bits of `f` in bitmask `u`. -// -// To set the bits `v` should be true, to unset -- false. -func (u *Usage) Set(f Usage, v bool) { - if v { - *u |= f - } else { - *u &= ^f - } -} diff --git a/pkg/intel/metadata/cbnt/cbntkey/hash_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntkey/hash_manifestcodegen.go deleted file mode 100644 index f7a2c50a..00000000 --- a/pkg/intel/metadata/cbnt/cbntkey/hash_manifestcodegen.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey - -package cbntkey - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewHash returns a new instance of Hash with -// all default values set. -func NewHash() *Hash { - s := &Hash{} - // Recursively initializing a child structure: - s.Digest = *cbnt.NewHashStructure() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Hash) Validate() error { - // Recursively validating a child structure: - if err := s.Digest.Validate(); err != nil { - return fmt.Errorf("error on field 'Digest': %w", err) - } - - return nil -} - -// ReadFrom reads the Hash from 'r' in format defined in the document #575623. -func (s *Hash) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Usage (ManifestFieldType: endValue) - { - n, err := 8, binary.Read(r, binary.LittleEndian, &s.Usage) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Usage': %w", err) - } - totalN += int64(n) - } - - // Digest (ManifestFieldType: subStruct) - { - n, err := s.Digest.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Digest': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Hash) RehashRecursive() { - s.Digest.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Hash) Rehash() { -} - -// WriteTo writes the Hash into 'w' in format defined in -// the document #575623. -func (s *Hash) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Usage (ManifestFieldType: endValue) - { - n, err := 8, binary.Write(w, binary.LittleEndian, &s.Usage) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Usage': %w", err) - } - totalN += int64(n) - } - - // Digest (ManifestFieldType: subStruct) - { - n, err := s.Digest.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Digest': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// UsageSize returns the size in bytes of the value of field Usage -func (s *Hash) UsageTotalSize() uint64 { - return 8 -} - -// DigestSize returns the size in bytes of the value of field Digest -func (s *Hash) DigestTotalSize() uint64 { - return s.Digest.TotalSize() -} - -// UsageOffset returns the offset in bytes of field Usage -func (s *Hash) UsageOffset() uint64 { - return 0 -} - -// DigestOffset returns the offset in bytes of field Digest -func (s *Hash) DigestOffset() uint64 { - return s.UsageOffset() + s.UsageTotalSize() -} - -// Size returns the total size of the Hash. -func (s *Hash) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.UsageTotalSize() - size += s.DigestTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Hash) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Hash", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Usage", "", &s.Usage, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Digest", "", &s.Digest, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v Usage) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - return v.String() -} - -// TotalSize returns the total size measured through binary.Size. -func (v Usage) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the Usage into 'w' in binary format. -func (v Usage) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the Usage from 'r' in binary format. -func (v Usage) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/cbntkey/manifest.go b/pkg/intel/metadata/cbnt/cbntkey/manifest.go deleted file mode 100644 index 7b76ba82..00000000 --- a/pkg/intel/metadata/cbnt/cbntkey/manifest.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate manifestcodegen - -package cbntkey - -import ( - "bytes" - "crypto" - "fmt" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" -) - -// PrettyString: CBnT Key Manifest -type Manifest struct { - cbnt.StructInfo `id:"__KEYM__" version:"0x21" var0:"0" var1:"0"` - - // KeyManifestSignatureOffset is Key Manifest KeySignature offset. - // - // The original name is "KeySignatureOffset" (in #575623). - KeyManifestSignatureOffset uint16 `rehashValue:"KeyAndSignatureOffset()" json:"kmSigOffset,omitempty"` - - // Reserved2 is an alignment. - Reserved2 [3]byte `json:"kmReserved2,omitempty"` - - // Revision is the revision of the Key Manifest defined by the Platform - // Manufacturer. - Revision uint8 `json:"kmRevision"` - - // KMSVN is the Key Manifest Security Version Number. - KMSVN cbnt.SVN `json:"kmSVN"` - - // KMID is the Key Manifest Identifier. - KMID uint8 `json:"kmID"` - - // PubKeyHashAlg is the hash algorithm of OEM public key digest programmed - // into the FPF. - PubKeyHashAlg cbnt.Algorithm `json:"kmPubKeyHashAlg"` - - // Hash is the slice of KMHASH_STRUCT (KHS) structures (see table 5-3 - // of the document #575623). Describes BPM pubkey digest (among other). - Hash []Hash `json:"kmHash"` - - // KeyAndSignature is the Key Manifest signature. - KeyAndSignature cbnt.KeySignature `json:"kmKeySignature"` -} - -func (m *Manifest) SetSignature( - algo cbnt.Algorithm, - hashAlgo cbnt.Algorithm, - privKey crypto.Signer, - signedData []byte, -) error { - err := m.KeyAndSignature.SetSignature(algo, hashAlgo, privKey, signedData) - if err != nil { - return fmt.Errorf("unable to set the signature: %w", err) - } - m.PubKeyHashAlg = m.KeyAndSignature.Signature.HashAlg - - return nil -} - -func (m *Manifest) ValidateBPMKey(bpmKS cbnt.KeySignature) error { - hashCount := 0 - for _, hashEntry := range m.Hash { - if !hashEntry.Usage.IsSet(UsageBPMSigningPKD) { - continue - } - - h, err := hashEntry.Digest.HashAlg.Hash() - if err != nil { - return fmt.Errorf("invalid hash algo %v: %w", hashEntry.Digest.HashAlg, err) - } - - if len(hashEntry.Digest.HashBuffer) != h.Size() { - return fmt.Errorf("invalid hash lenght: actual:%d expected:%d", len(hashEntry.Digest.HashBuffer), h.Size()) - } - - switch bpmKS.Key.KeyAlg { - case cbnt.AlgRSA: - if _, err := h.Write(bpmKS.Key.Data[4:]); err != nil { - return fmt.Errorf("unable to hash: %w", err) - } - default: - return fmt.Errorf("unsupported key algorithm: %v", bpmKS.Key.KeyAlg) - } - digest := h.Sum(nil) - - if !bytes.Equal(hashEntry.Digest.HashBuffer, digest) { - return fmt.Errorf("BPM key hash does not match the one in KM: actual:%X != in-KM:%X (hash algo: %v)", digest, hashEntry.Digest.HashBuffer, hashEntry.Digest.HashAlg) - } - hashCount++ - } - - if hashCount == 0 { - return fmt.Errorf("no hash of BPM's key was found in KM") - } - - return nil -} diff --git a/pkg/intel/metadata/cbnt/cbntkey/manifest_manifestcodegen.go b/pkg/intel/metadata/cbnt/cbntkey/manifest_manifestcodegen.go deleted file mode 100644 index 9dc88db9..00000000 --- a/pkg/intel/metadata/cbnt/cbntkey/manifest_manifestcodegen.go +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey - -package cbntkey - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join - _ = cbnt.StructInfo{} -) - -// NewManifest returns a new instance of Manifest with -// all default values set. -func NewManifest() *Manifest { - s := &Manifest{} - copy(s.StructInfo.ID[:], []byte(StructureIDManifest)) - s.StructInfo.Version = 0x21 - // Recursively initializing a child structure: - s.KeyAndSignature = *cbnt.NewKeySignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Manifest) Validate() error { - // See tag "rehashValue" - { - expectedValue := uint16(s.KeyAndSignatureOffset()) - if s.KeyManifestSignatureOffset != expectedValue { - return fmt.Errorf("field 'KeyManifestSignatureOffset' expects write-value '%v', but has %v", expectedValue, s.KeyManifestSignatureOffset) - } - } - // Recursively validating a child structure: - if err := s.KeyAndSignature.Validate(); err != nil { - return fmt.Errorf("error on field 'KeyAndSignature': %w", err) - } - - return nil -} - -// StructureIDManifest is the StructureID (in terms of -// the document #575623) of element 'Manifest'. -const StructureIDManifest = "__KEYM__" - -// GetStructInfo returns current value of StructInfo of the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Manifest) GetStructInfo() cbnt.StructInfo { - return s.StructInfo -} - -// SetStructInfo sets new value of StructInfo to the structure. -// -// StructInfo is a set of standard fields with presented in any element -// ("element" in terms of document #575623). -func (s *Manifest) SetStructInfo(newStructInfo cbnt.StructInfo) { - s.StructInfo = newStructInfo -} - -// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. -func (s *Manifest) ReadFrom(r io.Reader) (int64, error) { - var totalN int64 - - err := binary.Read(r, binary.LittleEndian, &s.StructInfo) - if err != nil { - return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err) - } - totalN += int64(binary.Size(s.StructInfo)) - - n, err := s.ReadDataFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read data: %w", err) - } - totalN += n - - return totalN, nil -} - -// ReadDataFrom reads the Manifest from 'r' excluding StructInfo, -// in format defined in the document #575623. -func (s *Manifest) ReadDataFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // StructInfo (ManifestFieldType: structInfo) - { - // ReadDataFrom does not read Struct, use ReadFrom for that. - } - - // KeyManifestSignatureOffset (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeyManifestSignatureOffset) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeyManifestSignatureOffset': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Read(r, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // Revision (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Revision) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Revision': %w", err) - } - totalN += int64(n) - } - - // KMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.KMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KMSVN': %w", err) - } - totalN += int64(n) - } - - // KMID (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.KMID) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KMID': %w", err) - } - totalN += int64(n) - } - - // PubKeyHashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.PubKeyHashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'PubKeyHashAlg': %w", err) - } - totalN += int64(n) - } - - // Hash (ManifestFieldType: list) - { - var count uint16 - err := binary.Read(r, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to read the count for field 'Hash': %w", err) - } - totalN += int64(binary.Size(count)) - s.Hash = make([]Hash, count) - - for idx := range s.Hash { - n, err := s.Hash[idx].ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Hash[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - // KeyAndSignature (ManifestFieldType: subStruct) - { - n, err := s.KeyAndSignature.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeyAndSignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Manifest) RehashRecursive() { - s.StructInfo.Rehash() - s.KeyAndSignature.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Manifest) Rehash() { - s.Variable0 = 0 - s.ElementSize = 0 - s.KeyManifestSignatureOffset = uint16(s.KeyAndSignatureOffset()) -} - -// WriteTo writes the Manifest into 'w' in format defined in -// the document #575623. -func (s *Manifest) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // StructInfo (ManifestFieldType: structInfo) - { - n, err := s.StructInfo.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err) - } - totalN += int64(n) - } - - // KeyManifestSignatureOffset (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeyManifestSignatureOffset) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeyManifestSignatureOffset': %w", err) - } - totalN += int64(n) - } - - // Reserved2 (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Write(w, binary.LittleEndian, s.Reserved2[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Reserved2': %w", err) - } - totalN += int64(n) - } - - // Revision (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Revision) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Revision': %w", err) - } - totalN += int64(n) - } - - // KMSVN (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.KMSVN) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KMSVN': %w", err) - } - totalN += int64(n) - } - - // KMID (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.KMID) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KMID': %w", err) - } - totalN += int64(n) - } - - // PubKeyHashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.PubKeyHashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'PubKeyHashAlg': %w", err) - } - totalN += int64(n) - } - - // Hash (ManifestFieldType: list) - { - count := uint16(len(s.Hash)) - err := binary.Write(w, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to write the count for field 'Hash': %w", err) - } - totalN += int64(binary.Size(count)) - for idx := range s.Hash { - n, err := s.Hash[idx].WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Hash[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - // KeyAndSignature (ManifestFieldType: subStruct) - { - n, err := s.KeyAndSignature.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeyAndSignature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// StructInfoSize returns the size in bytes of the value of field StructInfo -func (s *Manifest) StructInfoTotalSize() uint64 { - return s.StructInfo.TotalSize() -} - -// KeyManifestSignatureOffsetSize returns the size in bytes of the value of field KeyManifestSignatureOffset -func (s *Manifest) KeyManifestSignatureOffsetTotalSize() uint64 { - return 2 -} - -// Reserved2Size returns the size in bytes of the value of field Reserved2 -func (s *Manifest) Reserved2TotalSize() uint64 { - return 3 -} - -// RevisionSize returns the size in bytes of the value of field Revision -func (s *Manifest) RevisionTotalSize() uint64 { - return 1 -} - -// KMSVNSize returns the size in bytes of the value of field KMSVN -func (s *Manifest) KMSVNTotalSize() uint64 { - return 1 -} - -// KMIDSize returns the size in bytes of the value of field KMID -func (s *Manifest) KMIDTotalSize() uint64 { - return 1 -} - -// PubKeyHashAlgSize returns the size in bytes of the value of field PubKeyHashAlg -func (s *Manifest) PubKeyHashAlgTotalSize() uint64 { - return 2 -} - -// HashSize returns the size in bytes of the value of field Hash -func (s *Manifest) HashTotalSize() uint64 { - var size uint64 - size += uint64(binary.Size(uint16(0))) - for idx := range s.Hash { - size += s.Hash[idx].TotalSize() - } - return size -} - -// KeyAndSignatureSize returns the size in bytes of the value of field KeyAndSignature -func (s *Manifest) KeyAndSignatureTotalSize() uint64 { - return s.KeyAndSignature.TotalSize() -} - -// StructInfoOffset returns the offset in bytes of field StructInfo -func (s *Manifest) StructInfoOffset() uint64 { - return 0 -} - -// KeyManifestSignatureOffsetOffset returns the offset in bytes of field KeyManifestSignatureOffset -func (s *Manifest) KeyManifestSignatureOffsetOffset() uint64 { - return s.StructInfoOffset() + s.StructInfoTotalSize() -} - -// Reserved2Offset returns the offset in bytes of field Reserved2 -func (s *Manifest) Reserved2Offset() uint64 { - return s.KeyManifestSignatureOffsetOffset() + s.KeyManifestSignatureOffsetTotalSize() -} - -// RevisionOffset returns the offset in bytes of field Revision -func (s *Manifest) RevisionOffset() uint64 { - return s.Reserved2Offset() + s.Reserved2TotalSize() -} - -// KMSVNOffset returns the offset in bytes of field KMSVN -func (s *Manifest) KMSVNOffset() uint64 { - return s.RevisionOffset() + s.RevisionTotalSize() -} - -// KMIDOffset returns the offset in bytes of field KMID -func (s *Manifest) KMIDOffset() uint64 { - return s.KMSVNOffset() + s.KMSVNTotalSize() -} - -// PubKeyHashAlgOffset returns the offset in bytes of field PubKeyHashAlg -func (s *Manifest) PubKeyHashAlgOffset() uint64 { - return s.KMIDOffset() + s.KMIDTotalSize() -} - -// HashOffset returns the offset in bytes of field Hash -func (s *Manifest) HashOffset() uint64 { - return s.PubKeyHashAlgOffset() + s.PubKeyHashAlgTotalSize() -} - -// KeyAndSignatureOffset returns the offset in bytes of field KeyAndSignature -func (s *Manifest) KeyAndSignatureOffset() uint64 { - return s.HashOffset() + s.HashTotalSize() -} - -// Size returns the total size of the Manifest. -func (s *Manifest) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.StructInfoTotalSize() - size += s.KeyManifestSignatureOffsetTotalSize() - size += s.Reserved2TotalSize() - size += s.RevisionTotalSize() - size += s.KMSVNTotalSize() - size += s.KMIDTotalSize() - size += s.PubKeyHashAlgTotalSize() - size += s.HashTotalSize() - size += s.KeyAndSignatureTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Manifest) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "CBnT Key Manifest", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is structInfo - lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Manifest Signature Offset", "", &s.KeyManifestSignatureOffset, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Reserved 2", "", &s.Reserved2, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Revision", "", &s.Revision, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "KMSVN", "", &s.KMSVN, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "KMID", "", &s.KMID, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Pub Key Hash Alg", "", &s.PubKeyHashAlg, opts...)...) - // ManifestFieldType is list - lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("Hash: Array of \"Key Manifest\" of length %d", len(s.Hash)), s.Hash)) - for i := 0; i < len(s.Hash); i++ { - lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.Hash[i].PrettyString(depth+2, true))) - } - if depth < 1 { - lines = append(lines, "") - } - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Key And Signature", "", &s.KeyAndSignature, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/cbntkey/manifest_nocodegen.go b/pkg/intel/metadata/cbnt/cbntkey/manifest_nocodegen.go deleted file mode 100644 index d3746d81..00000000 --- a/pkg/intel/metadata/cbnt/cbntkey/manifest_nocodegen.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// -// To avoid error "m.StructInfo.PrettyString undefined" we place this -// function to a file with a build tag "!manifestcodegen" - -package cbntkey - -import ( - "fmt" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -// Print prints the Key Manifest. -func (m *Manifest) Print() { - if m.KeyAndSignature.Signature.DataTotalSize() < 1 { - fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) - fmt.Printf(" --KeyAndSignature--\n\tKey Manifest not signed!\n\n") - } else { - fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) - } -} diff --git a/pkg/intel/metadata/cbnt/cbntkey/manifest_test.go b/pkg/intel/metadata/cbnt/cbntkey/manifest_test.go deleted file mode 100644 index 5b367c19..00000000 --- a/pkg/intel/metadata/cbnt/cbntkey/manifest_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cbntkey - -import ( - "testing" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/unittest" -) - -func TestReadWrite(t *testing.T) { - unittest.CBNTManifestReadWrite(t, &Manifest{}, "testdata/km.bin") -} diff --git a/pkg/intel/metadata/cbnt/chipset_ac_module_information.go b/pkg/intel/metadata/cbnt/chipset_ac_module_information.go index 156f087a..ad942111 100644 --- a/pkg/intel/metadata/cbnt/chipset_ac_module_information.go +++ b/pkg/intel/metadata/cbnt/chipset_ac_module_information.go @@ -1,11 +1,19 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate manifestcodegen - package cbnt +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +// See document 315168-017, A.1.2, Table 12, p.91 var chipsetACModuleInformationSignature = []byte{ 0xAA, 0x3A, 0xC0, 0x7F, 0xA7, 0x46, 0xDB, 0x18, 0x2E, 0xAC, 0x69, 0x8F, 0x8D, 0x41, 0x7F, 0x5A, @@ -13,6 +21,7 @@ var chipsetACModuleInformationSignature = []byte{ // ChipsetACModuleInformation represents Chipset AC Module Information Table parts for all versions type ChipsetACModuleInformation struct { + Common UUID [16]byte ChipsetACMType uint8 Version uint8 @@ -24,10 +33,175 @@ type ChipsetACModuleInformation struct { AcmVersion uint8 AcmRevision [3]uint8 ProcessorIDList uint32 + TPMInfoList uint32 +} + +// NewChipsetACModuleInformation returns a new instance of ChipsetACModuleInformation with +// all default values set. +func NewChipsetACModuleInformation() *ChipsetACModuleInformation { + s := &ChipsetACModuleInformation{} + return s +} + +// ParseChipsetACModuleInformation parses Chipset AC Module Information Table according to the version +func ParseChipsetACModuleInformation(r io.Reader) (ChipsetACModuleInformation, error) { + acm := NewChipsetACModuleInformation() + _, err := acm.ReadFrom(r) + if err != nil { + return ChipsetACModuleInformation{}, err + } + + if acm.Version >= 5 { + if !bytes.Equal(acm.UUID[:], chipsetACModuleInformationSignature) { + return ChipsetACModuleInformation{}, fmt.Errorf( + "incorrect UUID [%x], expected: [%x]", acm.UUID, chipsetACModuleInformationSignature) + } + + err = binary.Read(r, binary.LittleEndian, &acm.TPMInfoList) + if err != nil { + return ChipsetACModuleInformation{}, err + } + } + + return *acm, nil +} + +// ReadFrom reads the ChipsetACModuleInformation from 'r' in format defined in the document #575623. +func (s *ChipsetACModuleInformation) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// WriteTo writes the ChipsetACModuleInformation into 'w' in format defined in +// the document #575623. +func (s *ChipsetACModuleInformation) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s *ChipsetACModuleInformation) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "UUID", + Size: func() uint64 { return 16 }, + Value: func() any { return &s.UUID }, + Type: ManifestFieldArrayStatic, + }, + { + ID: 1, + Name: "Chipset ACM Type", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.ChipsetACMType }, + Type: ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Version }, + Type: ManifestFieldEndValue, + }, + { + ID: 3, + Name: "Length", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.Length }, + Type: ManifestFieldEndValue, + }, + { + ID: 4, + Name: "Chipset ID List", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.ChipsetIDList }, + Type: ManifestFieldEndValue, + }, + { + ID: 5, + Name: "Os Sinit Data Ver", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.OsSinitDataVer }, + Type: ManifestFieldEndValue, + }, + { + ID: 6, + Name: "Min Mle Header Ver", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.MinMleHeaderVer }, + Type: ManifestFieldEndValue, + }, + { + ID: 7, + Name: "Capabilities", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.Capabilities }, + Type: ManifestFieldEndValue, + }, + { + ID: 8, + Name: "Acm Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.AcmVersion }, + Type: ManifestFieldEndValue, + }, + { + ID: 9, + Name: "Acm Revision", + Size: func() uint64 { return 3 }, + Value: func() any { return &s.AcmRevision }, + Type: ManifestFieldArrayStatic, + }, + { + ID: 10, + Name: "Processor ID List", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.ProcessorIDList }, + Type: ManifestFieldEndValue, + }, + { + ID: 11, + Name: "TPM Info List", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.TPMInfoList }, + Type: ManifestFieldEndValue, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *ChipsetACModuleInformation) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *ChipsetACModuleInformation) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the manifest +func (s *ChipsetACModuleInformation) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) } -// ChipsetACModuleInformationV5 represents Chipset AC Module Information Table for version >= 5 -type ChipsetACModuleInformationV5 struct { - Base ChipsetACModuleInformation - TPMInfoList uint32 +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *ChipsetACModuleInformation) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, s, "Chipset AC Module Information", opts...) } diff --git a/pkg/intel/metadata/cbnt/chipset_ac_module_information_manifestcodegen.go b/pkg/intel/metadata/cbnt/chipset_ac_module_information_manifestcodegen.go deleted file mode 100644 index 6c3f1375..00000000 --- a/pkg/intel/metadata/cbnt/chipset_ac_module_information_manifestcodegen.go +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewChipsetACModuleInformation returns a new instance of ChipsetACModuleInformation with -// all default values set. -func NewChipsetACModuleInformation() *ChipsetACModuleInformation { - s := &ChipsetACModuleInformation{} - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *ChipsetACModuleInformation) Validate() error { - - return nil -} - -// ReadFrom reads the ChipsetACModuleInformation from 'r' in format defined in the document #575623. -func (s *ChipsetACModuleInformation) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // UUID (ManifestFieldType: arrayStatic) - { - n, err := 16, binary.Read(r, binary.LittleEndian, s.UUID[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'UUID': %w", err) - } - totalN += int64(n) - } - - // ChipsetACMType (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.ChipsetACMType) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ChipsetACMType': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // Length (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.Length) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Length': %w", err) - } - totalN += int64(n) - } - - // ChipsetIDList (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.ChipsetIDList) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ChipsetIDList': %w", err) - } - totalN += int64(n) - } - - // OsSinitDataVer (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.OsSinitDataVer) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'OsSinitDataVer': %w", err) - } - totalN += int64(n) - } - - // MinMleHeaderVer (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.MinMleHeaderVer) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'MinMleHeaderVer': %w", err) - } - totalN += int64(n) - } - - // Capabilities (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Capabilities) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Capabilities': %w", err) - } - totalN += int64(n) - } - - // AcmVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.AcmVersion) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'AcmVersion': %w", err) - } - totalN += int64(n) - } - - // AcmRevision (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Read(r, binary.LittleEndian, s.AcmRevision[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'AcmRevision': %w", err) - } - totalN += int64(n) - } - - // ProcessorIDList (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.ProcessorIDList) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ProcessorIDList': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *ChipsetACModuleInformation) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *ChipsetACModuleInformation) Rehash() { -} - -// WriteTo writes the ChipsetACModuleInformation into 'w' in format defined in -// the document #575623. -func (s *ChipsetACModuleInformation) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // UUID (ManifestFieldType: arrayStatic) - { - n, err := 16, binary.Write(w, binary.LittleEndian, s.UUID[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'UUID': %w", err) - } - totalN += int64(n) - } - - // ChipsetACMType (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.ChipsetACMType) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ChipsetACMType': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // Length (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.Length) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Length': %w", err) - } - totalN += int64(n) - } - - // ChipsetIDList (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.ChipsetIDList) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ChipsetIDList': %w", err) - } - totalN += int64(n) - } - - // OsSinitDataVer (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.OsSinitDataVer) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'OsSinitDataVer': %w", err) - } - totalN += int64(n) - } - - // MinMleHeaderVer (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.MinMleHeaderVer) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'MinMleHeaderVer': %w", err) - } - totalN += int64(n) - } - - // Capabilities (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Capabilities) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Capabilities': %w", err) - } - totalN += int64(n) - } - - // AcmVersion (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.AcmVersion) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'AcmVersion': %w", err) - } - totalN += int64(n) - } - - // AcmRevision (ManifestFieldType: arrayStatic) - { - n, err := 3, binary.Write(w, binary.LittleEndian, s.AcmRevision[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'AcmRevision': %w", err) - } - totalN += int64(n) - } - - // ProcessorIDList (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.ProcessorIDList) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ProcessorIDList': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// UUIDSize returns the size in bytes of the value of field UUID -func (s *ChipsetACModuleInformation) UUIDTotalSize() uint64 { - return 16 -} - -// ChipsetACMTypeSize returns the size in bytes of the value of field ChipsetACMType -func (s *ChipsetACModuleInformation) ChipsetACMTypeTotalSize() uint64 { - return 1 -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *ChipsetACModuleInformation) VersionTotalSize() uint64 { - return 1 -} - -// LengthSize returns the size in bytes of the value of field Length -func (s *ChipsetACModuleInformation) LengthTotalSize() uint64 { - return 2 -} - -// ChipsetIDListSize returns the size in bytes of the value of field ChipsetIDList -func (s *ChipsetACModuleInformation) ChipsetIDListTotalSize() uint64 { - return 4 -} - -// OsSinitDataVerSize returns the size in bytes of the value of field OsSinitDataVer -func (s *ChipsetACModuleInformation) OsSinitDataVerTotalSize() uint64 { - return 4 -} - -// MinMleHeaderVerSize returns the size in bytes of the value of field MinMleHeaderVer -func (s *ChipsetACModuleInformation) MinMleHeaderVerTotalSize() uint64 { - return 4 -} - -// CapabilitiesSize returns the size in bytes of the value of field Capabilities -func (s *ChipsetACModuleInformation) CapabilitiesTotalSize() uint64 { - return 4 -} - -// AcmVersionSize returns the size in bytes of the value of field AcmVersion -func (s *ChipsetACModuleInformation) AcmVersionTotalSize() uint64 { - return 1 -} - -// AcmRevisionSize returns the size in bytes of the value of field AcmRevision -func (s *ChipsetACModuleInformation) AcmRevisionTotalSize() uint64 { - return 3 -} - -// ProcessorIDListSize returns the size in bytes of the value of field ProcessorIDList -func (s *ChipsetACModuleInformation) ProcessorIDListTotalSize() uint64 { - return 4 -} - -// UUIDOffset returns the offset in bytes of field UUID -func (s *ChipsetACModuleInformation) UUIDOffset() uint64 { - return 0 -} - -// ChipsetACMTypeOffset returns the offset in bytes of field ChipsetACMType -func (s *ChipsetACModuleInformation) ChipsetACMTypeOffset() uint64 { - return s.UUIDOffset() + s.UUIDTotalSize() -} - -// VersionOffset returns the offset in bytes of field Version -func (s *ChipsetACModuleInformation) VersionOffset() uint64 { - return s.ChipsetACMTypeOffset() + s.ChipsetACMTypeTotalSize() -} - -// LengthOffset returns the offset in bytes of field Length -func (s *ChipsetACModuleInformation) LengthOffset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// ChipsetIDListOffset returns the offset in bytes of field ChipsetIDList -func (s *ChipsetACModuleInformation) ChipsetIDListOffset() uint64 { - return s.LengthOffset() + s.LengthTotalSize() -} - -// OsSinitDataVerOffset returns the offset in bytes of field OsSinitDataVer -func (s *ChipsetACModuleInformation) OsSinitDataVerOffset() uint64 { - return s.ChipsetIDListOffset() + s.ChipsetIDListTotalSize() -} - -// MinMleHeaderVerOffset returns the offset in bytes of field MinMleHeaderVer -func (s *ChipsetACModuleInformation) MinMleHeaderVerOffset() uint64 { - return s.OsSinitDataVerOffset() + s.OsSinitDataVerTotalSize() -} - -// CapabilitiesOffset returns the offset in bytes of field Capabilities -func (s *ChipsetACModuleInformation) CapabilitiesOffset() uint64 { - return s.MinMleHeaderVerOffset() + s.MinMleHeaderVerTotalSize() -} - -// AcmVersionOffset returns the offset in bytes of field AcmVersion -func (s *ChipsetACModuleInformation) AcmVersionOffset() uint64 { - return s.CapabilitiesOffset() + s.CapabilitiesTotalSize() -} - -// AcmRevisionOffset returns the offset in bytes of field AcmRevision -func (s *ChipsetACModuleInformation) AcmRevisionOffset() uint64 { - return s.AcmVersionOffset() + s.AcmVersionTotalSize() -} - -// ProcessorIDListOffset returns the offset in bytes of field ProcessorIDList -func (s *ChipsetACModuleInformation) ProcessorIDListOffset() uint64 { - return s.AcmRevisionOffset() + s.AcmRevisionTotalSize() -} - -// Size returns the total size of the ChipsetACModuleInformation. -func (s *ChipsetACModuleInformation) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.UUIDTotalSize() - size += s.ChipsetACMTypeTotalSize() - size += s.VersionTotalSize() - size += s.LengthTotalSize() - size += s.ChipsetIDListTotalSize() - size += s.OsSinitDataVerTotalSize() - size += s.MinMleHeaderVerTotalSize() - size += s.CapabilitiesTotalSize() - size += s.AcmVersionTotalSize() - size += s.AcmRevisionTotalSize() - size += s.ProcessorIDListTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *ChipsetACModuleInformation) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Chipset AC Module Information", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "UUID", "", &s.UUID, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Chipset ACM Type", "", &s.ChipsetACMType, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Length", "", &s.Length, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Chipset ID List", "", &s.ChipsetIDList, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Os Sinit Data Ver", "", &s.OsSinitDataVer, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Min Mle Header Ver", "", &s.MinMleHeaderVer, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Capabilities", "", &s.Capabilities, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Acm Version", "", &s.AcmVersion, opts...)...) - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "Acm Revision", "", &s.AcmRevision, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Processor ID List", "", &s.ProcessorIDList, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// NewChipsetACModuleInformationV5 returns a new instance of ChipsetACModuleInformationV5 with -// all default values set. -func NewChipsetACModuleInformationV5() *ChipsetACModuleInformationV5 { - s := &ChipsetACModuleInformationV5{} - // Recursively initializing a child structure: - s.Base = *NewChipsetACModuleInformation() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *ChipsetACModuleInformationV5) Validate() error { - // Recursively validating a child structure: - if err := s.Base.Validate(); err != nil { - return fmt.Errorf("error on field 'Base': %w", err) - } - - return nil -} - -// ReadFrom reads the ChipsetACModuleInformationV5 from 'r' in format defined in the document #575623. -func (s *ChipsetACModuleInformationV5) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Base (ManifestFieldType: subStruct) - { - n, err := s.Base.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Base': %w", err) - } - totalN += int64(n) - } - - // TPMInfoList (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.TPMInfoList) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'TPMInfoList': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *ChipsetACModuleInformationV5) RehashRecursive() { - s.Base.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *ChipsetACModuleInformationV5) Rehash() { -} - -// WriteTo writes the ChipsetACModuleInformationV5 into 'w' in format defined in -// the document #575623. -func (s *ChipsetACModuleInformationV5) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Base (ManifestFieldType: subStruct) - { - n, err := s.Base.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Base': %w", err) - } - totalN += int64(n) - } - - // TPMInfoList (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.TPMInfoList) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'TPMInfoList': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// BaseSize returns the size in bytes of the value of field Base -func (s *ChipsetACModuleInformationV5) BaseTotalSize() uint64 { - return s.Base.TotalSize() -} - -// TPMInfoListSize returns the size in bytes of the value of field TPMInfoList -func (s *ChipsetACModuleInformationV5) TPMInfoListTotalSize() uint64 { - return 4 -} - -// BaseOffset returns the offset in bytes of field Base -func (s *ChipsetACModuleInformationV5) BaseOffset() uint64 { - return 0 -} - -// TPMInfoListOffset returns the offset in bytes of field TPMInfoList -func (s *ChipsetACModuleInformationV5) TPMInfoListOffset() uint64 { - return s.BaseOffset() + s.BaseTotalSize() -} - -// Size returns the total size of the ChipsetACModuleInformationV5. -func (s *ChipsetACModuleInformationV5) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.BaseTotalSize() - size += s.TPMInfoListTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *ChipsetACModuleInformationV5) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Chipset AC Module Information V 5", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Base", "", &s.Base, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "TPM Info List", "", &s.TPMInfoList, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/chipset_ac_module_information_nocodegen.go b/pkg/intel/metadata/cbnt/chipset_ac_module_information_nocodegen.go deleted file mode 100644 index f0e4031d..00000000 --- a/pkg/intel/metadata/cbnt/chipset_ac_module_information_nocodegen.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// -// To avoid errors "type ChipsetACModuleInformation has no field or method ReadFrom" -// with a build tag "!manifestcodegen" - -package cbnt - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" -) - -// ParseChipsetACModuleInformation parses Chipset AC Module Information Table according to the version -func ParseChipsetACModuleInformation(r io.Reader) (int64, ChipsetACModuleInformationV5, error) { - var result ChipsetACModuleInformationV5 - total, err := result.Base.ReadFrom(r) - if !bytes.Equal(result.Base.UUID[:], chipsetACModuleInformationSignature) { - return 0, ChipsetACModuleInformationV5{}, fmt.Errorf( - "incorrect UUID [%x], expected: [%x]", result.Base.UUID, chipsetACModuleInformationSignature) - } - if err != nil { - return total, result, err - } - if result.Base.Version < 5 { - return total, result, nil - } - err = binary.Read(r, binary.LittleEndian, &result.TPMInfoList) - total += int64(binary.Size(result.TPMInfoList)) - return total, result, err -} diff --git a/pkg/intel/metadata/cbnt/chipset_ac_module_information_test.go b/pkg/intel/metadata/cbnt/chipset_ac_module_information_test.go new file mode 100644 index 00000000..2fcb2da4 --- /dev/null +++ b/pkg/intel/metadata/cbnt/chipset_ac_module_information_test.go @@ -0,0 +1,167 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "encoding/binary" + "strings" + "testing" +) + +func TestChipsetACModuleInformationNew(t *testing.T) { + t.Parallel() + + got := NewChipsetACModuleInformation() + if got == nil { + t.Fatal("NewChipsetACModuleInformation() = nil, want non-nil") + } + want := ChipsetACModuleInformation{} + if *got != want { + t.Errorf("NewChipsetACModuleInformation() = %+v, want zero-value struct", *got) + } +} + +func TestChipsetACModuleInformationReadWriteRoundTrip(t *testing.T) { + t.Parallel() + + want := ChipsetACModuleInformation{ + UUID: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + ChipsetACMType: 1, + Version: 4, + Length: 42, + ChipsetIDList: 100, + OsSinitDataVer: 101, + MinMleHeaderVer: 102, + Capabilities: 103, + AcmVersion: 2, + AcmRevision: [3]uint8{3, 4, 5}, + ProcessorIDList: 104, + } + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("ChipsetACModuleInformation.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("ChipsetACModuleInformation.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got ChipsetACModuleInformation + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("ChipsetACModuleInformation.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("ChipsetACModuleInformation.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got != want { + t.Errorf("ChipsetACModuleInformation round-trip = %+v, want %+v", got, want) + } +} + +func TestParseChipsetACModuleInformation(t *testing.T) { + t.Parallel() + + t.Run("version_lt_5", func(t *testing.T) { + acm := ChipsetACModuleInformation{Version: 4, Length: 0x1234} + var buf bytes.Buffer + if _, err := acm.WriteTo(&buf); err != nil { + t.Fatalf("ChipsetACModuleInformation.WriteTo() error = %v, want nil", err) + } + + got, err := ParseChipsetACModuleInformation(bytes.NewReader(buf.Bytes())) + if err != nil { + t.Fatalf("ParseChipsetACModuleInformation() error = %v, want nil", err) + } + if got.Version != 4 { + t.Errorf("ParseChipsetACModuleInformation() Version = %d, want %d", got.Version, 4) + } + }) + + t.Run("version_gte_5_reads_tpm_info_list", func(t *testing.T) { + acm := ChipsetACModuleInformation{Version: 5} + copy(acm.UUID[:], chipsetACModuleInformationSignature) + wantTPMInfoList := uint32(0xAABBCCDD) + + var buf bytes.Buffer + if _, err := acm.WriteTo(&buf); err != nil { + t.Fatalf("ChipsetACModuleInformation.WriteTo() error = %v, want nil", err) + } + if err := binary.Write(&buf, binary.LittleEndian, wantTPMInfoList); err != nil { + t.Fatalf("binary.Write(TPMInfoList) error = %v, want nil", err) + } + + got, err := ParseChipsetACModuleInformation(bytes.NewReader(buf.Bytes())) + if err != nil { + t.Fatalf("ParseChipsetACModuleInformation() error = %v, want nil", err) + } + if got.TPMInfoList != wantTPMInfoList { + t.Errorf("ParseChipsetACModuleInformation() TPMInfoList = 0x%x, want 0x%x", got.TPMInfoList, wantTPMInfoList) + } + }) + + t.Run("version_gte_5_invalid_uuid", func(t *testing.T) { + acm := ChipsetACModuleInformation{Version: 5} + acm.UUID = [16]byte{0xFF} + + var buf bytes.Buffer + if _, err := acm.WriteTo(&buf); err != nil { + t.Fatalf("ChipsetACModuleInformation.WriteTo() error = %v, want nil", err) + } + if err := binary.Write(&buf, binary.LittleEndian, uint32(1)); err != nil { + t.Fatalf("binary.Write(TPMInfoList) error = %v, want nil", err) + } + + if _, err := ParseChipsetACModuleInformation(bytes.NewReader(buf.Bytes())); err == nil { + t.Errorf("ParseChipsetACModuleInformation() error = nil, want non-nil") + } + }) +} + +func TestChipsetACModuleInformationMethods(t *testing.T) { + t.Parallel() + + acm := NewChipsetACModuleInformation() + + if got := len(acm.Layout()); got != 12 { + t.Errorf("len(ChipsetACModuleInformation.Layout()) = %d, want %d", got, 11) + } + + size0, err := acm.SizeOf(0) + if err != nil { + t.Fatalf("ChipsetACModuleInformation.SizeOf(0) error = %v, want nil", err) + } + if size0 != 16 { + t.Errorf("ChipsetACModuleInformation.SizeOf(0) = %d, want %d", size0, 16) + } + + offset10, err := acm.OffsetOf(10) + if err != nil { + t.Fatalf("ChipsetACModuleInformation.OffsetOf(10) error = %v, want nil", err) + } + if offset10 != 40 { + t.Errorf("ChipsetACModuleInformation.OffsetOf(10) = %d, want %d", offset10, 40) + } + + if _, err := acm.SizeOf(99); err == nil { + t.Errorf("ChipsetACModuleInformation.SizeOf(99) error = nil, want non-nil") + } + if _, err := acm.OffsetOf(99); err == nil { + t.Errorf("ChipsetACModuleInformation.OffsetOf(99) error = nil, want non-nil") + } + + var nilACM *ChipsetACModuleInformation + if got := nilACM.TotalSize(); got != 0 { + t.Errorf("(*ChipsetACModuleInformation)(nil).TotalSize() = %d, want %d", got, 0) + } + + pretty := acm.PrettyString(0, true) + if !strings.Contains(pretty, "Chipset AC Module Information") { + t.Errorf("ChipsetACModuleInformation.PrettyString() = %q, want to contain %q", pretty, "Chipset AC Module Information") + } +} diff --git a/pkg/intel/metadata/cbnt/common.go b/pkg/intel/metadata/cbnt/common.go new file mode 100644 index 00000000..d7ed6585 --- /dev/null +++ b/pkg/intel/metadata/cbnt/common.go @@ -0,0 +1,327 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cbnt provides representation of BG/CBnT structures. +package cbnt + +import ( + "encoding/binary" + "fmt" + "io" + "strings" + + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +var ( + endianess = binary.LittleEndian +) + +type LayoutProvider interface { + Layout() []LayoutField +} + +// Acts as an accessor for all the methods shared accross the types. +// All types implementing Structure should embed it. +type Common struct{} + +// TotalSize returns the sum of size of all fields from a provided Layout +// descriptor. +func (Common) TotalSize(p LayoutProvider) uint64 { + var total uint64 + for _, f := range p.Layout() { + total += f.Size() + } + return total +} + +// SizeOf returns the size of the field from a provided Layout descriptor, +// of a given id. +func (Common) SizeOf(p LayoutProvider, id int) (uint64, error) { + for _, f := range p.Layout() { + if f.ID == id { + return f.Size(), nil + } + } + + return 0, fmt.Errorf("has no field of ID %d", id) +} + +// OffsetOf returns the size of the field from a provided Layout descriptor, +// of a given id. +func (Common) OffsetOf(p LayoutProvider, id int) (uint64, error) { + var offset uint64 + + for _, f := range p.Layout() { + if f.ID == id { + return offset, nil + } + offset += f.Size() + } + + return 0, fmt.Errorf("has no field of ID %d", id) +} + +// PrettyString returns the content of the provided Layout descriptor in an +// easy-to-read format. +func (Common) PrettyString(depth uint, withHeader bool, p LayoutProvider, structName string, opts ...pretty.Option) string { + var lines []string + + if withHeader { + lines = append(lines, pretty.Header(depth, structName, p)) + } + + for _, f := range p.Layout() { + if f.Type == ManifestFieldList { + // Handling type detection here would be dirty here, let's not do that and just skip + // This case is difficult to handle without using reflection (which I really want to avoid), + // thus this is an exception from generalizing logic and has to be handled by the type itself. + continue + } + lines = append(lines, pretty.SubValue(depth+1, f.Name, "", f.Value(), opts...)...) + } + + if depth < 2 { + lines = append(lines, "") + } + return strings.Join(lines, "\n") +} + +// ReadFrom reads the 'r' and fills the fields of the provided Layout +// descriptor in format defined in the document #575623. +func (Common) ReadFrom(r io.Reader, p LayoutProvider) (int64, error) { + totalN := int64(0) + + for _, f := range p.Layout() { + switch f.Type { + case ManifestFieldEndValue: + n, err := readStatic(r, f.Size(), f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to read field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldArrayDynamicWithSize: + size := uint16(f.Size()) + n, err := readArrayDynamic(r, &size, f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to read field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldArrayDynamicWithPrefix: + n, err := readArrayDynamic(r, nil, f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to read field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldList: + if f.ReadList == nil { + return totalN, fmt.Errorf("field '%s' has no list reader", f.Name) + } + n, err := f.ReadList(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldArrayStatic: + n, err := readStatic(r, f.Size(), f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to read field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldSubStruct: + fieldValue := f.Value() + sub, ok := fieldValue.(io.ReaderFrom) + if !ok { + return totalN, fmt.Errorf("field '%s' does not implement io.ReaderFrom", f.Name) + } + n, err := readSubStruct(r, sub) + if err != nil { + return totalN, fmt.Errorf("unable to read field '%s': %w", f.Name, err) + } + totalN += n + } + } + + return totalN, nil +} + +// WriteTo writes the fields from the provided Layout descriptor into 'w' +// in format defined in the document #575623. +func (Common) WriteTo(w io.Writer, p LayoutProvider) (int64, error) { + totalN := int64(0) + + for _, f := range p.Layout() { + switch f.Type { + case ManifestFieldEndValue: + n, err := writeStatic(w, f.Size(), f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to write field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldArrayDynamicWithSize: + n, err := writeArrayDynamic(w, false, f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to write field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldArrayDynamicWithPrefix: + n, err := writeArrayDynamic(w, true, f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to write field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldList: + if f.WriteList == nil { + return totalN, fmt.Errorf("field '%s' has no list writer", f.Name) + } + n, err := f.WriteList(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldArrayStatic: + n, err := writeStatic(w, f.Size(), f.Value()) + if err != nil { + return totalN, fmt.Errorf("unable to write field '%s': %w", f.Name, err) + } + totalN += n + case ManifestFieldSubStruct: + fieldValue := f.Value() + if fieldValue == nil { + continue + } + + sub, ok := fieldValue.(io.WriterTo) + if !ok { + return totalN, fmt.Errorf("field '%s' does not implement io.WriterTo", f.Name) + } + n, err := writeSubStruct(w, sub) + if err != nil { + return totalN, fmt.Errorf("unable to write field '%s': %w", f.Name, err) + } + totalN += n + } + } + + return totalN, nil + +} + +// We have 5 possible types of ManifestFieldType: +// endValue, arrayDynamic, arrayStatic, list and subStruct. +// Common.ReadFrom will distingush these (apart from list) and use the helpers. +func readStatic(r io.Reader, fieldSize uint64, fieldValue any) (int64, error) { + n, err := fieldSize, binary.Read(r, endianess, fieldValue) + if err != nil { + return 0, err + } + return int64(n), nil +} + +func readArrayDynamic(r io.Reader, size *uint16, out any) (int64, error) { + total := int64(0) + + if size == nil { + var n uint16 + if err := binary.Read(r, endianess, &n); err != nil { + return total, err + } + total += int64(binary.Size(n)) + size = &n + } + + dst, ok := out.(*[]byte) + if !ok { + return total, fmt.Errorf("arrayDynamic expects *[]byte, got %T", out) + } + + *dst = make([]byte, *size) + n := len(*dst) + if err := binary.Read(r, endianess, *dst); err != nil { + return total, err + } + total += int64(n) + + return total, nil +} + +func readSubStruct(r io.Reader, out io.ReaderFrom) (int64, error) { + n, err := out.ReadFrom(r) + if err != nil { + return 0, err + } + return n, nil +} + +func writeStatic(w io.Writer, fieldSize uint64, fieldValue any) (int64, error) { + n, err := fieldSize, binary.Write(w, endianess, fieldValue) + if err != nil { + return 0, err + } + return int64(n), nil +} + +func writeArrayDynamic(w io.Writer, withPrefix bool, out any) (int64, error) { + total := int64(0) + + src, ok := out.(*[]byte) + if !ok { + return total, fmt.Errorf("arrayDynamic expects *[]byte, got %T", out) + } + + if withPrefix { + size := uint16(len(*src)) + if err := binary.Write(w, endianess, size); err != nil { + return total, err + } + total += int64(binary.Size(size)) + } + + n := len(*src) + if err := binary.Write(w, endianess, *src); err != nil { + return total, err + } + total += int64(n) + + return total, nil +} + +func writeSubStruct(w io.Writer, out io.WriterTo) (int64, error) { + n, err := out.WriteTo(w) + if err != nil { + return 0, err + } + return n, nil +} + +// Okay this might seem bit hacky: we use dummy type that just +// implements LayoutProvider, and based on info value passes +// either full Layout or Layout - StructInfo. +// This is used a lot with BPM types. Not ideal, but +// spares lines of boilerplate code per type. +type DummyLayout struct { + Fields []LayoutField +} + +func (s DummyLayout) Layout() []LayoutField { + return s.Fields +} + +type StructInfo interface { + Structure + StructInfo() StructInfo +} + +func NewStructInfo(bgv BootGuardVersion) StructInfo { + switch bgv { + case Version10: + s := &StructInfoBG{} + return s + case Version20, Version21: + s := &StructInfoCBNT{} + return s + default: + return nil + } +} diff --git a/pkg/intel/metadata/cbnt/config.go b/pkg/intel/metadata/cbnt/config.go deleted file mode 100644 index 78db7115..00000000 --- a/pkg/intel/metadata/cbnt/config.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cbnt - -var ( - // StrictOrderCheck defines if elements order checks should be performed. - // For example in the Boot Policy Manifest elements could be in a wrong - // order. And we still can parse it, but in this way `*Offset` methods - // could be confusing, since they will show the offset as they will - // be written (not as they were parsed). - // - // We require a strict order because it is explicitly required - // in the documentation #575623: - // - // > The order of the elements and the order of the fields within each - // > element are architectural and must be followed. - StrictOrderCheck = true -) diff --git a/pkg/intel/metadata/cbnt/consts.go b/pkg/intel/metadata/cbnt/consts.go new file mode 100644 index 00000000..ae5d61f6 --- /dev/null +++ b/pkg/intel/metadata/cbnt/consts.go @@ -0,0 +1,91 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +const ( + // BasePhysAddr is the absolute physical address where the firmware image ends. + // + // See Figure 2.1 in https://www.intel.com/content/dam/www/public/us/en/documents/guides/fit-bios-specification.pdf + // + // Note: A firmware image grows towards lower addresses. So an image will be mapped to addresses: + // [ BasePhysAddr-length .. BasePhysAddr ) + // + // Note: SPI chip is mapped into this region. So we actually work directly with data of the SPI chip + // + // See also CalculatePhysAddrOfOffset(). + BasePhysAddr = 1 << 32 // "4GB" + + // FITPointerOffset is the offset of the physical address of the FIT pointer. + // See Section 3.1 in "Firmware Interface Table" specification: + // * https://www.intel.com/content/dam/www/public/us/en/documents/guides/fit-bios-specification.pdf + FITPointerOffset = 0x40 + + // FITPointerPhysAddr is the physical address of the FIT pointer. + // See "1 Firmware Interface Table" in "Firmware Interface Table" specification: + // * https://www.intel.com/content/dam/www/public/us/en/documents/guides/fit-bios-specification.pdf + FITPointerPhysAddr = BasePhysAddr - FITPointerOffset + + // FITPointerSize is the size of the FIT pointer. + // It is suggested to be 0x10 bytes because of "Figure 2-1" of the specification. + FITPointerSize = 0x10 + + // FITHeadersMagic is the magic string, expected in the beginning of the + // first FIT entry + FITHeadersMagic = "_FIT_ " + + Version10 BootGuardVersion = 1 + Version20 BootGuardVersion = 2 + Version21 BootGuardVersion = 3 + + // Supported algorithms, note that these are supported by both BG and CBnT, though + // BG uses onlt few of them + AlgUnknown Algorithm = 0x0000 + AlgRSA Algorithm = 0x0001 + AlgSHA1 Algorithm = 0x0004 + AlgSHA256 Algorithm = 0x000B + AlgSHA384 Algorithm = 0x000C + AlgSHA512 Algorithm = 0x000D + AlgNull Algorithm = 0x0010 + AlgSM3 Algorithm = 0x0012 + AlgRSASSA Algorithm = 0x0014 + AlgRSAPSS Algorithm = 0x0016 + AlgECDSA Algorithm = 0x0018 + AlgSM2 Algorithm = 0x001b + AlgECC Algorithm = 0x0023 + + // Possible values of TPM2PCRExtendPolicySupport + TPM2PCRExtendIllegal TPM2PCRExtendPolicySupport = 0 + TPM2PCRExtendMaximumAgilityPolicy TPM2PCRExtendPolicySupport = 1 + TPM2PCRExtendMaximumPerformancePolicy TPM2PCRExtendPolicySupport = 2 + TPM2PCRExtendBothPolicies TPM2PCRExtendPolicySupport = 3 + + // Possible types of Manifest fields. Refer to pkg/intel/metadata/README.md + // for detailed desription of the meaning and usage of these. + ManifestFieldEndValue ManifestFieldType = "endValue" + ManifestFieldArrayStatic ManifestFieldType = "arrayStatic" + ManifestFieldArrayDynamicWithSize ManifestFieldType = "arrayDynamicWithSize" + ManifestFieldArrayDynamicWithPrefix ManifestFieldType = "arrayDynamicWithPrefix" + ManifestFieldList ManifestFieldType = "list" + ManifestFieldSubStruct ManifestFieldType = "subStruct" + + // StructureIDManifest is the StructureID (in terms of + // the document #575623) of element 'Manifest'. + StructureIDManifest = "__KEYM__" +) + +var ( + // StrictOrderCheck defines if elements order checks should be performed. + // For example in the Boot Policy Manifest elements could be in a wrong + // order. And we still can parse it, but in this way `*Offset` methods + // could be confusing, since they will show the offset as they will + // be written (not as they were parsed). + // + // We require a strict order because it is explicitly required + // in the documentation #575623: + // + // > The order of the elements and the order of the fields within each + // > element are architectural and must be followed. + StrictOrderCheck = true +) diff --git a/pkg/intel/metadata/cbnt/crypto_routines.go b/pkg/intel/metadata/cbnt/crypto_routines.go index c97fd196..487502eb 100644 --- a/pkg/intel/metadata/cbnt/crypto_routines.go +++ b/pkg/intel/metadata/cbnt/crypto_routines.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate manifestcodegen - package cbnt import ( @@ -12,33 +10,16 @@ import ( _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" + "encoding/binary" "fmt" "hash" + "io" "strings" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" "github.com/tjfoc/gmsm/sm3" ) -// Algorithm represents a crypto algorithm value. -type Algorithm uint16 - -// Supported algorithms -const ( - AlgUnknown Algorithm = 0x0000 - AlgRSA Algorithm = 0x0001 - AlgSHA1 Algorithm = 0x0004 - AlgSHA256 Algorithm = 0x000B - AlgSHA384 Algorithm = 0x000C - AlgSHA512 Algorithm = 0x000D - AlgNull Algorithm = 0x0010 - AlgSM3 Algorithm = 0x0012 - AlgRSASSA Algorithm = 0x0014 - AlgRSAPSS Algorithm = 0x0016 - AlgECDSA Algorithm = 0x0018 - AlgSM2 Algorithm = 0x001b - AlgECC Algorithm = 0x0023 -) - var hashInfo = []struct { alg Algorithm hashFactory func() hash.Hash @@ -50,6 +31,8 @@ var hashInfo = []struct { {AlgSM3, sm3.New}, } +type Algorithm uint16 + // IsNull returns true if a is AlgNull or zero (unset). func (a Algorithm) IsNull() bool { return a == AlgNull || a == AlgUnknown @@ -86,7 +69,7 @@ func (a Algorithm) String() string { case AlgSHA512: _, err = s.WriteString("SHA512") case AlgSM3: - _, err = s.WriteString("SM3_256") + _, err = s.WriteString("SM3") case AlgNull: _, err = s.WriteString("AlgNull") case AlgRSASSA: @@ -139,3 +122,23 @@ func GetAlgFromString(name string) (Algorithm, error) { return AlgNull, fmt.Errorf("algorithm name provided unknown") } } + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (a Algorithm) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return a.String() +} + +// TotalSize returns the total size measured through binary.Size. +func (a Algorithm) TotalSize() uint64 { + return uint64(binary.Size(a)) +} + +// WriteTo writes the Algorithm into 'w' in binary format. +func (a Algorithm) WriteTo(w io.Writer) (int64, error) { + return int64(a.TotalSize()), binary.Write(w, binary.LittleEndian, a) +} + +// ReadFrom reads the Algorithm from 'r' in binary format. +func (a *Algorithm) ReadFrom(r io.Reader) (int64, error) { + return int64(a.TotalSize()), binary.Read(r, binary.LittleEndian, a) +} diff --git a/pkg/intel/metadata/cbnt/crypto_routines_manifestcodegen.go b/pkg/intel/metadata/cbnt/crypto_routines_manifestcodegen.go deleted file mode 100644 index 0a1aca6c..00000000 --- a/pkg/intel/metadata/cbnt/crypto_routines_manifestcodegen.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v Algorithm) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - return v.String() -} - -// TotalSize returns the total size measured through binary.Size. -func (v Algorithm) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the Algorithm into 'w' in binary format. -func (v Algorithm) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the Algorithm from 'r' in binary format. -func (v Algorithm) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/crypto_routines_test.go b/pkg/intel/metadata/cbnt/crypto_routines_test.go new file mode 100644 index 00000000..44f246a4 --- /dev/null +++ b/pkg/intel/metadata/cbnt/crypto_routines_test.go @@ -0,0 +1,197 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "strings" + "testing" +) + +func TestAlgorithmIsNull(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + alg Algorithm + want bool + }{ + "unknown": {alg: AlgUnknown, want: true}, + "null": {alg: AlgNull, want: true}, + "sha256": {alg: AlgSHA256, want: false}, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + if got := tt.alg.IsNull(); got != tt.want { + t.Errorf("Algorithm(%v).IsNull() = %v, want %v", tt.alg, got, tt.want) + } + }) + } +} + +func TestAlgorithmHash(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + alg Algorithm + wantSize int + wantErr bool + }{ + "sha1": {alg: AlgSHA1, wantSize: 20}, + "sha256": {alg: AlgSHA256, wantSize: 32}, + "sha384": {alg: AlgSHA384, wantSize: 48}, + "sha512": {alg: AlgSHA512, wantSize: 64}, + "sm3": {alg: AlgSM3, wantSize: 32}, + "non_hash": {alg: AlgRSA, wantErr: true}, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + h, err := tt.alg.Hash() + if tt.wantErr { + if err == nil { + t.Errorf("Algorithm(%v).Hash() error = nil, want non-nil", tt.alg) + } + return + } + + if err != nil { + t.Fatalf("Algorithm(%v).Hash() error = %v, want nil", tt.alg, err) + } + if got := h.Size(); got != tt.wantSize { + t.Errorf("Algorithm(%v).Hash().Size() = %d, want %d", tt.alg, got, tt.wantSize) + } + }) + } +} + +func TestAlgorithmString(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + alg Algorithm + want string + }{ + "unknown_alg": {alg: AlgUnknown, want: "AlgUnknown"}, + "rsa": {alg: AlgRSA, want: "RSA"}, + "sha1": {alg: AlgSHA1, want: "SHA1"}, + "sha256": {alg: AlgSHA256, want: "SHA256"}, + "sha384": {alg: AlgSHA384, want: "SHA384"}, + "sha512": {alg: AlgSHA512, want: "SHA512"}, + "sm3": {alg: AlgSM3, want: "SM3"}, + "alg_null": {alg: AlgNull, want: "AlgNull"}, + "rsassa": {alg: AlgRSASSA, want: "RSASSA"}, + "rsapss": {alg: AlgRSAPSS, want: "RSAPSS"}, + "ecdsa": {alg: AlgECDSA, want: "ECDSA"}, + "ecc": {alg: AlgECC, want: "ECC"}, + "sm2": {alg: AlgSM2, want: "SM2"}, + "fallback": {alg: Algorithm(0xFFFF), want: "Alg?<65535>"}, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + if got := tt.alg.String(); got != tt.want { + t.Errorf("Algorithm(%d).String() = %q, want %q", tt.alg, got, tt.want) + } + }) + } +} + +func TestGetAlgFromString(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + in string + want Algorithm + wantErr bool + }{ + "algunknown": {in: "AlgUnknown", want: AlgUnknown}, + "rsa": {in: "rsa", want: AlgRSA}, + "sha1": {in: "SHA1", want: AlgSHA1}, + "sha256": {in: "sha256", want: AlgSHA256}, + "sha384": {in: "SHA384", want: AlgSHA384}, + "sm3": {in: "sm3", want: AlgSM3}, + "algnull": {in: "AlgNull", want: AlgNull}, + "rsassa": {in: "RSASSA", want: AlgRSASSA}, + "rsapss": {in: "rsapss", want: AlgRSAPSS}, + "ecdsa": {in: "ECDSA", want: AlgECDSA}, + "ecc": {in: "ecc", want: AlgECC}, + "sm2": {in: "SM2", want: AlgSM2}, + "bad": {in: "invalid-algo", want: AlgNull, wantErr: true}, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := GetAlgFromString(tt.in) + if tt.wantErr { + if err == nil { + t.Errorf("GetAlgFromString(%q) error = nil, want non-nil", tt.in) + } + } else if err != nil { + t.Fatalf("GetAlgFromString(%q) error = %v, want nil", tt.in, err) + } + + if got != tt.want { + t.Errorf("GetAlgFromString(%q) = %v, want %v", tt.in, got, tt.want) + } + }) + } +} + +func TestAlgorithmPrettyStringTotalSizeAndRW(t *testing.T) { + t.Parallel() + + a := AlgSHA256 + if got, want := a.PrettyString(0, true), a.String(); got != want { + t.Errorf("Algorithm.PrettyString() = %q, want %q", got, want) + } + + if got := a.TotalSize(); got != 2 { + t.Errorf("Algorithm.TotalSize() = %d, want %d", got, 2) + } + + var buf bytes.Buffer + n, err := a.WriteTo(&buf) + if err != nil { + t.Fatalf("Algorithm.WriteTo() error = %v, want nil", err) + } + if n != int64(a.TotalSize()) { + t.Errorf("Algorithm.WriteTo() bytes = %d, want %d", n, a.TotalSize()) + } + + var got Algorithm + n, err = (&got).ReadFrom(&buf) + if err != nil { + t.Fatalf("Algorithm.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("Algorithm.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + if got != a { + t.Errorf("Algorithm round-trip = %v, want %v", got, a) + } + + _, err = (&got).ReadFrom(bytes.NewBuffer([]byte{0x01})) + if err == nil { + t.Errorf("Algorithm.ReadFrom(short input) error = nil, want non-nil") + } else if !strings.Contains(err.Error(), "unexpected EOF") { + t.Errorf("Algorithm.ReadFrom(short input) error = %q, want to contain %q", err.Error(), "unexpected EOF") + } +} diff --git a/pkg/intel/metadata/cbnt/hash.go b/pkg/intel/metadata/cbnt/hash.go index c2f9bc83..b6ebd94e 100644 --- a/pkg/intel/metadata/cbnt/hash.go +++ b/pkg/intel/metadata/cbnt/hash.go @@ -2,18 +2,371 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate manifestcodegen - package cbnt -// HashStructure describes a digest. -type HashStructure struct { - HashAlg Algorithm `default:"0x10" json:"hsAlg"` - HashBuffer []byte `json:"hsBuffer"` -} +import ( + "encoding/binary" + "fmt" + "io" + "strings" + + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) // HashList describes multiple digests type HashList struct { + Common Size uint16 `rehashValue:"TotalSize()" json:"hlSize"` List []HashStructure `json:"hlList"` } + +// NewHashList returns a new instance of HashList with +// all default values set. +func NewHashList() *HashList { + s := &HashList{} + s.Rehash() + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *HashList) Validate() error { + expectedValue := uint16(s.Common.TotalSize(s)) + if s.Size != expectedValue { + return fmt.Errorf("field 'Size' expects write-value '%v', but has %v", expectedValue, s.Size) + } + + return nil +} + +// ReadFrom reads the HashList from 'r' in format defined in the document #575623. +func (s *HashList) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// RehashRecursive calls Rehash (see below) recursively. +func (s *HashList) RehashRecursive() { + s.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (s *HashList) Rehash() { + s.Size = uint16(s.Common.TotalSize(s)) +} + +// WriteTo writes the HashList into 'w' in format defined in +// the document #575623. +func (s *HashList) WriteTo(w io.Writer) (int64, error) { + s.Rehash() + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s *HashList) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "Size", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.Size }, + Type: ManifestFieldEndValue, + }, + { + ID: 1, + Name: fmt.Sprintf("List: Array of \"Hash List\" of length %d", len(s.List)), + Size: func() uint64 { + size := uint64(binary.Size(uint16(0))) + for idx := range s.List { + size += s.List[idx].Common.TotalSize(&s.List[idx]) + } + return size + }, + Value: func() any { return &s.List }, + Type: ManifestFieldList, + // this is basically the logic from ReadFrom of HashList + // for the ManifestFieldType list. Just that now we pass it + // as closure and let generic ReadFrom make use of it. + ReadList: func(r io.Reader) (int64, error) { + var count uint16 + if err := binary.Read(r, endianess, &count); err != nil { + return 0, fmt.Errorf("unable to read the count for field 'List': %w", err) + } + totalN := int64(binary.Size(count)) + + s.List = make([]HashStructure, count) + for idx := range s.List { + n, err := s.List[idx].ReadFrom(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field 'List[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + WriteList: func(w io.Writer) (int64, error) { + count := uint16(len(s.List)) + if err := binary.Write(w, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to write the count for field 'List': %w", err) + } + totalN := int64(binary.Size(count)) + + for idx := range s.List { + n, err := s.List[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'List[%d]': %w", idx, err) + } + totalN += int64(n) + } + + return totalN, nil + }, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *HashList) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + // normally it would be 0, but ret is already 0 if we land here + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *HashList) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the HashList. +func (s *HashList) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *HashList) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + base := Common{}.PrettyString(depth, withHeader, s, "Hash List", opts...) + var lines []string + lines = append(lines, base) + + lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("List: Array of \"Hash List\" of length %d", len(s.List)), s.List)) + for i := 0; i < len(s.List); i++ { + lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.List[i].PrettyString(depth+2, true, opts...))) + } + + if depth < 1 { + lines = append(lines, "") + } + + return strings.Join(lines, "\n") +} + +type HashStructure struct { + Common + HashAlg Algorithm `default:"0x10" json:"hsAlg"` + HashBuffer []byte `json:"hsBuffer"` +} + +// NewHashStructure returns a new instance of HashStructure with +// all default values set. +func NewHashStructure(alg Algorithm) *HashStructure { + s := &HashStructure{} + // For bg pkg, the default one + s.HashAlg = alg + return s +} + +// ReadFrom reads the HashStructure from 'r' in format defined in the document #575623. +func (s *HashStructure) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// WriteTo writes the HashStructure into 'w' in format defined in +// the document #575623. +func (s *HashStructure) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s *HashStructure) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "Hash Alg", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.HashAlg }, + Type: ManifestFieldEndValue, + }, + { + ID: 1, + Name: "Hash Buffer", + Size: func() uint64 { + h, err := s.HashAlg.Hash() + if err != nil { + return uint64(binary.Size(uint16(0))) + } + return uint64(binary.Size(uint16(0))) + uint64(h.Size()) + }, + Value: func() any { return &s.HashBuffer }, + Type: ManifestFieldArrayDynamicWithPrefix, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *HashStructure) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + // normally it would be 0, but ret is already 0 if we land here + return ret, fmt.Errorf("HashStructure: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *HashStructure) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashStructure: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the manifest +func (s *HashStructure) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *HashStructure) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, s, "Hash Structure", opts...) +} + +// HashStructureFill describes a digest in BG fill format. +// Unlike HashStructure, HashBuffer does not carry a size prefix on the wire. +type HashStructureFill struct { + Common + HashAlg Algorithm `default:"0x0b" json:"hsAlg"` + HashBuffer []byte `json:"hsBuffer"` +} + +// NewHashStructureFill returns a new instance of HashStructureFill with +// all default values set. Note: here only for legacy reasons (i.e. supporting +// BG 1.0). +func NewHashStructureFill(alg Algorithm) *HashStructureFill { + s := &HashStructureFill{} + s.HashAlg = alg + return s +} + +// this little hack here mimics the hack from old bg package +func (s *HashStructureFill) hashBufferSize() uint64 { + const hashSizeFieldLen = 2 + if s.HashAlg.IsNull() { + return 32 + hashSizeFieldLen + } + + h, err := s.HashAlg.Hash() + if err != nil { + return hashSizeFieldLen + } + return uint64((h.Size() + hashSizeFieldLen)) +} + +// ReadFrom reads the HashStructureFill from 'r' in format defined in the document #575623. +func (s *HashStructureFill) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// WriteTo writes the HashStructureFill into 'w' in format defined in +// the document #575623. +func (s *HashStructureFill) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s *HashStructureFill) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "Hash Alg", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.HashAlg }, + Type: ManifestFieldEndValue, + }, + { + ID: 1, + Name: "Hash Buffer", + Size: s.hashBufferSize, + Value: func() any { return &s.HashBuffer }, + Type: ManifestFieldArrayDynamicWithSize, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *HashStructureFill) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashStructureFill: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *HashStructureFill) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashStructureFill: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the manifest +func (s *HashStructureFill) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *HashStructureFill) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, s, "Hash Structure Fill", opts...) +} diff --git a/pkg/intel/metadata/cbnt/hash_list_test.go b/pkg/intel/metadata/cbnt/hash_list_test.go new file mode 100644 index 00000000..972a76c5 --- /dev/null +++ b/pkg/intel/metadata/cbnt/hash_list_test.go @@ -0,0 +1,147 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "strings" + "testing" +) + +func TestHashListNew(t *testing.T) { + t.Parallel() + + h := NewHashList() + if h == nil { + t.Fatal("NewHashList() = nil, want non-nil") + } + if h.Size != 4 { + t.Errorf("NewHashList().Size = %d, want %d", h.Size, 4) + } + if err := h.Validate(); err != nil { + t.Errorf("NewHashList().Validate() error = %v, want nil", err) + } +} + +func TestHashListValidateRejectsInvalidSize(t *testing.T) { + t.Parallel() + + h := NewHashList() + h.Size = 0 + + if err := h.Validate(); err == nil { + t.Errorf("HashList.Validate() error = nil, want non-nil") + } +} + +func TestHashListRehashRecursive(t *testing.T) { + t.Parallel() + + h := &HashList{ + List: []HashStructure{{ + HashAlg: AlgSHA256, + HashBuffer: bytes.Repeat([]byte{0xA5}, 32), + }}, + } + h.RehashRecursive() + + wantSize := uint16(h.TotalSize()) + if h.Size != wantSize { + t.Errorf("HashList.RehashRecursive() Size = %d, want %d", h.Size, wantSize) + } +} + +func TestHashListReadWriteRoundTrip(t *testing.T) { + t.Parallel() + + want := &HashList{ + List: []HashStructure{{ + HashAlg: AlgSHA256, + HashBuffer: bytes.Repeat([]byte{0x5A}, 32), + }}, + } + want.Rehash() + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("HashList.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("HashList.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got HashList + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("HashList.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("HashList.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got.Size != want.Size { + t.Errorf("HashList round-trip Size = %d, want %d", got.Size, want.Size) + } + if len(got.List) != len(want.List) { + t.Fatalf("len(HashList round-trip List) = %d, want %d", len(got.List), len(want.List)) + } + if got.List[0].HashAlg != want.List[0].HashAlg { + t.Errorf("HashList round-trip List[0].HashAlg = %v, want %v", got.List[0].HashAlg, want.List[0].HashAlg) + } + if !bytes.Equal(got.List[0].HashBuffer, want.List[0].HashBuffer) { + t.Errorf("HashList round-trip List[0].HashBuffer = %v, want %v", got.List[0].HashBuffer, want.List[0].HashBuffer) + } +} + +func TestHashListSizeOffsetAndTotal(t *testing.T) { + t.Parallel() + + h := &HashList{ + List: []HashStructure{{ + HashAlg: AlgSHA256, + HashBuffer: bytes.Repeat([]byte{0xCC}, 32), + }}, + } + h.Rehash() + + size0, err := h.SizeOf(0) + if err != nil { + t.Fatalf("HashList.SizeOf(0) error = %v, want nil", err) + } + if size0 != 2 { + t.Errorf("HashList.SizeOf(0) = %d, want %d", size0, 2) + } + + offset1, err := h.OffsetOf(1) + if err != nil { + t.Fatalf("HashList.OffsetOf(1) error = %v, want nil", err) + } + if offset1 != 2 { + t.Errorf("HashList.OffsetOf(1) = %d, want %d", offset1, 2) + } + + if _, err := h.SizeOf(99); err == nil { + t.Errorf("HashList.SizeOf(99) error = nil, want non-nil") + } + if _, err := h.OffsetOf(99); err == nil { + t.Errorf("HashList.OffsetOf(99) error = nil, want non-nil") + } + + var nilList *HashList + if got := nilList.TotalSize(); got != 0 { + t.Errorf("(*HashList)(nil).TotalSize() = %d, want %d", got, 0) + } +} + +func TestHashListPrettyString(t *testing.T) { + t.Parallel() + + h := NewHashList() + got := h.PrettyString(0, true) + if !strings.Contains(got, "Hash List") { + t.Errorf("HashList.PrettyString() = %q, want to contain %q", got, "Hash List") + } +} diff --git a/pkg/intel/metadata/cbnt/hash_manifestcodegen.go b/pkg/intel/metadata/cbnt/hash_manifestcodegen.go deleted file mode 100644 index 8cee6b3a..00000000 --- a/pkg/intel/metadata/cbnt/hash_manifestcodegen.go +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewHashList returns a new instance of HashList with -// all default values set. -func NewHashList() *HashList { - s := &HashList{} - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *HashList) Validate() error { - // See tag "rehashValue" - { - expectedValue := uint16(s.TotalSize()) - if s.Size != expectedValue { - return fmt.Errorf("field 'Size' expects write-value '%v', but has %v", expectedValue, s.Size) - } - } - - return nil -} - -// ReadFrom reads the HashList from 'r' in format defined in the document #575623. -func (s *HashList) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Size (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.Size) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Size': %w", err) - } - totalN += int64(n) - } - - // List (ManifestFieldType: list) - { - var count uint16 - err := binary.Read(r, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to read the count for field 'List': %w", err) - } - totalN += int64(binary.Size(count)) - s.List = make([]HashStructure, count) - - for idx := range s.List { - n, err := s.List[idx].ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'List[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *HashList) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *HashList) Rehash() { - s.Size = uint16(s.TotalSize()) -} - -// WriteTo writes the HashList into 'w' in format defined in -// the document #575623. -func (s *HashList) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Size (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.Size) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Size': %w", err) - } - totalN += int64(n) - } - - // List (ManifestFieldType: list) - { - count := uint16(len(s.List)) - err := binary.Write(w, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to write the count for field 'List': %w", err) - } - totalN += int64(binary.Size(count)) - for idx := range s.List { - n, err := s.List[idx].WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'List[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// SizeSize returns the size in bytes of the value of field Size -func (s *HashList) SizeTotalSize() uint64 { - return 2 -} - -// ListSize returns the size in bytes of the value of field List -func (s *HashList) ListTotalSize() uint64 { - var size uint64 - size += uint64(binary.Size(uint16(0))) - for idx := range s.List { - size += s.List[idx].TotalSize() - } - return size -} - -// SizeOffset returns the offset in bytes of field Size -func (s *HashList) SizeOffset() uint64 { - return 0 -} - -// ListOffset returns the offset in bytes of field List -func (s *HashList) ListOffset() uint64 { - return s.SizeOffset() + s.SizeTotalSize() -} - -// Size returns the total size of the HashList. -func (s *HashList) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.SizeTotalSize() - size += s.ListTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *HashList) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Hash List", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Size", "", &s.Size, opts...)...) - // ManifestFieldType is list - lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("List: Array of \"Hash List\" of length %d", len(s.List)), s.List)) - for i := 0; i < len(s.List); i++ { - lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.List[i].PrettyString(depth+2, true))) - } - if depth < 1 { - lines = append(lines, "") - } - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// NewHashStructure returns a new instance of HashStructure with -// all default values set. -func NewHashStructure() *HashStructure { - s := &HashStructure{} - // Set through tag "default": - s.HashAlg = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *HashStructure) Validate() error { - - return nil -} - -// ReadFrom reads the HashStructure from 'r' in format defined in the document #575623. -func (s *HashStructure) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // HashBuffer (ManifestFieldType: arrayDynamic) - { - var size uint16 - err := binary.Read(r, binary.LittleEndian, &size) - if err != nil { - return totalN, fmt.Errorf("unable to the read size of field 'HashBuffer': %w", err) - } - totalN += int64(binary.Size(size)) - s.HashBuffer = make([]byte, size) - n, err := len(s.HashBuffer), binary.Read(r, binary.LittleEndian, s.HashBuffer) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashBuffer': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *HashStructure) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *HashStructure) Rehash() { -} - -// WriteTo writes the HashStructure into 'w' in format defined in -// the document #575623. -func (s *HashStructure) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // HashBuffer (ManifestFieldType: arrayDynamic) - { - size := uint16(len(s.HashBuffer)) - err := binary.Write(w, binary.LittleEndian, size) - if err != nil { - return totalN, fmt.Errorf("unable to write the size of field 'HashBuffer': %w", err) - } - totalN += int64(binary.Size(size)) - n, err := len(s.HashBuffer), binary.Write(w, binary.LittleEndian, s.HashBuffer) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashBuffer': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// HashAlgSize returns the size in bytes of the value of field HashAlg -func (s *HashStructure) HashAlgTotalSize() uint64 { - return 2 -} - -// HashBufferSize returns the size in bytes of the value of field HashBuffer -func (s *HashStructure) HashBufferTotalSize() uint64 { - size := uint64(binary.Size(uint16(0))) - size += uint64(len(s.HashBuffer)) - return size -} - -// HashAlgOffset returns the offset in bytes of field HashAlg -func (s *HashStructure) HashAlgOffset() uint64 { - return 0 -} - -// HashBufferOffset returns the offset in bytes of field HashBuffer -func (s *HashStructure) HashBufferOffset() uint64 { - return s.HashAlgOffset() + s.HashAlgTotalSize() -} - -// Size returns the total size of the HashStructure. -func (s *HashStructure) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.HashAlgTotalSize() - size += s.HashBufferTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *HashStructure) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Hash Structure", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Hash Alg", "", &s.HashAlg, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Hash Buffer", "", &s.HashBuffer, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/hash_structure_fill_test.go b/pkg/intel/metadata/cbnt/hash_structure_fill_test.go new file mode 100644 index 00000000..b0bc8b99 --- /dev/null +++ b/pkg/intel/metadata/cbnt/hash_structure_fill_test.go @@ -0,0 +1,137 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "strings" + "testing" +) + +func TestHashStructureFillNew(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + alg Algorithm + }{ + "sha384": {alg: AlgSHA384}, + "null": {alg: AlgNull}, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + h := NewHashStructureFill(tt.alg) + if h == nil { + t.Fatal("NewHashStructureFill() = nil, want non-nil") + } + if h.HashAlg != tt.alg { + t.Errorf("NewHashStructureFill(%v).HashAlg = %v, want %v", tt.alg, h.HashAlg, tt.alg) + } + }) + } +} + +func TestHashStructureFillHashBufferSizeByAlgorithm(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + alg Algorithm + wantSize uint64 + }{ + "sha256": {alg: AlgSHA256, wantSize: 34}, + "null": {alg: AlgNull, wantSize: 34}, + "unknown": { + alg: AlgRSA, + wantSize: 2, + }, + } + + for name, tt := range tests { + name := name + tt := tt + + t.Run(name, func(t *testing.T) { + t.Parallel() + + h := &HashStructureFill{HashAlg: tt.alg} + size, err := h.SizeOf(1) + if err != nil { + t.Fatalf("HashStructureFill.SizeOf(1) error = %v, want nil", err) + } + if size != tt.wantSize { + t.Errorf("HashStructureFill.SizeOf(1) with alg %v = %d, want %d", tt.alg, size, tt.wantSize) + } + }) + } +} + +func TestHashStructureFillReadWriteRoundTrip(t *testing.T) { + t.Parallel() + + want := &HashStructureFill{ + HashAlg: AlgSHA256, + HashBuffer: bytes.Repeat([]byte{0x33}, 34), + } + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("HashStructureFill.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("HashStructureFill.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got HashStructureFill + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("HashStructureFill.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("HashStructureFill.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got.HashAlg != want.HashAlg { + t.Errorf("HashStructureFill round-trip HashAlg = %v, want %v", got.HashAlg, want.HashAlg) + } + if !bytes.Equal(got.HashBuffer, want.HashBuffer) { + t.Errorf("HashStructureFill round-trip HashBuffer = %v, want %v", got.HashBuffer, want.HashBuffer) + } +} + +func TestHashStructureFillMethods(t *testing.T) { + t.Parallel() + + h := &HashStructureFill{HashAlg: AlgSHA256, HashBuffer: bytes.Repeat([]byte{0x10}, 34)} + + offset1, err := h.OffsetOf(1) + if err != nil { + t.Fatalf("HashStructureFill.OffsetOf(1) error = %v, want nil", err) + } + if offset1 != 2 { + t.Errorf("HashStructureFill.OffsetOf(1) = %d, want %d", offset1, 2) + } + + if _, err := h.SizeOf(99); err == nil { + t.Errorf("HashStructureFill.SizeOf(99) error = nil, want non-nil") + } + if _, err := h.OffsetOf(99); err == nil { + t.Errorf("HashStructureFill.OffsetOf(99) error = nil, want non-nil") + } + + var nilHS *HashStructureFill + if got := nilHS.TotalSize(); got != 0 { + t.Errorf("(*HashStructureFill)(nil).TotalSize() = %d, want %d", got, 0) + } + + pretty := h.PrettyString(0, true) + if !strings.Contains(pretty, "Hash Structure Fill") { + t.Errorf("HashStructureFill.PrettyString() = %q, want to contain %q", pretty, "Hash Structure Fill") + } +} diff --git a/pkg/intel/metadata/cbnt/hash_structure_test.go b/pkg/intel/metadata/cbnt/hash_structure_test.go new file mode 100644 index 00000000..9b7a73a7 --- /dev/null +++ b/pkg/intel/metadata/cbnt/hash_structure_test.go @@ -0,0 +1,123 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "strings" + "testing" +) + +func TestHashStructureNew(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + alg Algorithm + }{ + "sha256": {alg: AlgSHA256}, + "null": {alg: AlgNull}, + } + + for name, tt := range tests { + name := name + tt := tt + + t.Run(name, func(t *testing.T) { + t.Parallel() + + h := NewHashStructure(tt.alg) + if h == nil { + t.Fatal("NewHashStructure() = nil, want non-nil") + } + if h.HashAlg != tt.alg { + t.Errorf("NewHashStructure(%v).HashAlg = %v, want %v", tt.alg, h.HashAlg, tt.alg) + } + }) + } +} + +func TestHashStructureReadWriteRoundTrip(t *testing.T) { + t.Parallel() + + want := &HashStructure{ + HashAlg: AlgSHA256, + HashBuffer: bytes.Repeat([]byte{0x7C}, 32), + } + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("HashStructure.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("HashStructure.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got HashStructure + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("HashStructure.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("HashStructure.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got.HashAlg != want.HashAlg { + t.Errorf("HashStructure round-trip HashAlg = %v, want %v", got.HashAlg, want.HashAlg) + } + if !bytes.Equal(got.HashBuffer, want.HashBuffer) { + t.Errorf("HashStructure round-trip HashBuffer = %v, want %v", got.HashBuffer, want.HashBuffer) + } +} + +func TestHashStructureMethods(t *testing.T) { + t.Parallel() + + h := &HashStructure{ + HashAlg: AlgSHA256, + HashBuffer: bytes.Repeat([]byte{0x22}, 32), + } + + size0, err := h.SizeOf(0) + if err != nil { + t.Fatalf("HashStructure.SizeOf(0) error = %v, want nil", err) + } + if size0 != 2 { + t.Errorf("HashStructure.SizeOf(0) = %d, want %d", size0, 2) + } + + size1, err := h.SizeOf(1) + if err != nil { + t.Fatalf("HashStructure.SizeOf(1) error = %v, want nil", err) + } + if size1 != 34 { + t.Errorf("HashStructure.SizeOf(1) = %d, want %d", size1, 34) + } + + offset1, err := h.OffsetOf(1) + if err != nil { + t.Fatalf("HashStructure.OffsetOf(1) error = %v, want nil", err) + } + if offset1 != 2 { + t.Errorf("HashStructure.OffsetOf(1) = %d, want %d", offset1, 2) + } + + if _, err := h.SizeOf(99); err == nil { + t.Errorf("HashStructure.SizeOf(99) error = nil, want non-nil") + } + if _, err := h.OffsetOf(99); err == nil { + t.Errorf("HashStructure.OffsetOf(99) error = nil, want non-nil") + } + + var nilHS *HashStructure + if got := nilHS.TotalSize(); got != 0 { + t.Errorf("(*HashStructure)(nil).TotalSize() = %d, want %d", got, 0) + } + + pretty := h.PrettyString(0, true) + if !strings.Contains(pretty, "Hash Structure") { + t.Errorf("HashStructure.PrettyString() = %q, want to contain %q", pretty, "Hash Structure") + } +} diff --git a/pkg/intel/metadata/cbnt/key.go b/pkg/intel/metadata/cbnt/key.go index 4b700592..f3fcacf1 100644 --- a/pkg/intel/metadata/cbnt/key.go +++ b/pkg/intel/metadata/cbnt/key.go @@ -14,42 +14,22 @@ import ( "crypto/rsa" "encoding/binary" "fmt" + "io" "math/big" + "strings" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" "github.com/tjfoc/gmsm/sm2" ) -// Key is a public key of an asymmetric crypto keypair. type Key struct { + Common KeyAlg Algorithm `json:"keyAlg"` Version uint8 `require:"0x10" json:"keyVersion"` KeySize BitSize `json:"keyBitsize"` Data []byte `countValue:"keyDataSize()" json:"keyData"` } -// BitSize is a size in bits. -type BitSize uint16 - -// InBits returns the size in bits. -func (ks BitSize) InBits() uint16 { - return uint16(ks) -} - -// InBytes returns the size in bytes. -func (ks BitSize) InBytes() uint16 { - return uint16(ks >> 3) -} - -// SetInBits sets the size in bits. -func (ks *BitSize) SetInBits(amountOfBits uint16) { - *ks = BitSize(amountOfBits) -} - -// SetInBytes sets the size in bytes. -func (ks *BitSize) SetInBytes(amountOfBytes uint16) { - *ks = BitSize(amountOfBytes << 3) -} - // keyDataSize returns the expected length of Data for specified // KeyAlg and KeySize. func (k Key) keyDataSize() int64 { @@ -76,7 +56,7 @@ func (k Key) PubKey() (crypto.PublicKey, error) { case AlgRSA: result := &rsa.PublicKey{ N: new(big.Int).SetBytes(reverseBytes(k.Data[4:])), - E: int(binaryOrder.Uint32(k.Data)), + E: int(endianess.Uint32(k.Data)), } return result, nil case AlgECC: @@ -112,7 +92,7 @@ func (k *Key) SetPubKey(key crypto.PublicKey) error { n := key.N.Bytes() k.KeySize.SetInBytes(uint16(len(n))) k.Data = make([]byte, 4+len(n)) - binaryOrder.PutUint32(k.Data, uint32(key.E)) + endianess.PutUint32(k.Data, uint32(key.E)) copy(k.Data[4:], reverseBytes(n)) return nil @@ -164,7 +144,8 @@ func (k *Key) PrintBPMPubKey(bpmAlg Algorithm) error { if err != nil { return err } - if k.KeyAlg == AlgRSA { + switch k.KeyAlg { + case AlgRSA: if err := binary.Write(buf, binary.LittleEndian, k.Data[4:]); err != nil { return err } @@ -172,7 +153,7 @@ func (k *Key) PrintBPMPubKey(bpmAlg Algorithm) error { return fmt.Errorf("unable to hash: %w", err) } fmt.Printf(" Boot Policy Manifest Pubkey Hash: 0x%x\n", hash.Sum(nil)) - } else if k.KeyAlg == AlgSM2 || k.KeyAlg == AlgECC { + case AlgSM2, AlgECC: if err := binary.Write(buf, binary.LittleEndian, k.Data); err != nil { return err } @@ -180,7 +161,7 @@ func (k *Key) PrintBPMPubKey(bpmAlg Algorithm) error { return fmt.Errorf("unable to hash: %w", err) } fmt.Printf(" Boot Policy Manifest Pubkey Hash: 0x%x\n", hash.Sum(nil)) - } else { + default: fmt.Printf(" Boot Policy Manifest Pubkey Hash: Unknown Algorithm\n") } } else { @@ -228,3 +209,156 @@ func (k *Key) PrintKMPubKey(kmAlg Algorithm) error { return nil } + +// NewKey returns a new instance of Key with +// all default values set. +func NewKey() *Key { + s := &Key{} + // Set through tag "required": + s.Version = 0x10 + return s +} + +// Layout returns the structure's layout descriptor +func (k *Key) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "Key Alg", + Size: func() uint64 { return 2 }, + Value: func() any { return &k.KeyAlg }, + Type: ManifestFieldEndValue, + }, + { + ID: 1, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &k.Version }, + Type: ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Key Size", + Size: func() uint64 { return 2 }, + Value: func() any { return &k.KeySize }, + Type: ManifestFieldEndValue, + }, + { + ID: 3, + Name: "Data", + Size: func() uint64 { return uint64(k.keyDataSize()) }, + Value: func() any { return &k.Data }, + Type: ManifestFieldArrayDynamicWithSize, + }, + } +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (k *Key) Validate() error { + // See tag "require" + if k.Version != 0x10 { + return fmt.Errorf("field 'Version' expects value '0x10', but has %v", k.Version) + } + + return nil +} + +// ReadFrom reads the Key from 'r' in format defined in the document #575623. +func (k *Key) ReadFrom(r io.Reader) (int64, error) { + totalN, err := k.Common.ReadFrom(r, k) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// WriteTo writes the Key into 'w' in format defined in +// the document #575623. +func (k *Key) WriteTo(w io.Writer) (int64, error) { + return k.Common.WriteTo(w, k) +} + +// SizeOf returns the size of the structure's field of a given id. +func (k *Key) SizeOf(id int) (uint64, error) { + ret, err := k.Common.SizeOf(k, id) + if err != nil { + // normally it would be 0, but ret is already 0 if we land here + return ret, fmt.Errorf("Key: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (k *Key) OffsetOf(id int) (uint64, error) { + ret, err := k.Common.OffsetOf(k, id) + if err != nil { + return ret, fmt.Errorf("Key: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the Key. +func (k *Key) TotalSize() uint64 { + if k == nil { + return 0 + } + + return k.Common.TotalSize(k) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (k *Key) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, k, "Key", opts...) +} + +type BitSize uint16 + +// InBits returns the size in bits. +func (ks BitSize) InBits() uint16 { + return uint16(ks) +} + +// InBytes returns the size in bytes. +func (ks BitSize) InBytes() uint16 { + return uint16(ks >> 3) +} + +// SetInBits sets the size in bits. +func (ks *BitSize) SetInBits(amountOfBits uint16) { + *ks = BitSize(amountOfBits) +} + +// SetInBytes sets the size in bytes. +func (ks *BitSize) SetInBytes(amountOfBytes uint16) { + *ks = BitSize(amountOfBytes << 3) +} + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (ks BitSize) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "Bit Size", ks)) + } + lines = append(lines, pretty.SubValue(depth+1, "In Bits", "", ks.InBits(), opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", ks.InBytes(), opts...)...) + return strings.Join(lines, "\n") +} + +// TotalSize returns the total size measured through binary.Size. +func (ks BitSize) TotalSize() uint64 { + return uint64(binary.Size(ks)) +} + +// WriteTo writes the BitSize into 'w' in binary format. +func (ks BitSize) WriteTo(w io.Writer) (int64, error) { + return int64(ks.TotalSize()), binary.Write(w, binary.LittleEndian, ks) +} + +// ReadFrom reads the BitSize from 'r' in binary format. +func (ks BitSize) ReadFrom(r io.Reader) (int64, error) { + return int64(ks.TotalSize()), binary.Read(r, binary.LittleEndian, ks) +} diff --git a/pkg/intel/metadata/cbnt/key_manifestcodegen.go b/pkg/intel/metadata/cbnt/key_manifestcodegen.go deleted file mode 100644 index 21a85c0f..00000000 --- a/pkg/intel/metadata/cbnt/key_manifestcodegen.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewKey returns a new instance of Key with -// all default values set. -func NewKey() *Key { - s := &Key{} - // Set through tag "required": - s.Version = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Key) Validate() error { - // See tag "require" - if s.Version != 0x10 { - return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) - } - - return nil -} - -// ReadFrom reads the Key from 'r' in format defined in the document #575623. -func (s *Key) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // KeyAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeyAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeyAlg': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - size := uint16(s.keyDataSize()) - s.Data = make([]byte, size) - n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Key) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Key) Rehash() { -} - -// WriteTo writes the Key into 'w' in format defined in -// the document #575623. -func (s *Key) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // KeyAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeyAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeyAlg': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// KeyAlgSize returns the size in bytes of the value of field KeyAlg -func (s *Key) KeyAlgTotalSize() uint64 { - return 2 -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *Key) VersionTotalSize() uint64 { - return 1 -} - -// KeySizeSize returns the size in bytes of the value of field KeySize -func (s *Key) KeySizeTotalSize() uint64 { - return 2 -} - -// DataSize returns the size in bytes of the value of field Data -func (s *Key) DataTotalSize() uint64 { - return uint64(len(s.Data)) -} - -// KeyAlgOffset returns the offset in bytes of field KeyAlg -func (s *Key) KeyAlgOffset() uint64 { - return 0 -} - -// VersionOffset returns the offset in bytes of field Version -func (s *Key) VersionOffset() uint64 { - return s.KeyAlgOffset() + s.KeyAlgTotalSize() -} - -// KeySizeOffset returns the offset in bytes of field KeySize -func (s *Key) KeySizeOffset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// DataOffset returns the offset in bytes of field Data -func (s *Key) DataOffset() uint64 { - return s.KeySizeOffset() + s.KeySizeTotalSize() -} - -// Size returns the total size of the Key. -func (s *Key) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.KeyAlgTotalSize() - size += s.VersionTotalSize() - size += s.KeySizeTotalSize() - size += s.DataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Key) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Key", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Alg", "", &s.KeyAlg, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Size", "", &s.KeySize, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v BitSize) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Bit Size", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "In Bits", "", v.InBits(), opts...)...) - lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", v.InBytes(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v BitSize) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the BitSize into 'w' in binary format. -func (v BitSize) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the BitSize from 'r' in binary format. -func (v BitSize) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/key_signature.go b/pkg/intel/metadata/cbnt/key_signature.go index baf4967b..8a643647 100644 --- a/pkg/intel/metadata/cbnt/key_signature.go +++ b/pkg/intel/metadata/cbnt/key_signature.go @@ -1,18 +1,19 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate manifestcodegen - package cbnt import ( "crypto" "fmt" + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" ) -// KeySignature combines a public key and a signature in a single structure. type KeySignature struct { + Common Version uint8 `require:"0x10" json:"ksVersion,omitempty"` Key Key `json:"ksKey"` Signature Signature `json:"ksSignature"` @@ -79,3 +80,107 @@ func (s *KeySignature) FillSignature(signAlgo Algorithm, pubKey crypto.PublicKey return s.Signature.FillSignature(signAlgo, pubKey, signedData, hashAlgo) } + +// NewKeySignature returns a new instance of KeySignature with +// all default values set. +func NewKeySignature() *KeySignature { + s := &KeySignature{} + // Set through tag "required": + s.Version = 0x10 + // Recursively initializing a child structure: + s.Key = *NewKey() + // Recursively initializing a child structure: + s.Signature = *NewSignature() + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *KeySignature) Validate() error { + // See tag "require" + if s.Version != 0x10 { + return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) + } + // Recursively validating a child structure: + if err := s.Key.Validate(); err != nil { + return fmt.Errorf("error on field 'Key': %w", err) + } + // Recursively validating a child structure: + if err := s.Signature.Validate(); err != nil { + return fmt.Errorf("error on field 'Signature': %w", err) + } + + return nil +} + +// ReadFrom reads the KeySignature from 'r' in format defined in the document #575623. +func (s *KeySignature) ReadFrom(r io.Reader) (int64, error) { + return s.Common.ReadFrom(r, s) +} + +// WriteTo writes the KeySignature into 'w' in format defined in +// the document #575623. +func (s *KeySignature) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s *KeySignature) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Version }, + Type: ManifestFieldEndValue, + }, + { + ID: 1, + Name: "Key", + Size: func() uint64 { return s.Key.Common.TotalSize(&s.Key) }, + Value: func() any { return &s.Key }, + Type: ManifestFieldSubStruct, + }, + { + ID: 2, + Name: "Signature", + Size: func() uint64 { return s.Signature.Common.TotalSize(&s.Signature) }, + Value: func() any { return &s.Signature }, + Type: ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *KeySignature) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *KeySignature) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the KeySignature. +func (s *KeySignature) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *KeySignature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, s, "Key Signature", opts...) +} diff --git a/pkg/intel/metadata/cbnt/key_signature_manifestcodegen.go b/pkg/intel/metadata/cbnt/key_signature_manifestcodegen.go deleted file mode 100644 index 68b23ba1..00000000 --- a/pkg/intel/metadata/cbnt/key_signature_manifestcodegen.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewKeySignature returns a new instance of KeySignature with -// all default values set. -func NewKeySignature() *KeySignature { - s := &KeySignature{} - // Set through tag "required": - s.Version = 0x10 - // Recursively initializing a child structure: - s.Key = *NewKey() - // Recursively initializing a child structure: - s.Signature = *NewSignature() - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *KeySignature) Validate() error { - // See tag "require" - if s.Version != 0x10 { - return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) - } - // Recursively validating a child structure: - if err := s.Key.Validate(); err != nil { - return fmt.Errorf("error on field 'Key': %w", err) - } - // Recursively validating a child structure: - if err := s.Signature.Validate(); err != nil { - return fmt.Errorf("error on field 'Signature': %w", err) - } - - return nil -} - -// ReadFrom reads the KeySignature from 'r' in format defined in the document #575623. -func (s *KeySignature) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // Key (ManifestFieldType: subStruct) - { - n, err := s.Key.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Key': %w", err) - } - totalN += int64(n) - } - - // Signature (ManifestFieldType: subStruct) - { - n, err := s.Signature.ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Signature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *KeySignature) RehashRecursive() { - s.Key.Rehash() - s.Signature.Rehash() - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *KeySignature) Rehash() { -} - -// WriteTo writes the KeySignature into 'w' in format defined in -// the document #575623. -func (s *KeySignature) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // Key (ManifestFieldType: subStruct) - { - n, err := s.Key.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Key': %w", err) - } - totalN += int64(n) - } - - // Signature (ManifestFieldType: subStruct) - { - n, err := s.Signature.WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Signature': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *KeySignature) VersionTotalSize() uint64 { - return 1 -} - -// KeySize returns the size in bytes of the value of field Key -func (s *KeySignature) KeyTotalSize() uint64 { - return s.Key.TotalSize() -} - -// SignatureSize returns the size in bytes of the value of field Signature -func (s *KeySignature) SignatureTotalSize() uint64 { - return s.Signature.TotalSize() -} - -// VersionOffset returns the offset in bytes of field Version -func (s *KeySignature) VersionOffset() uint64 { - return 0 -} - -// KeyOffset returns the offset in bytes of field Key -func (s *KeySignature) KeyOffset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// SignatureOffset returns the offset in bytes of field Signature -func (s *KeySignature) SignatureOffset() uint64 { - return s.KeyOffset() + s.KeyTotalSize() -} - -// Size returns the total size of the KeySignature. -func (s *KeySignature) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.VersionTotalSize() - size += s.KeyTotalSize() - size += s.SignatureTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *KeySignature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Key Signature", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Key", "", &s.Key, opts...)...) - // ManifestFieldType is subStruct - lines = append(lines, pretty.SubValue(depth+1, "Signature", "", &s.Signature, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/key_signature_test.go b/pkg/intel/metadata/cbnt/key_signature_test.go new file mode 100644 index 00000000..fcadd038 --- /dev/null +++ b/pkg/intel/metadata/cbnt/key_signature_test.go @@ -0,0 +1,147 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "strings" + "testing" +) + +func TestKeySignatureNew(t *testing.T) { + t.Parallel() + + ks := NewKeySignature() + if ks == nil { + t.Fatal("NewKeySignature() = nil, want non-nil") + } + if ks.Version != 0x10 { + t.Errorf("NewKeySignature().Version = 0x%x, want 0x10", ks.Version) + } + if ks.Key.Version != 0x10 { + t.Errorf("NewKeySignature().Key.Version = 0x%x, want 0x10", ks.Key.Version) + } + if ks.Signature.Version != 0x10 { + t.Errorf("NewKeySignature().Signature.Version = 0x%x, want 0x10", ks.Signature.Version) + } + if err := ks.Validate(); err != nil { + t.Errorf("NewKeySignature().Validate() error = %v, want nil", err) + } +} + +func TestKeySignatureSetSignatureAuto(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() error = %v, want nil", err) + } + + payload := []byte("key-signature-auto") + ks := NewKeySignature() + if err := ks.SetSignatureAuto(key, payload); err == nil { + t.Errorf("KeySignature.SetSignatureAuto() error = nil, want non-nil") + } +} + +func TestKeySignatureSetSignatureAndFillSignature(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() error = %v, want nil", err) + } + + payload := []byte("key-signature-manual") + + ksSet := NewKeySignature() + if err := ksSet.SetSignature(AlgRSASSA, AlgSHA256, key, payload); err != nil { + t.Fatalf("KeySignature.SetSignature() error = %v, want nil", err) + } + if err := ksSet.Verify(payload); err != nil { + t.Errorf("KeySignature.Verify() after SetSignature error = %v, want nil", err) + } + + sigData, err := NewSignatureData(AlgRSASSA, key, payload) + if err != nil { + t.Fatalf("NewSignatureData() error = %v, want nil", err) + } + rawSig, ok := sigData.(SignatureRSAASA) + if !ok { + t.Fatalf("NewSignatureData() type = %T, want %T", sigData, SignatureRSAASA(nil)) + } + + ksFill := NewKeySignature() + if err := ksFill.FillSignature(AlgRSASSA, &key.PublicKey, []byte(rawSig), AlgSHA256); err != nil { + t.Fatalf("KeySignature.FillSignature() error = %v, want nil", err) + } + if err := ksFill.Verify(payload); err != nil { + t.Errorf("KeySignature.Verify() after FillSignature error = %v, want nil", err) + } +} + +func TestKeySignatureReadWriteRoundTrip(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() error = %v, want nil", err) + } + + want := NewKeySignature() + if err := want.SetSignature(AlgRSASSA, AlgSHA256, key, []byte("round-trip")); err != nil { + t.Fatalf("KeySignature.SetSignature() error = %v, want nil", err) + } + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("KeySignature.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("KeySignature.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got KeySignature + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("KeySignature.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("KeySignature.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got.Version != want.Version { + t.Errorf("KeySignature round-trip Version = 0x%x, want 0x%x", got.Version, want.Version) + } + if got.Key.KeyAlg != want.Key.KeyAlg { + t.Errorf("KeySignature round-trip KeyAlg = %v, want %v", got.Key.KeyAlg, want.Key.KeyAlg) + } + if got.Signature.SigScheme != want.Signature.SigScheme { + t.Errorf("KeySignature round-trip SigScheme = %v, want %v", got.Signature.SigScheme, want.Signature.SigScheme) + } +} + +func TestKeySignatureMethods(t *testing.T) { + t.Parallel() + + ks := NewKeySignature() + if _, err := ks.SizeOf(99); err == nil { + t.Errorf("KeySignature.SizeOf(99) error = nil, want non-nil") + } + if _, err := ks.OffsetOf(99); err == nil { + t.Errorf("KeySignature.OffsetOf(99) error = nil, want non-nil") + } + + if got := len(ks.Layout()); got != 3 { + t.Errorf("len(KeySignature.Layout()) = %d, want %d", got, 3) + } + + var nilKS *KeySignature + if got := nilKS.TotalSize(); got != 0 { + t.Errorf("(*KeySignature)(nil).TotalSize() = %d, want %d", got, 0) + } + + pretty := ks.PrettyString(0, true) + if !strings.Contains(pretty, "Key Signature") { + t.Errorf("KeySignature.PrettyString() = %q, want to contain %q", pretty, "Key Signature") + } +} diff --git a/pkg/intel/metadata/cbnt/key_test.go b/pkg/intel/metadata/cbnt/key_test.go new file mode 100644 index 00000000..66db972c --- /dev/null +++ b/pkg/intel/metadata/cbnt/key_test.go @@ -0,0 +1,241 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "strings" + "testing" +) + +func TestKeyNewAndValidate(t *testing.T) { + t.Parallel() + + k := NewKey() + if k == nil { + t.Fatal("NewKey() = nil, want non-nil") + } + if k.Version != 0x10 { + t.Errorf("NewKey().Version = 0x%x, want 0x10", k.Version) + } + if err := k.Validate(); err != nil { + t.Errorf("NewKey().Validate() error = %v, want nil", err) + } +} + +func TestKeySetPubKeyAndPubKeyRSA(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() error = %v, want nil", err) + } + + k := NewKey() + if err := k.SetPubKey(&key.PublicKey); err != nil { + t.Fatalf("Key.SetPubKey(RSA) error = %v, want nil", err) + } + + gotPub, err := k.PubKey() + if err != nil { + t.Fatalf("Key.PubKey() error = %v, want nil", err) + } + rsaPub, ok := gotPub.(*rsa.PublicKey) + if !ok { + t.Fatalf("Key.PubKey() type = %T, want %T", gotPub, &rsa.PublicKey{}) + } + + if rsaPub.E != key.E { + t.Errorf("Key.PubKey().E = %d, want %d", rsaPub.E, key.E) + } + if rsaPub.N.Cmp(key.N) != 0 { + t.Errorf("Key.PubKey().N = %x, want %x", rsaPub.N.Bytes(), key.N.Bytes()) + } +} + +func TestKeySetPubKeyAndPubKeyECDSA(t *testing.T) { + key := ecdsaP256with32b(t) + + k := NewKey() + if err := k.SetPubKey(&key.PublicKey); err != nil { + t.Fatalf("Key.SetPubKey(ECDSA) error = %v, want nil", err) + } + + gotPub, err := k.PubKey() + if err != nil { + t.Fatalf("Key.PubKey() error = %v, want nil", err) + } + ecdsaPub, ok := gotPub.(ecdsa.PublicKey) + if !ok { + t.Fatalf("Key.PubKey() type = %T, want %T", gotPub, ecdsa.PublicKey{}) + } + + if ecdsaPub.X.Cmp(key.X) != 0 { + t.Errorf("Key.PubKey().X = %x, want %x", ecdsaPub.X.Bytes(), key.X.Bytes()) + } + if ecdsaPub.Y.Cmp(key.Y) != 0 { + t.Errorf("Key.PubKey().Y = %x, want %x", ecdsaPub.Y.Bytes(), key.Y.Bytes()) + } +} + +func TestKeyPubKeyErrors(t *testing.T) { + t.Parallel() + + k := Key{KeyAlg: AlgRSA} + k.KeySize.SetInBytes(256) + k.Data = []byte{1} + if _, err := k.PubKey(); err == nil { + t.Errorf("Key.PubKey() with invalid data length error = nil, want non-nil") + } + + k = Key{KeyAlg: AlgSHA1} + if _, err := k.PubKey(); err == nil { + t.Errorf("Key.PubKey() with unexpected algorithm error = nil, want non-nil") + } +} + +func TestKeySetPubKeyUnexpectedType(t *testing.T) { + t.Parallel() + + k := NewKey() + if err := k.SetPubKey(struct{}{}); err == nil { + t.Errorf("Key.SetPubKey(struct{}{}) error = nil, want non-nil") + } +} + +func TestKeyReadWriteRoundTrip(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() error = %v, want nil", err) + } + + want := NewKey() + if err := want.SetPubKey(&key.PublicKey); err != nil { + t.Fatalf("Key.SetPubKey() error = %v, want nil", err) + } + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("Key.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("Key.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got Key + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("Key.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("Key.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got.KeyAlg != want.KeyAlg { + t.Errorf("Key round-trip KeyAlg = %v, want %v", got.KeyAlg, want.KeyAlg) + } + if got.KeySize != want.KeySize { + t.Errorf("Key round-trip KeySize = %v, want %v", got.KeySize, want.KeySize) + } + if !bytes.Equal(got.Data, want.Data) { + t.Errorf("Key round-trip Data = %v, want %v", got.Data, want.Data) + } +} + +func TestKeyMethods(t *testing.T) { + t.Parallel() + + k := NewKey() + + if got := len(k.Layout()); got != 4 { + t.Errorf("len(Key.Layout()) = %d, want %d", got, 4) + } + + if _, err := k.SizeOf(99); err == nil { + t.Errorf("Key.SizeOf(99) error = nil, want non-nil") + } + if _, err := k.OffsetOf(99); err == nil { + t.Errorf("Key.OffsetOf(99) error = nil, want non-nil") + } + + var nilKey *Key + if got := nilKey.TotalSize(); got != 0 { + t.Errorf("(*Key)(nil).TotalSize() = %d, want %d", got, 0) + } + + pretty := k.PrettyString(0, true) + if !strings.Contains(pretty, "Key") { + t.Errorf("Key.PrettyString() = %q, want to contain %q", pretty, "Key") + } +} + +func TestKeyPrintMethodsErrorPaths(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() error = %v, want nil", err) + } + + k := NewKey() + if err := k.SetPubKey(&key.PublicKey); err != nil { + t.Fatalf("Key.SetPubKey() error = %v, want nil", err) + } + + if err := k.PrintKMPubKey(AlgSHA1); err == nil { + t.Errorf("Key.PrintKMPubKey(AlgSHA1) error = nil, want non-nil") + } + + if err := k.PrintBPMPubKey(AlgRSA); err == nil { + t.Errorf("Key.PrintBPMPubKey(AlgRSA) error = nil, want non-nil") + } +} + +func TestBitSizeMethods(t *testing.T) { + t.Parallel() + + var bs BitSize + bs.SetInBytes(8) + if got := bs.InBits(); got != 64 { + t.Errorf("BitSize.InBits() after SetInBytes(8) = %d, want %d", got, 64) + } + bs.SetInBits(24) + if got := bs.InBytes(); got != 3 { + t.Errorf("BitSize.InBytes() after SetInBits(24) = %d, want %d", got, 3) + } + + var buf bytes.Buffer + n, err := bs.WriteTo(&buf) + if err != nil { + t.Fatalf("BitSize.WriteTo() error = %v, want nil", err) + } + if n != int64(bs.TotalSize()) { + t.Errorf("BitSize.WriteTo() bytes = %d, want %d", n, bs.TotalSize()) + } + + var got BitSize + n, err = (&got).ReadFrom(&buf) + if err == nil { + t.Fatalf("BitSize.ReadFrom() error = nil, want non-nil") + } + if n != int64(got.TotalSize()) { + t.Errorf("BitSize.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } +} + +func ecdsaP256with32b(t *testing.T) *ecdsa.PrivateKey { + t.Helper() + + for { + k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("ecdsa.GenerateKey() error = %v, want nil", err) + } + if len(k.X.Bytes()) == 32 && len(k.Y.Bytes()) == 32 { + return k + } + } +} diff --git a/pkg/intel/metadata/cbnt/keymanifest/hash.go b/pkg/intel/metadata/cbnt/keymanifest/hash.go new file mode 100644 index 00000000..dd5e620a --- /dev/null +++ b/pkg/intel/metadata/cbnt/keymanifest/hash.go @@ -0,0 +1,203 @@ +// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntkey + +import ( + "encoding/binary" + "fmt" + "io" + "strings" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +// Hash is "KM hash Structure" defined in document #575623. +type Hash struct { + cbnt.Common + // Usage is the digest usage bitmask. + // + // More than one bit can be set to indicate shared digest usage. + // Usage of bit 0 is normative; other usages are informative. + Usage Usage `json:"hashUsage"` + + // Digest is the actual digest. + Digest cbnt.HashStructure `json:"hashStruct"` +} + +// Usage is the digest usage bitmask. +// +// More than one bit can be set to indicate shared digest usage. +// Usage of bit 0 is normative; other usages are informative. +type Usage uint64 + +const ( + // UsageBPMSigningPKD is the bit meaning the digest could be used as + // Boot Policy Manifest signing pubkey digest. + UsageBPMSigningPKD = Usage(1 << iota) + + // UsageFITPatchManifestSigningPKD is the bit meaning the digest could be used as + // FIT Patch Manifest signing pubkey digest. + UsageFITPatchManifestSigningPKD + + // UsageACMManifestSigningPKD is the bit meaning the digest could be used as + // ACM Manifest signing pubkey digest. + UsageACMManifestSigningPKD + + // UsageSDEVSigningPKD is the bit meaning the digest could be used as + // SDEV signing pubkey digest. + UsageSDEVSigningPKD + + // UsageReserved is a reserved bit + UsageReserved +) + +// String implements fmt.Stringer. +func (u Usage) String() string { + var result []string + for i := uint(0); i < 64; i++ { + f := Usage(1 << i) + if !u.IsSet(f) { + continue + } + var descr string + switch f { + case UsageBPMSigningPKD: + descr = "BPM_signing_pubkey_digest" + case UsageFITPatchManifestSigningPKD: + descr = "FIT_patch_manifest_signing_pubkey_digest" + case UsageACMManifestSigningPKD: + descr = "ACM_manifest_signing_pubkey_digest" + case UsageSDEVSigningPKD: + descr = "SDEV_signing_pubkey_digest" + case UsageReserved: + descr = "Reserved" + default: + descr = fmt.Sprintf("unexpected_bit_%d", i) + } + result = append(result, descr) + } + + return strings.Join(result, ",") +} + +// IsSet returns true if bits `f` are set in bitmask `u`. +func (u Usage) IsSet(f Usage) bool { + return u&f != 0 +} + +// Set sets/unsets the bits of `f` in bitmask `u`. +// +// To set the bits `v` should be true, to unset -- false. +func (u *Usage) Set(f Usage, v bool) { + if v { + *u |= f + } else { + *u &= ^f + } +} + +// NewHash returns a new instance of Hash with +// all default values set. +func NewHash() *Hash { + s := &Hash{} + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *Hash) Validate() error { + return nil +} + +// ReadFrom reads the Hash from 'r' in format defined in the document #575623. +func (s *Hash) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// WriteTo writes the Hash into 'w' in format defined in +// the document #575623. +func (s *Hash) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +func (s *Hash) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Usage", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.Usage }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 1, + Name: "Digest", + Size: func() uint64 { return s.Digest.Common.TotalSize(&s.Digest) }, + Value: func() any { return &s.Digest }, + Type: cbnt.ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *Hash) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + // normally it would be 0, but ret is already 0 if we land here + return ret, fmt.Errorf("Hash: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *Hash) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("Hash: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the Hash. +func (s *Hash) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *Hash) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return s.Common.PrettyString(depth, withHeader, s, "Hash", opts...) +} + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (u Usage) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return u.String() +} + +// TotalSize returns the total size measured through binary.Size. +func (u Usage) TotalSize() uint64 { + return uint64(binary.Size(u)) +} + +// WriteTo writes the Usage into 'w' in binary format. +func (u Usage) WriteTo(w io.Writer) (int64, error) { + return int64(u.TotalSize()), binary.Write(w, binary.LittleEndian, u) +} + +// ReadFrom reads the Usage from 'r' in binary format. +func (u Usage) ReadFrom(r io.Reader) (int64, error) { + return int64(u.TotalSize()), binary.Read(r, binary.LittleEndian, u) +} diff --git a/pkg/intel/metadata/cbnt/keymanifest/manifest.go b/pkg/intel/metadata/cbnt/keymanifest/manifest.go new file mode 100644 index 00000000..f1192381 --- /dev/null +++ b/pkg/intel/metadata/cbnt/keymanifest/manifest.go @@ -0,0 +1,48 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cbntkey provides Key Manifest representation. +package cbntkey + +import ( + "crypto" + "fmt" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" +) + +type Manifest interface { + ValidateBPMKey(bpmKS cbnt.KeySignature) error + SetSignature( + algo cbnt.Algorithm, + hashAlgo cbnt.Algorithm, + privKey crypto.Signer, + signedData []byte, + ) error + cbnt.Element + Print() +} + +func NewManifest(bgv cbnt.BootGuardVersion) (Manifest, error) { + switch bgv { + case cbnt.Version10: + s := &BGManifest{} + s.StructInfoBG = *cbnt.NewStructInfo(cbnt.Version10).(*cbnt.StructInfoBG) + s.Version = 0x10 + copy(s.ID[:], []byte(cbnt.StructureIDManifest)) + s.KeyAndSignature = *cbnt.NewKeySignature() + return s, nil + case cbnt.Version20, cbnt.Version21: + s := &CBnTManifest{} + s.StructInfoCBNT = *cbnt.NewStructInfo(cbnt.Version20).(*cbnt.StructInfoCBNT) + s.Version = 0x21 + copy(s.ID[:], []byte(cbnt.StructureIDManifest)) + s.KeyAndSignature = *cbnt.NewKeySignature() + return s, nil + default: + // This will never be the case in internal usage of NewManifest, + // though out of principle the error handling is here + return nil, fmt.Errorf("version not supported") + } +} diff --git a/pkg/intel/metadata/cbnt/keymanifest/manifest_bg.go b/pkg/intel/metadata/cbnt/keymanifest/manifest_bg.go new file mode 100644 index 00000000..bc3a7b86 --- /dev/null +++ b/pkg/intel/metadata/cbnt/keymanifest/manifest_bg.go @@ -0,0 +1,202 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntkey + +import ( + "bytes" + "crypto" + "fmt" + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +type BGManifest struct { + cbnt.Common + cbnt.StructInfoBG `id:"__KEYM__" version:"0x10"` + KMVersion uint8 `json:"kmVersion"` + KMSVN cbnt.SVN `json:"kmSVN"` + KMID uint8 `json:"kmID"` + BPKey cbnt.HashStructure `json:"kmBPKey"` + KeyAndSignature cbnt.KeySignature `json:"kmKeySignature"` +} + +// Setter for the Key signature. +func (m *BGManifest) SetSignature( + algo cbnt.Algorithm, + hashAlgo cbnt.Algorithm, + privKey crypto.Signer, + signedData []byte, +) error { + err := m.KeyAndSignature.SetSignature(algo, algo, privKey, signedData) + if err != nil { + return fmt.Errorf("unable to set the signature: %w", err) + } + + return nil +} + +// ValidateBPMKey returns an error if BPKey does not match Key Signature from BPM. +func (m *BGManifest) ValidateBPMKey(bpmKS cbnt.KeySignature) error { + h, err := m.BPKey.HashAlg.Hash() + if err != nil { + return fmt.Errorf("invalid hash algo %v: %w", m.BPKey.HashAlg, err) + } + + if len(m.BPKey.HashBuffer) != h.Size() { + return fmt.Errorf("invalid hash lenght: actual:%d expected:%d", len(m.BPKey.HashBuffer), h.Size()) + } + + switch bpmKS.Key.KeyAlg { + case cbnt.AlgRSA: + if _, err := h.Write(bpmKS.Key.Data[4:]); err != nil { + return fmt.Errorf("unable to hash: %w", err) + } + default: + return fmt.Errorf("unsupported key algorithm: %v", bpmKS.Key.KeyAlg) + } + digest := h.Sum(nil) + + if !bytes.Equal(m.BPKey.HashBuffer, digest) { + return fmt.Errorf("BPM key hash does not match the one in KM: actual:%X != in-KM:%X (hash algo: %v)", digest, m.BPKey.HashBuffer, m.BPKey.HashAlg) + } + + return nil +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (m *BGManifest) Validate() error { + // Recursively validating a child structure: + if err := m.KeyAndSignature.Validate(); err != nil { + return fmt.Errorf("error on field 'KeyAndSignature': %w", err) + } + + return nil +} + +// StructureIDManifest is the StructureID (in terms of +// the document #575623) of element 'Manifest'. +const StructureIDManifest = "__KEYM__" + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (m *BGManifest) GetStructInfo() cbnt.StructInfo { + return m.StructInfoBG +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (m *BGManifest) SetStructInfo(newStructInfo cbnt.StructInfo) { + m.StructInfoBG = newStructInfo.(cbnt.StructInfoBG) +} + +// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. +func (m *BGManifest) ReadFrom(r io.Reader) (int64, error) { + return m.Common.ReadFrom(r, m) +} + +// WriteTo writes the Manifest into 'w' in format defined in +// the document #575623. +func (m *BGManifest) WriteTo(w io.Writer) (int64, error) { + return m.Common.WriteTo(w, m) +} + +// Layout returns the structure's layout descriptor +func (m *BGManifest) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return m.StructInfoBG.TotalSize() }, + Value: func() any { return m.StructInfoBG }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "KM Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &m.KMVersion }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 2, + Name: "KMSVN", + Size: func() uint64 { return 1 }, + Value: func() any { return &m.KMSVN }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 3, + Name: "KMID", + Size: func() uint64 { return 1 }, + Value: func() any { return &m.KMID }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 4, + Name: "BP Key", + Size: func() uint64 { return m.BPKey.TotalSize() }, + Value: func() any { return &m.BPKey }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 5, + Name: "Key And Signature", + Size: func() uint64 { return m.KeyAndSignature.TotalSize() }, + Value: func() any { return &m.KeyAndSignature }, + Type: cbnt.ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (m *BGManifest) SizeOf(id int) (uint64, error) { + ret, err := m.Common.SizeOf(m, id) + if err != nil { + return ret, fmt.Errorf("CBnTManifest: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (m *BGManifest) OffsetOf(id int) (uint64, error) { + ret, err := m.Common.OffsetOf(m, id) + if err != nil { + return ret, fmt.Errorf("CBnTManifest: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the Manifest. +func (m *BGManifest) TotalSize() uint64 { + if m == nil { + return 0 + } + + return m.Common.TotalSize(m) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (m *BGManifest) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return m.Common.PrettyString(depth, withHeader, m, "BG Key Manifest", opts...) +} + +// Print prints the Key Manifest +func (m *BGManifest) Print() { + if len(m.KeyAndSignature.Signature.Data) < 1 { + fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) + fmt.Printf(" --KeyAndSignature--\n\tKey Manifest not signed!\n\n") + } else { + fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) + } +} diff --git a/pkg/intel/metadata/cbnt/keymanifest/manifest_cbnt.go b/pkg/intel/metadata/cbnt/keymanifest/manifest_cbnt.go new file mode 100644 index 00000000..ad3155fd --- /dev/null +++ b/pkg/intel/metadata/cbnt/keymanifest/manifest_cbnt.go @@ -0,0 +1,375 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntkey + +import ( + "bytes" + "crypto" + "encoding/binary" + "fmt" + "io" + "strings" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +// PrettyString: CBnT Key Manifest +type CBnTManifest struct { + cbnt.Common + cbnt.StructInfoCBNT `id:"__KEYM__" version:"0x21" var0:"0" var1:"0"` + + // KeyManifestSignatureOffset is Key Manifest KeySignature offset. + // + // The original name is "KeySignatureOffset" (in #575623). + KeyManifestSignatureOffset uint16 `rehashValue:"KeyAndSignatureOffset()" json:"kmSigOffset,omitempty"` + + // Reserved2 is an alignment. + Reserved2 [3]byte `json:"kmReserved2,omitempty"` + + // Revision is the revision of the Key Manifest defined by the Platform + // Manufacturer. + Revision uint8 `json:"kmRevision"` + + // KMSVN is the Key Manifest Security Version Number. + KMSVN cbnt.SVN `json:"kmSVN"` + + // KMID is the Key Manifest Identifier. + KMID uint8 `json:"kmID"` + + // PubKeyHashAlg is the hash algorithm of OEM public key digest programmed + // into the FPF. + PubKeyHashAlg cbnt.Algorithm `json:"kmPubKeyHashAlg"` + + // Hash is the slice of KMHASH_STRUCT (KHS) structures (see table 5-3 + // of the document #575623). Describes BPM pubkey digest (among other). + Hash HashList `json:"kmHash"` + + // KeyAndSignature is the Key Manifest signature. + KeyAndSignature cbnt.KeySignature `json:"kmKeySignature"` +} + +type HashList []Hash + +func (l *HashList) Structures() []cbnt.Structure { + out := make([]cbnt.Structure, 0, len(*l)) + for i := range *l { + out = append(out, &(*l)[i]) + } + return out +} + +// Setter for the Key Signature. +func (m *CBnTManifest) SetSignature( + algo cbnt.Algorithm, + hashAlgo cbnt.Algorithm, + privKey crypto.Signer, + signedData []byte, +) error { + err := m.KeyAndSignature.SetSignature(algo, hashAlgo, privKey, signedData) + if err != nil { + return fmt.Errorf("unable to set the signature: %w", err) + } + m.PubKeyHashAlg = m.KeyAndSignature.Signature.HashAlg + + return nil +} + +// ValidateBPMKey returns an error if any of the keys in BPM does not match +// the ones in KM. +func (m *CBnTManifest) ValidateBPMKey(bpmKS cbnt.KeySignature) error { + hashCount := 0 + for _, hashEntry := range m.Hash { + if !hashEntry.Usage.IsSet(UsageBPMSigningPKD) { + continue + } + + h, err := hashEntry.Digest.HashAlg.Hash() + if err != nil { + return fmt.Errorf("invalid hash algo %v: %w", hashEntry.Digest.HashAlg, err) + } + + if len(hashEntry.Digest.HashBuffer) != h.Size() { + return fmt.Errorf("invalid hash lenght: actual:%d expected:%d", len(hashEntry.Digest.HashBuffer), h.Size()) + } + + switch bpmKS.Key.KeyAlg { + case cbnt.AlgRSA: + if _, err := h.Write(bpmKS.Key.Data[4:]); err != nil { + return fmt.Errorf("unable to hash: %w", err) + } + default: + return fmt.Errorf("unsupported key algorithm: %v", bpmKS.Key.KeyAlg) + } + digest := h.Sum(nil) + + if !bytes.Equal(hashEntry.Digest.HashBuffer, digest) { + return fmt.Errorf("BPM key hash does not match the one in KM: actual:%X != in-KM:%X (hash algo: %v)", digest, hashEntry.Digest.HashBuffer, hashEntry.Digest.HashAlg) + } + hashCount++ + } + + if hashCount == 0 { + return fmt.Errorf("no hash of BPM's key was found in KM") + } + + return nil +} + +// Validate (recursively) checks the structure if there are any unexpected values. +func (m *CBnTManifest) Validate() error { + v, err := m.OffsetOf(8) + if err != nil { + return fmt.Errorf("error on field 'KeyAndSignature': %w", err) + } + expectedValue := uint16(v) + if m.KeyManifestSignatureOffset != expectedValue { + return fmt.Errorf("field 'KeyManifestSignatureOffset' expects write-value '%v', but has %v", expectedValue, m.KeyManifestSignatureOffset) + } + // Recursively validating a child structure: + if err := m.KeyAndSignature.Validate(); err != nil { + return fmt.Errorf("error on field 'KeyAndSignature': %w", err) + } + + return nil +} + +// GetStructInfo returns current value of StructInfo of the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (m *CBnTManifest) GetStructInfo() cbnt.StructInfo { + return m.StructInfoCBNT +} + +// SetStructInfo sets new value of StructInfo to the structure. +// +// StructInfo is a set of standard fields with presented in any element +// ("element" in terms of document #575623). +func (m *CBnTManifest) SetStructInfo(newStructInfo cbnt.StructInfo) { + m.StructInfoCBNT = newStructInfo.(cbnt.StructInfoCBNT) +} + +// ReadFrom reads the Manifest from 'r' in format defined in the document #575623. +func (m *CBnTManifest) ReadFrom(r io.Reader) (int64, error) { + return m.Common.ReadFrom(r, m) +} + +// RehashRecursive calls Rehash (see below) recursively. +func (m *CBnTManifest) RehashRecursive() { + m.Rehash() +} + +// Rehash sets values which are calculated automatically depending on the rest +// data. It is usually about the total size field of an element. +func (m *CBnTManifest) Rehash() { + m.Variable0 = 0 + m.ElementSize = 0 + v, err := m.OffsetOf(8) + if err != nil { + // TODO: this will never be true, but still lets think of how to handle + fmt.Println("offset fail (todo handle better)") + } + m.KeyManifestSignatureOffset = uint16(v) +} + +// WriteTo writes the Manifest into 'w' in format defined in +// the document #575623. +func (m *CBnTManifest) WriteTo(w io.Writer) (int64, error) { + m.Rehash() + return m.Common.WriteTo(w, m) +} + +// Layout returns the structure's layout descriptor +func (m *CBnTManifest) Layout() []cbnt.LayoutField { + return []cbnt.LayoutField{ + { + ID: 0, + Name: "Struct Info", + Size: func() uint64 { return m.StructInfoCBNT.TotalSize() }, + Value: func() any { return m.StructInfoCBNT }, + Type: cbnt.ManifestFieldSubStruct, + }, + { + ID: 1, + Name: "Key Manifest Signature Offset", + Size: func() uint64 { return 2 }, + Value: func() any { return &m.KeyManifestSignatureOffset }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Reserved 2", + Size: func() uint64 { return 3 }, + Value: func() any { return &m.Reserved2 }, + Type: cbnt.ManifestFieldArrayStatic, + }, + { + ID: 3, + Name: "Revision", + Size: func() uint64 { return 1 }, + Value: func() any { return &m.Revision }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 4, + Name: "KMSVN", + Size: func() uint64 { return 1 }, + Value: func() any { return &m.KMSVN }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 5, + Name: "KMID", + Size: func() uint64 { return 1 }, + Value: func() any { return &m.KMID }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 6, + Name: "Pub Key Hash Alg", + Size: func() uint64 { return 2 }, + Value: func() any { return &m.PubKeyHashAlg }, + Type: cbnt.ManifestFieldEndValue, + }, + { + ID: 7, + Name: fmt.Sprintf("Hash: Array of \"Key Manifest\" of length %d", len(m.Hash)), + Size: func() uint64 { + size := uint64(binary.Size(uint16(0))) + for idx := range m.Hash { + size += m.Hash[idx].TotalSize() + } + return size + }, + Value: func() any { return &m.Hash }, + Type: cbnt.ManifestFieldList, + ReadList: func(r io.Reader) (int64, error) { + var count uint16 + err := binary.Read(r, binary.LittleEndian, &count) + if err != nil { + return 0, fmt.Errorf("unable to read the count for field 'Hash': %w", err) + } + totalN := int64(binary.Size(count)) + m.Hash = make([]Hash, count) + for idx := range m.Hash { + n, err := m.Hash[idx].ReadFrom(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field 'Hash[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + WriteList: func(w io.Writer) (int64, error) { + count := uint16(len(m.Hash)) + if err := binary.Write(w, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to write the count for field 'Hash': %w", err) + } + totalN := int64(binary.Size(count)) + + for idx := range m.Hash { + n, err := m.Hash[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'Hash[%d]': %w", idx, err) + } + totalN += int64(n) + } + + return totalN, nil + }, + }, + { + ID: 8, + Name: "Key And Signature", + Size: func() uint64 { return m.KeyAndSignature.TotalSize() }, + Value: func() any { return &m.KeyAndSignature }, + Type: cbnt.ManifestFieldSubStruct, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (m *CBnTManifest) SizeOf(id int) (uint64, error) { + ret, err := m.Common.SizeOf(m, id) + if err != nil { + return ret, fmt.Errorf("CBnTManifest: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (m *CBnTManifest) OffsetOf(id int) (uint64, error) { + ret, err := m.Common.OffsetOf(m, id) + if err != nil { + return ret, fmt.Errorf("CBnTManifest: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the Manifest. +func (m *CBnTManifest) TotalSize() uint64 { + if m == nil { + return 0 + } + + return m.Common.TotalSize(m) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (m *CBnTManifest) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "CBnT Key Manifest", m)) + } + if m == nil { + return strings.Join(lines, "\n") + } + + lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &m.StructInfoCBNT, opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "Key Manifest Signature Offset", "", &m.KeyManifestSignatureOffset, opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "Reserved 2", "", &m.Reserved2, opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "Revision", "", &m.Revision, opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "KMSVN", "", &m.KMSVN, opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "KMID", "", &m.KMID, opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "Pub Key Hash Alg", "", &m.PubKeyHashAlg, opts...)...) + + lines = append(lines, pretty.Header( + depth+1, + fmt.Sprintf("Hash: Array of \"Key Manifest\" of length %d", len(m.Hash)), + m.Hash, + )) + for i := 0; i < len(m.Hash); i++ { + lines = append( + lines, + fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+ + strings.TrimSpace(m.Hash[i].PrettyString(depth+2, true, opts...)), + ) + } + + if depth < 1 { + lines = append(lines, "") + } + + lines = append(lines, pretty.SubValue(depth+1, "Key And Signature", "", &m.KeyAndSignature, opts...)...) + + if depth < 2 { + lines = append(lines, "") + } + + return strings.Join(lines, "\n") +} + +// Print prints the Key Manifest. +func (m *CBnTManifest) Print() { + if len(m.KeyAndSignature.Signature.Data) < 1 { + fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) + fmt.Printf(" --KeyAndSignature--\n\tKey Manifest not signed!\n\n") + } else { + fmt.Printf("%v\n", m.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) + } +} diff --git a/pkg/intel/metadata/cbnt/keymanifest/manifest_test.go b/pkg/intel/metadata/cbnt/keymanifest/manifest_test.go new file mode 100644 index 00000000..3b478840 --- /dev/null +++ b/pkg/intel/metadata/cbnt/keymanifest/manifest_test.go @@ -0,0 +1,29 @@ +// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbntkey + +import ( + "testing" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/integration" +) + +func TestReadWriteBG(t *testing.T) { + m, err := NewManifest(cbnt.Version10) + if err != nil { + t.Fatalf("%v", err) + } + integration.ManifestReadWrite(t, m, "testdata/km_bg.bin") +} + +func TestReadWriteCBNT(t *testing.T) { + m, err := NewManifest(cbnt.Version20) + if err != nil { + t.Fatalf("%v", err) + } + integration.ManifestReadWrite(t, m, "testdata/km_cbnt.bin") + +} diff --git a/pkg/intel/metadata/bg/bgkey/testdata/km.bin b/pkg/intel/metadata/cbnt/keymanifest/testdata/km_bg.bin similarity index 100% rename from pkg/intel/metadata/bg/bgkey/testdata/km.bin rename to pkg/intel/metadata/cbnt/keymanifest/testdata/km_bg.bin diff --git a/pkg/intel/metadata/cbnt/cbntkey/testdata/km.bin b/pkg/intel/metadata/cbnt/keymanifest/testdata/km_cbnt.bin similarity index 100% rename from pkg/intel/metadata/cbnt/cbntkey/testdata/km.bin rename to pkg/intel/metadata/cbnt/keymanifest/testdata/km_cbnt.bin diff --git a/pkg/intel/metadata/cbnt/revision.go b/pkg/intel/metadata/cbnt/revision.go new file mode 100644 index 00000000..26558512 --- /dev/null +++ b/pkg/intel/metadata/cbnt/revision.go @@ -0,0 +1,53 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "encoding/binary" + "fmt" + "io" +) + +type BootGuardVersion uint8 + +func (bgv BootGuardVersion) String() string { + switch bgv { + case Version10: + return "1.0" + case Version20: + return "2.0" + case Version21: + return "2.1" + } + return "unknown" +} + +func DetectBGV(r io.ReadSeeker) (BootGuardVersion, error) { + // We could take StructInfoBG here as well since version + // is under the saem offset, so it does not really matter. + // Plus we just have it here for version detection, so it won't + // hurt even if read version is actually 0x10. + var s StructInfoCBNT + err := binary.Read(r, endianess, &s) + if err != nil { + return 0, fmt.Errorf("unable to read field 'ID': %w", err) + } + _, err = r.Seek(0, 0) + if err != nil { + return 0, err + } + + // See #575623-1.2.9 Section 5.3.3.1 Tab. 5-16. + switch s.Version { + case 0x10: + return Version10, nil + case 0x20, 0x21: + return Version20, nil + case 0x22, 0x23, 0x24, 0x25: + return Version21, nil + default: + return 0, fmt.Errorf("couldn't detect version 0x%x", s.Version) + } +} diff --git a/pkg/intel/metadata/cbnt/revision_test.go b/pkg/intel/metadata/cbnt/revision_test.go new file mode 100644 index 00000000..4ca25f1b --- /dev/null +++ b/pkg/intel/metadata/cbnt/revision_test.go @@ -0,0 +1,133 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "errors" + "io" + "strings" + "testing" +) + +func TestBootGuardVersionString(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + in BootGuardVersion + want string + }{ + "version_1_0": {in: Version10, want: "1.0"}, + "version_2_0": {in: Version20, want: "2.0"}, + "version_2_1": {in: Version21, want: "2.1"}, + "unknown": {in: BootGuardVersion(0xFF), want: "unknown"}, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + if got := tt.in.String(); got != tt.want { + t.Errorf("BootGuardVersion(%d).String() = %q, want %q", tt.in, got, tt.want) + } + }) + } +} + +func TestDetectBGV(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + version byte + want BootGuardVersion + wantErr bool + errSub string + }{ + {name: "0x10_to_v1_0", version: 0x10, want: Version10}, + {name: "0x20_to_v2_0", version: 0x20, want: Version20}, + {name: "0x21_to_v2_0", version: 0x21, want: Version20}, + {name: "0x22_to_v2_1", version: 0x22, want: Version21}, + {name: "0x23_to_v2_1", version: 0x23, want: Version21}, + {name: "0x24_to_v2_1", version: 0x24, want: Version21}, + {name: "0x25_to_v2_1", version: 0x25, want: Version21}, + {name: "unknown", version: 0x99, wantErr: true, errSub: "couldn't detect version"}, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + r := bytes.NewReader(rawStructInfoVersion(tt.version)) + got, err := DetectBGV(r) + if tt.wantErr { + if err == nil { + t.Errorf("DetectBGV(version 0x%x) error = nil, want non-nil", tt.version) + } else if tt.errSub != "" && !strings.Contains(err.Error(), tt.errSub) { + t.Errorf("DetectBGV(version 0x%x) error = %q, want substring %q", tt.version, err.Error(), tt.errSub) + } + } else if err != nil { + t.Fatalf("DetectBGV(version 0x%x) error = %v, want nil", tt.version, err) + } else if got != tt.want { + t.Errorf("DetectBGV(version 0x%x) = %v, want %v", tt.version, got, tt.want) + } + + if pos, err := r.Seek(0, io.SeekCurrent); err != nil { + t.Fatalf("reader.Seek(current) error = %v, want nil", err) + } else if pos != 0 { + t.Errorf("reader position after DetectBGV() = %d, want %d", pos, 0) + } + }) + } +} + +func TestDetectBGVErrorPaths(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + r io.ReadSeeker + errSub string + }{ + {name: "read_error", r: bytes.NewReader(nil), errSub: "unable to read field 'ID'"}, + {name: "seek_error", r: seekFailReadSeeker{r: bytes.NewReader(rawStructInfoVersion(0x20))}, errSub: "seek failed"}, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + _, err := DetectBGV(tt.r) + if err == nil { + t.Fatalf("DetectBGV(%s) error = nil, want non-nil", tt.name) + } + if !strings.Contains(err.Error(), tt.errSub) { + t.Errorf("DetectBGV(%s) error = %q, want substring %q", tt.name, err.Error(), tt.errSub) + } + }) + } +} + +func rawStructInfoVersion(version byte) []byte { + b := make([]byte, 12) + b[8] = version + return b +} + +type seekFailReadSeeker struct { + r io.Reader +} + +func (s seekFailReadSeeker) Read(p []byte) (int, error) { + return s.r.Read(p) +} + +func (s seekFailReadSeeker) Seek(offset int64, whence int) (int64, error) { + return 0, errors.New("seek failed") +} diff --git a/pkg/intel/metadata/cbnt/signature.go b/pkg/intel/metadata/cbnt/signature.go index 53d1ec89..b5fbb6e9 100644 --- a/pkg/intel/metadata/cbnt/signature.go +++ b/pkg/intel/metadata/cbnt/signature.go @@ -8,18 +8,16 @@ package cbnt import ( "crypto" - "crypto/rand" "fmt" "math/big" -) -var ( - // RandReader exports the rand.Reader - RandReader = rand.Reader + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" ) -// Signature exports the Signature structure type Signature struct { + Common SigScheme Algorithm `json:"sigScheme"` Version uint8 `require:"0x10" json:"sigVersion,omitempty"` KeySize BitSize `json:"sigKeysize,omitempty"` @@ -27,8 +25,119 @@ type Signature struct { Data []byte `countValue:"KeySize.InBytes()" prettyValue:"dataPrettyValue()" json:"sigData"` } -func (m Signature) dataPrettyValue() interface{} { - r, _ := m.SignatureData() +// NewSignature returns a new instance of Signature with +// all default values set. +func NewSignature() *Signature { + s := &Signature{} + // Set through tag "required": + s.Version = 0x10 + return s +} + +// Validate (recursively) checks the structure if there are any unexpected +// values. It returns an error if so. +func (s *Signature) Validate() error { + // See tag "require" + if s.Version != 0x10 { + return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) + } + + return nil +} + +// ReadFrom reads the Signature from 'r' in format defined in the document #575623. +func (s *Signature) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// WriteTo writes the Signature into 'w' in format defined in +// the document #575623. +func (s *Signature) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s *Signature) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "Sig Scheme", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.SigScheme }, + Type: ManifestFieldEndValue, + }, + { + ID: 1, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Version }, + Type: ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Key Size", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.KeySize }, + Type: ManifestFieldEndValue, + }, + { + ID: 3, + Name: "Hash Alg", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.HashAlg }, + Type: ManifestFieldEndValue, + }, + { + ID: 4, + Name: "Data", + Size: func() uint64 { return uint64(s.KeySize.InBytes()) }, + Value: func() any { return &s.Data }, + Type: ManifestFieldArrayDynamicWithSize, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *Signature) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *Signature) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the Signature. +func (s *Signature) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *Signature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, s, "Signature", opts...) +} + +func (s Signature) dataPrettyValue() any { + r, _ := s.SignatureData() return r } @@ -37,31 +146,31 @@ func (m Signature) dataPrettyValue() interface{} { // * SignatureRSAASA // * SignatureECDSA // * SignatureSM2 -func (m Signature) SignatureData() (SignatureDataInterface, error) { - switch m.SigScheme { +func (s Signature) SignatureData() (SignatureDataInterface, error) { + switch s.SigScheme { case AlgRSAPSS: - return SignatureRSAPSS(m.Data), nil + return SignatureRSAPSS(s.Data), nil case AlgRSASSA: - return SignatureRSAASA(m.Data), nil + return SignatureRSAASA(s.Data), nil case AlgECDSA: - if len(m.Data) != 64 && len(m.Data) != 96 { - return nil, fmt.Errorf("invalid length of the signature data: %d (expected 64 or 96)", len(m.Data)) + if len(s.Data) != 64 && len(s.Data) != 96 { + return nil, fmt.Errorf("invalid length of the signature data: %d (expected 64 or 96)", len(s.Data)) } return SignatureECDSA{ - R: new(big.Int).SetBytes(reverseBytes(m.Data[:len(m.Data)/2])), - S: new(big.Int).SetBytes(reverseBytes(m.Data[len(m.Data)/2:])), + R: new(big.Int).SetBytes(reverseBytes(s.Data[:len(s.Data)/2])), + S: new(big.Int).SetBytes(reverseBytes(s.Data[len(s.Data)/2:])), }, nil case AlgSM2: - if len(m.Data) != 64 && len(m.Data) != 96 { - return nil, fmt.Errorf("invalid length of the signature data: %d (expected 64 or 96)", len(m.Data)) + if len(s.Data) != 64 && len(s.Data) != 96 { + return nil, fmt.Errorf("invalid length of the signature data: %d (expected 64 or 96)", len(s.Data)) } return SignatureSM2{ - R: new(big.Int).SetBytes(reverseBytes(m.Data[:len(m.Data)/2])), - S: new(big.Int).SetBytes(reverseBytes(m.Data[len(m.Data)/2:])), + R: new(big.Int).SetBytes(reverseBytes(s.Data[:len(s.Data)/2])), + S: new(big.Int).SetBytes(reverseBytes(s.Data[len(s.Data)/2:])), }, nil } - return nil, fmt.Errorf("unexpected signature scheme: %s", m.SigScheme) + return nil, fmt.Errorf("unexpected signature scheme: %s", s.SigScheme) } // SetSignatureByData sets all the fields of the structure Signature by @@ -70,45 +179,45 @@ func (m Signature) SignatureData() (SignatureDataInterface, error) { // * SignatureRSAASA // * SignatureECDSA // * SignatureSM2 -func (m *Signature) SetSignatureByData(sig SignatureDataInterface, hashAlgo Algorithm) error { - err := m.SetSignatureData(sig) +func (s *Signature) SetSignatureByData(sig SignatureDataInterface, hashAlgo Algorithm) error { + err := s.SetSignatureData(sig) if err != nil { return err } switch sig := sig.(type) { case SignatureRSAPSS: - m.SigScheme = AlgRSAPSS + s.SigScheme = AlgRSAPSS if hashAlgo.IsNull() { - m.HashAlg = AlgSHA384 + s.HashAlg = AlgSHA384 } else { - m.HashAlg = hashAlgo + s.HashAlg = hashAlgo } - m.KeySize.SetInBytes(uint16(len(m.Data))) + s.KeySize.SetInBytes(uint16(len(s.Data))) case SignatureRSAASA: - m.SigScheme = AlgRSASSA + s.SigScheme = AlgRSASSA if hashAlgo.IsNull() { - m.HashAlg = AlgSHA256 + s.HashAlg = AlgSHA256 } else { - m.HashAlg = hashAlgo + s.HashAlg = hashAlgo } - m.KeySize.SetInBytes(uint16(len(m.Data))) + s.KeySize.SetInBytes(uint16(len(s.Data))) case SignatureECDSA: - m.SigScheme = AlgECDSA + s.SigScheme = AlgECDSA if hashAlgo.IsNull() { - m.HashAlg = AlgSHA512 + s.HashAlg = AlgSHA512 } else { - m.HashAlg = hashAlgo + s.HashAlg = hashAlgo } - m.KeySize.SetInBits(uint16(sig.R.BitLen())) + s.KeySize.SetInBits(uint16(sig.R.BitLen())) case SignatureSM2: - m.SigScheme = AlgSM2 + s.SigScheme = AlgSM2 if hashAlgo.IsNull() { - m.HashAlg = AlgSM3 + s.HashAlg = AlgSM3 } else { - m.HashAlg = hashAlgo + s.HashAlg = hashAlgo } - m.KeySize.SetInBits(uint16(sig.R.BitLen())) + s.KeySize.SetInBits(uint16(sig.R.BitLen())) default: return fmt.Errorf("unexpected signature type: %T", sig) } @@ -121,31 +230,31 @@ func (m *Signature) SetSignatureByData(sig SignatureDataInterface, hashAlgo Algo // * SignatureRSAASA // * SignatureECDSA // * SignatureSM2 -func (m *Signature) SetSignatureData(sig SignatureDataInterface) error { +func (s *Signature) SetSignatureData(sig SignatureDataInterface) error { switch sig := sig.(type) { case SignatureRSAPSS: - m.Data = sig + s.Data = sig case SignatureRSAASA: - m.Data = sig + s.Data = sig case SignatureECDSA, SignatureSM2: - var r, s *big.Int + var r, p *big.Int switch sig := sig.(type) { case SignatureECDSA: - r, s = sig.R, sig.S + r, p = sig.R, sig.S case SignatureSM2: - r, s = sig.R, sig.S + r, p = sig.R, sig.S default: return fmt.Errorf("internal error") } - if r.BitLen() != s.BitLen() { - return fmt.Errorf("the length of component R (%d) is not equal to the length of component S (%d)", r.BitLen(), s.BitLen()) + if r.BitLen() != p.BitLen() { + return fmt.Errorf("the length of component R (%d) is not equal to the length of component S (%d)", r.BitLen(), p.BitLen()) } if r.BitLen() != 256 && r.BitLen() != 384 { return fmt.Errorf("component R (or S) size should be 256 or 384 bites (not %d)", r.BitLen()) } - m.Data = make([]byte, r.BitLen()/8+s.BitLen()/8) - copy(m.Data[:], reverseBytes(r.Bytes())) - copy(m.Data[r.BitLen()/8:], reverseBytes(s.Bytes())) + s.Data = make([]byte, r.BitLen()/8+p.BitLen()/8) + copy(s.Data[:], reverseBytes(r.Bytes())) + copy(s.Data[r.BitLen()/8:], reverseBytes(p.Bytes())) default: return fmt.Errorf("unexpected signature type: %T", sig) } @@ -157,14 +266,14 @@ func (m *Signature) SetSignatureData(sig SignatureDataInterface) error { // // if signAlgo is zero then it is detected automatically, based on the type // of the provided private key. -func (m *Signature) SetSignature(signAlgo Algorithm, hashAlgo Algorithm, privKey crypto.Signer, signedData []byte) error { - m.Version = 0x10 - m.HashAlg = hashAlgo +func (s *Signature) SetSignature(signAlgo Algorithm, hashAlgo Algorithm, privKey crypto.Signer, signedData []byte) error { + s.Version = 0x10 + s.HashAlg = hashAlgo signData, err := NewSignatureData(signAlgo, privKey, signedData) if err != nil { return fmt.Errorf("unable to construct the signature data: %w", err) } - err = m.SetSignatureByData(signData, m.HashAlg) + err = s.SetSignatureByData(signData, s.HashAlg) if err != nil { return fmt.Errorf("unable to set the signature: %w", err) } @@ -177,14 +286,14 @@ func (m *Signature) SetSignature(signAlgo Algorithm, hashAlgo Algorithm, privKey // // if signAlgo is zero then it is detected automatically, based on the type // of the provided private key. -func (m *Signature) FillSignature(signAlgo Algorithm, pubKey crypto.PublicKey, signedData []byte, hashAlgo Algorithm) error { - m.Version = 0x10 +func (s *Signature) FillSignature(signAlgo Algorithm, pubKey crypto.PublicKey, signedData []byte, hashAlgo Algorithm) error { + s.Version = 0x10 signData, err := NewSignatureByData(signAlgo, pubKey, signedData) if err != nil { return fmt.Errorf("unable to construct the signature data: %w", err) } - err = m.SetSignatureByData(signData, hashAlgo) + err = s.SetSignatureByData(signData, hashAlgo) if err != nil { return fmt.Errorf("unable to set the signature: %w", err) } diff --git a/pkg/intel/metadata/cbnt/signature_manifestcodegen.go b/pkg/intel/metadata/cbnt/signature_manifestcodegen.go deleted file mode 100644 index 143c990b..00000000 --- a/pkg/intel/metadata/cbnt/signature_manifestcodegen.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewSignature returns a new instance of Signature with -// all default values set. -func NewSignature() *Signature { - s := &Signature{} - // Set through tag "required": - s.Version = 0x10 - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *Signature) Validate() error { - // See tag "require" - if s.Version != 0x10 { - return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version) - } - - return nil -} - -// ReadFrom reads the Signature from 'r' in format defined in the document #575623. -func (s *Signature) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // SigScheme (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.SigScheme) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'SigScheme': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - size := uint16(s.KeySize.InBytes()) - s.Data = make([]byte, size) - n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *Signature) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *Signature) Rehash() { -} - -// WriteTo writes the Signature into 'w' in format defined in -// the document #575623. -func (s *Signature) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // SigScheme (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.SigScheme) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'SigScheme': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // KeySize (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySize) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'KeySize': %w", err) - } - totalN += int64(n) - } - - // HashAlg (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.HashAlg) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'HashAlg': %w", err) - } - totalN += int64(n) - } - - // Data (ManifestFieldType: arrayDynamic) - { - n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Data': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// SigSchemeSize returns the size in bytes of the value of field SigScheme -func (s *Signature) SigSchemeTotalSize() uint64 { - return 2 -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *Signature) VersionTotalSize() uint64 { - return 1 -} - -// KeySizeSize returns the size in bytes of the value of field KeySize -func (s *Signature) KeySizeTotalSize() uint64 { - return 2 -} - -// HashAlgSize returns the size in bytes of the value of field HashAlg -func (s *Signature) HashAlgTotalSize() uint64 { - return 2 -} - -// DataSize returns the size in bytes of the value of field Data -func (s *Signature) DataTotalSize() uint64 { - return uint64(len(s.Data)) -} - -// SigSchemeOffset returns the offset in bytes of field SigScheme -func (s *Signature) SigSchemeOffset() uint64 { - return 0 -} - -// VersionOffset returns the offset in bytes of field Version -func (s *Signature) VersionOffset() uint64 { - return s.SigSchemeOffset() + s.SigSchemeTotalSize() -} - -// KeySizeOffset returns the offset in bytes of field KeySize -func (s *Signature) KeySizeOffset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// HashAlgOffset returns the offset in bytes of field HashAlg -func (s *Signature) HashAlgOffset() uint64 { - return s.KeySizeOffset() + s.KeySizeTotalSize() -} - -// DataOffset returns the offset in bytes of field Data -func (s *Signature) DataOffset() uint64 { - return s.HashAlgOffset() + s.HashAlgTotalSize() -} - -// Size returns the total size of the Signature. -func (s *Signature) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.SigSchemeTotalSize() - size += s.VersionTotalSize() - size += s.KeySizeTotalSize() - size += s.HashAlgTotalSize() - size += s.DataTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *Signature) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Signature", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Sig Scheme", "", &s.SigScheme, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Key Size", "", &s.KeySize, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Hash Alg", "", &s.HashAlg, opts...)...) - // ManifestFieldType is arrayDynamic - lines = append(lines, pretty.SubValue(depth+1, "Data", "", s.dataPrettyValue(), opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/signature_test.go b/pkg/intel/metadata/cbnt/signature_test.go new file mode 100644 index 00000000..d361f204 --- /dev/null +++ b/pkg/intel/metadata/cbnt/signature_test.go @@ -0,0 +1,513 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "fmt" + "math/big" + "strings" + "testing" +) + +func TestSignatureNewAndValidate(t *testing.T) { + t.Parallel() + + s := NewSignature() + if s == nil { + t.Fatal("NewSignature() = nil, want non-nil") + } + if s.Version != 0x10 { + t.Errorf("NewSignature().Version = 0x%x, want 0x10", s.Version) + } + if err := s.Validate(); err != nil { + t.Errorf("NewSignature().Validate() error = %v, want nil", err) + } +} + +func TestSignatureValidateRejectsInvalidVersion(t *testing.T) { + t.Parallel() + + s := NewSignature() + s.Version = 0 + + if err := s.Validate(); err == nil { + t.Errorf("Signature.Validate() error = nil, want non-nil") + } +} + +func TestSignatureSetSignatureByData(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + sig SignatureDataInterface + hashAlgo Algorithm + wantSigScheme Algorithm + wantHash Algorithm + wantSizeBits uint16 + }{ + "rsassa_default_hash": { + sig: SignatureRSAASA([]byte{1, 2, 3, 4}), + hashAlgo: AlgUnknown, + wantSigScheme: AlgRSASSA, + wantHash: AlgSHA256, + wantSizeBits: 32, + }, + "rsapss_default_hash": { + sig: SignatureRSAPSS([]byte{1, 2, 3, 4, 5, 6}), + hashAlgo: AlgUnknown, + wantSigScheme: AlgRSAPSS, + wantHash: AlgSHA384, + wantSizeBits: 48, + }, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + s := NewSignature() + if err := s.SetSignatureByData(tt.sig, tt.hashAlgo); err != nil { + t.Fatalf("Signature.SetSignatureByData() error = %v, want nil", err) + } + + if s.SigScheme != tt.wantSigScheme { + t.Errorf("Signature.SetSignatureByData() SigScheme = %v, want %v", s.SigScheme, tt.wantSigScheme) + } + if s.HashAlg != tt.wantHash { + t.Errorf("Signature.SetSignatureByData() HashAlg = %v, want %v", s.HashAlg, tt.wantHash) + } + if got := s.KeySize.InBits(); got != tt.wantSizeBits { + t.Errorf("Signature.SetSignatureByData() KeySize = %d, want %d", got, tt.wantSizeBits) + } + }) + } +} + +func TestSignatureSetSignatureDataErrors(t *testing.T) { + t.Parallel() + + s := NewSignature() + + if err := s.SetSignatureData(unsupportedSignatureData{}); err == nil { + t.Errorf("Signature.SetSignatureData(unsupportedSignatureData{}) error = nil, want non-nil") + } + + badLen := SignatureECDSA{R: big.NewInt(1), S: new(big.Int).Lsh(big.NewInt(1), 255)} + if err := s.SetSignatureData(badLen); err == nil { + t.Errorf("Signature.SetSignatureData(mismatched ECDSA components) error = nil, want non-nil") + } + + badSize := SignatureECDSA{R: big.NewInt(1), S: big.NewInt(1)} + if err := s.SetSignatureData(badSize); err == nil { + t.Errorf("Signature.SetSignatureData(non-256/384 ECDSA components) error = nil, want non-nil") + } +} + +func TestSignatureSetFillAndParseDataWithRSA(t *testing.T) { + key := genRSA(t, 2048) + + payload := []byte("signature-rsa") + + sSet := NewSignature() + if err := sSet.SetSignature(AlgRSASSA, AlgSHA256, key, payload); err != nil { + t.Fatalf("Signature.SetSignature() error = %v, want nil", err) + } + parsed, err := sSet.SignatureData() + if err != nil { + t.Fatalf("Signature.SignatureData() error = %v, want nil", err) + } + if err := parsed.Verify(&key.PublicKey, sSet.HashAlg, payload); err != nil { + t.Errorf("SignatureData.Verify() error = %v, want nil", err) + } + + sFill := NewSignature() + if err := sFill.FillSignature(AlgRSASSA, &key.PublicKey, sSet.Data, AlgSHA256); err != nil { + t.Fatalf("Signature.FillSignature() error = %v, want nil", err) + } + parsedFill, err := sFill.SignatureData() + if err != nil { + t.Fatalf("Signature.SignatureData() after FillSignature error = %v, want nil", err) + } + if err := parsedFill.Verify(&key.PublicKey, sFill.HashAlg, payload); err != nil { + t.Errorf("SignatureData.Verify() after FillSignature error = %v, want nil", err) + } +} + +func TestSignatureDataByScheme(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + sigScheme Algorithm + data []byte + wantType string + wantErr bool + errSub string + }{ + "rsapss": {sigScheme: AlgRSAPSS, data: []byte{1, 2, 3}, wantType: "cbnt.SignatureRSAPSS"}, + "rsassa": {sigScheme: AlgRSASSA, data: []byte{4, 5, 6}, wantType: "cbnt.SignatureRSAASA"}, + "ecdsa_64": { + sigScheme: AlgECDSA, + data: bytes.Repeat([]byte{0x11}, 64), + wantType: "cbnt.SignatureECDSA", + }, + "sm2_96": { + sigScheme: AlgSM2, + data: bytes.Repeat([]byte{0x22}, 96), + wantType: "cbnt.SignatureSM2", + }, + "ecdsa_bad_len": { + sigScheme: AlgECDSA, + data: []byte{1, 2, 3}, + wantErr: true, + errSub: "invalid length", + }, + "sm2_bad_len": { + sigScheme: AlgSM2, + data: []byte{1, 2, 3}, + wantErr: true, + errSub: "invalid length", + }, + "unknown_scheme": { + sigScheme: AlgSHA256, + data: []byte{1}, + wantErr: true, + errSub: "unexpected signature scheme", + }, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + s := Signature{SigScheme: tt.sigScheme, Data: tt.data} + got, err := s.SignatureData() + if tt.wantErr { + if err == nil { + t.Errorf("Signature.SignatureData(%v) error = nil, want non-nil", tt.sigScheme) + } else if tt.errSub != "" && !strings.Contains(err.Error(), tt.errSub) { + t.Errorf("Signature.SignatureData(%v) error = %q, want substring %q", tt.sigScheme, err.Error(), tt.errSub) + } + return + } + + if err != nil { + t.Fatalf("Signature.SignatureData(%v) error = %v, want nil", tt.sigScheme, err) + } + if gotType := fmt.Sprintf("%T", got); gotType != tt.wantType { + t.Errorf("Signature.SignatureData(%v) type = %s, want %s", tt.sigScheme, gotType, tt.wantType) + } + }) + } +} + +func TestSignatureSetSignatureByDataAdditionalCases(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + sig SignatureDataInterface + hashAlgo Algorithm + wantSigScheme Algorithm + wantHash Algorithm + wantKeyBits uint16 + wantErr bool + errSub string + }{ + "ecdsa_default_hash": { + sig: SignatureECDSA{R: bigIntBits(256), S: bigIntBits(256)}, + hashAlgo: AlgUnknown, + wantSigScheme: AlgECDSA, + wantHash: AlgSHA512, + wantKeyBits: 256, + }, + "sm2_default_hash": { + sig: SignatureSM2{R: bigIntBits(256), S: bigIntBits(256)}, + hashAlgo: AlgUnknown, + wantSigScheme: AlgSM2, + wantHash: AlgSM3, + wantKeyBits: 256, + }, + "ecdsa_explicit_hash": { + sig: SignatureECDSA{R: bigIntBits(256), S: bigIntBits(256)}, + hashAlgo: AlgSHA384, + wantSigScheme: AlgECDSA, + wantHash: AlgSHA384, + wantKeyBits: 256, + }, + "unsupported_sig_type": { + sig: unsupportedSignatureData{}, + wantErr: true, + errSub: "unexpected signature type", + }, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + s := NewSignature() + err := s.SetSignatureByData(tt.sig, tt.hashAlgo) + if tt.wantErr { + if err == nil { + t.Errorf("Signature.SetSignatureByData(%T) error = nil, want non-nil", tt.sig) + } else if tt.errSub != "" && !strings.Contains(err.Error(), tt.errSub) { + t.Errorf("Signature.SetSignatureByData(%T) error = %q, want substring %q", tt.sig, err.Error(), tt.errSub) + } + return + } + + if err != nil { + t.Fatalf("Signature.SetSignatureByData(%T) error = %v, want nil", tt.sig, err) + } + if s.SigScheme != tt.wantSigScheme { + t.Errorf("Signature.SetSignatureByData(%T) SigScheme = %v, want %v", tt.sig, s.SigScheme, tt.wantSigScheme) + } + if s.HashAlg != tt.wantHash { + t.Errorf("Signature.SetSignatureByData(%T) HashAlg = %v, want %v", tt.sig, s.HashAlg, tt.wantHash) + } + if got := s.KeySize.InBits(); got != tt.wantKeyBits { + t.Errorf("Signature.SetSignatureByData(%T) KeySize bits = %d, want %d", tt.sig, got, tt.wantKeyBits) + } + }) + } +} + +func TestNewSignatureDataAndNewSignatureByData(t *testing.T) { + rsaKey := genRSA(t, 2048) + ecdsaKey := genECDSA(t) + data := []byte("signature-data") + + t.Run("NewSignatureData", func(t *testing.T) { + tests := map[string]struct { + signAlgo Algorithm + privKey crypto.Signer + wantType string + wantErr bool + errSub string + }{ + "auto_detect_rsa": { + signAlgo: 0, + privKey: rsaKey, + wantErr: true, + errSub: "is not implemented", + }, + "explicit_rsassa": { + signAlgo: AlgRSASSA, + privKey: rsaKey, + wantType: "cbnt.SignatureRSAASA", + }, + "ecdsa_wrong_key_type": { + signAlgo: AlgECDSA, + privKey: rsaKey, + wantErr: true, + errSub: "expected private ECDSA key", + }, + "unsupported_algo": { + signAlgo: AlgSHA1, + privKey: rsaKey, + wantErr: true, + errSub: "is not implemented", + }, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + sig, err := NewSignatureData(tt.signAlgo, tt.privKey, data) + if tt.wantErr { + if err == nil { + t.Errorf("NewSignatureData(%v, %T) error = nil, want non-nil", tt.signAlgo, tt.privKey) + } else if tt.errSub != "" && !strings.Contains(err.Error(), tt.errSub) { + t.Errorf("NewSignatureData(%v, %T) error = %q, want substring %q", tt.signAlgo, tt.privKey, err.Error(), tt.errSub) + } + return + } + + if err != nil { + t.Fatalf("NewSignatureData(%v, %T) error = %v, want nil", tt.signAlgo, tt.privKey, err) + } + if gotType := fmt.Sprintf("%T", sig); gotType != tt.wantType { + t.Errorf("NewSignatureData(%v, %T) type = %s, want %s", tt.signAlgo, tt.privKey, gotType, tt.wantType) + } + }) + } + }) + + t.Run("NewSignatureByData", func(t *testing.T) { + rawECDSA := append(bytes.Repeat([]byte{0xA5}, 32), bytes.Repeat([]byte{0x5A}, 32)...) + + tests := map[string]struct { + signAlgo Algorithm + pubKey crypto.PublicKey + input []byte + wantType string + wantErr bool + errSub string + }{ + "auto_detect_rsa": { + signAlgo: 0, + pubKey: &rsaKey.PublicKey, + input: []byte{1, 2, 3}, + wantType: "cbnt.SignatureRSAASA", + }, + "auto_detect_ecdsa": { + signAlgo: 0, + pubKey: &ecdsaKey.PublicKey, + input: rawECDSA, + wantType: "cbnt.SignatureECDSA", + }, + "explicit_pss": { + signAlgo: AlgRSAPSS, + pubKey: &rsaKey.PublicKey, + input: []byte{9, 8, 7}, + wantType: "cbnt.SignatureRSAPSS", + }, + "unsupported_algo": { + signAlgo: AlgSHA1, + pubKey: &rsaKey.PublicKey, + input: []byte{1}, + wantErr: true, + errSub: "is not implemented", + }, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + sig, err := NewSignatureByData(tt.signAlgo, tt.pubKey, tt.input) + if tt.wantErr { + if err == nil { + t.Errorf("NewSignatureByData(%v, %T) error = nil, want non-nil", tt.signAlgo, tt.pubKey) + } else if tt.errSub != "" && !strings.Contains(err.Error(), tt.errSub) { + t.Errorf("NewSignatureByData(%v, %T) error = %q, want substring %q", tt.signAlgo, tt.pubKey, err.Error(), tt.errSub) + } + return + } + + if err != nil { + t.Fatalf("NewSignatureByData(%v, %T) error = %v, want nil", tt.signAlgo, tt.pubKey, err) + } + if gotType := fmt.Sprintf("%T", sig); gotType != tt.wantType { + t.Errorf("NewSignatureByData(%v, %T) type = %s, want %s", tt.signAlgo, tt.pubKey, gotType, tt.wantType) + } + }) + } + }) +} + +func TestSignatureReadWriteRoundTrip(t *testing.T) { + t.Parallel() + + want := &Signature{ + SigScheme: AlgRSASSA, + Version: 0x10, + HashAlg: AlgSHA256, + Data: []byte{0x10, 0x11, 0x12, 0x13}, + } + want.KeySize.SetInBytes(uint16(len(want.Data))) + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("Signature.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("Signature.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got Signature + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("Signature.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("Signature.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got.SigScheme != want.SigScheme { + t.Errorf("Signature round-trip SigScheme = %v, want %v", got.SigScheme, want.SigScheme) + } + if got.KeySize != want.KeySize { + t.Errorf("Signature round-trip KeySize = %v, want %v", got.KeySize, want.KeySize) + } + if !bytes.Equal(got.Data, want.Data) { + t.Errorf("Signature round-trip Data = %v, want %v", got.Data, want.Data) + } +} + +func TestSignatureMethods(t *testing.T) { + t.Parallel() + + s := NewSignature() + if _, err := s.SizeOf(99); err == nil { + t.Errorf("Signature.SizeOf(99) error = nil, want non-nil") + } + if _, err := s.OffsetOf(99); err == nil { + t.Errorf("Signature.OffsetOf(99) error = nil, want non-nil") + } + + if got := len(s.Layout()); got != 5 { + t.Errorf("len(Signature.Layout()) = %d, want %d", got, 5) + } + + var nilSig *Signature + if got := nilSig.TotalSize(); got != 0 { + t.Errorf("(*Signature)(nil).TotalSize() = %d, want %d", got, 0) + } + + pretty := s.PrettyString(0, true) + if !strings.Contains(pretty, "Signature") { + t.Errorf("Signature.PrettyString() = %q, want to contain %q", pretty, "Signature") + } +} + +type unsupportedSignatureData struct{} + +func (unsupportedSignatureData) String() string { return "unsupported" } + +func (unsupportedSignatureData) Verify(pkIface crypto.PublicKey, hashAlgo Algorithm, signedData []byte) error { + return nil +} + +func genRSA(t *testing.T, bits int) *rsa.PrivateKey { + t.Helper() + + key, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + t.Fatalf("rsa.GenerateKey(%d) error = %v, want nil", bits, err) + } + + return key +} + +func genECDSA(t *testing.T) *ecdsa.PrivateKey { + t.Helper() + + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("ecdsa.GenerateKey(P256) error = %v, want nil", err) + } + + return key +} + +func bigIntBits(bits int) *big.Int { + b := make([]byte, bits/8) + b[0] = 0x80 + return new(big.Int).SetBytes(b) +} diff --git a/pkg/intel/metadata/cbnt/signature_types.go b/pkg/intel/metadata/cbnt/signature_types.go index 6ac96134..a790bb39 100644 --- a/pkg/intel/metadata/cbnt/signature_types.go +++ b/pkg/intel/metadata/cbnt/signature_types.go @@ -7,6 +7,7 @@ package cbnt import ( "crypto" "crypto/ecdsa" + "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/sha512" @@ -17,7 +18,10 @@ import ( "github.com/tjfoc/gmsm/sm2" ) -var sm2UID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38} +var ( + sm2UID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38} + RandReader = rand.Reader +) // NewSignatureData returns an implementation of SignatureDataInterface, // accordingly to signAlgo, privKey and signedData. diff --git a/pkg/intel/metadata/cbnt/signature_types_test.go b/pkg/intel/metadata/cbnt/signature_types_test.go new file mode 100644 index 00000000..23df6ebf --- /dev/null +++ b/pkg/intel/metadata/cbnt/signature_types_test.go @@ -0,0 +1,163 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/sha512" + "fmt" + "math/big" + "strings" + "testing" +) + +func TestSignatureRSAStringAndVerify(t *testing.T) { + tests := []struct { + name string + buildFunc func(*testing.T, *rsa.PrivateKey, []byte) ([]byte, SignatureDataInterface) + okHash Algorithm + badHash Algorithm + }{ + { + name: "RSAPSS", + buildFunc: func(t *testing.T, key *rsa.PrivateKey, data []byte) ([]byte, SignatureDataInterface) { + t.Helper() + h := sha512.New384() + if _, err := h.Write(data); err != nil { + t.Fatalf("hash.Write() error = %v, want nil", err) + } + digest := h.Sum(nil) + rawSig, err := rsa.SignPSS(rand.Reader, key, crypto.SHA384, digest, &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA384, + }) + if err != nil { + t.Fatalf("rsa.SignPSS() error = %v, want nil", err) + } + return rawSig, SignatureRSAPSS(rawSig) + }, + okHash: AlgSHA384, + badHash: AlgSHA512, + }, + { + name: "RSAASA", + buildFunc: func(t *testing.T, key *rsa.PrivateKey, data []byte) ([]byte, SignatureDataInterface) { + t.Helper() + h := sha256.New() + if _, err := h.Write(data); err != nil { + t.Fatalf("hash.Write() error = %v, want nil", err) + } + digest := h.Sum(nil) + rawSig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, digest) + if err != nil { + t.Fatalf("rsa.SignPKCS1v15() error = %v, want nil", err) + } + return rawSig, SignatureRSAASA(rawSig) + }, + okHash: AlgSHA256, + badHash: AlgSHA512, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() error = %v, want nil", err) + } + + wrongKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("ecdsa.GenerateKey() error = %v, want nil", err) + } + + data := []byte(strings.ToLower(tt.name) + "-signature-data") + rawSig, sig := tt.buildFunc(t, key, data) + + if got, want := sig.String(), fmt.Sprintf("0x%X", rawSig); got != want { + t.Errorf("%s.String() = %q, want %q", tt.name, got, want) + } + + verifyTests := []struct { + name string + pk crypto.PublicKey + hash Algorithm + msg []byte + wantErr bool + }{ + {name: "valid", pk: &key.PublicKey, hash: tt.okHash, msg: data, wantErr: false}, + {name: "unsupported_hash", pk: &key.PublicKey, hash: tt.badHash, msg: data, wantErr: true}, + {name: "wrong_key_type", pk: &wrongKey.PublicKey, hash: tt.okHash, msg: data, wantErr: true}, + {name: "tampered_data", pk: &key.PublicKey, hash: tt.okHash, msg: []byte("tampered"), wantErr: true}, + } + + for _, vt := range verifyTests { + vt := vt + t.Run(vt.name, func(t *testing.T) { + err := sig.Verify(vt.pk, vt.hash, vt.msg) + if vt.wantErr { + if err == nil { + t.Errorf("%s.Verify(%s) error = nil, want non-nil", tt.name, vt.name) + } + } else if err != nil { + t.Errorf("%s.Verify(%s) error = %v, want nil", tt.name, vt.name, err) + } + }) + } + }) + } +} + +func TestSignatureNonRSAStringAndVerify(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + sig SignatureDataInterface + hash Algorithm + wantString string + errSubstring string + }{ + { + name: "ECDSA", + sig: SignatureECDSA{R: big.NewInt(0x12), S: big.NewInt(0xAB)}, + hash: AlgSHA256, + wantString: "{R: 0x12, S: 0xAB}", + errSubstring: "not implemented", + }, + { + name: "SM2", + sig: SignatureSM2{R: big.NewInt(0x34), S: big.NewInt(0xCD)}, + hash: AlgSM3, + wantString: "{R: 0x34, S: 0xCD}", + errSubstring: "not implemented", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + if got := tt.sig.String(); got != tt.wantString { + t.Errorf("%s.String() = %q, want %q", tt.name, got, tt.wantString) + } + + err := tt.sig.Verify(nil, tt.hash, []byte("data")) + if err == nil { + t.Fatalf("%s.Verify() error = nil, want non-nil", tt.name) + } + if !strings.Contains(err.Error(), tt.errSubstring) { + t.Errorf("%s.Verify() error = %q, want substring %q", tt.name, err.Error(), tt.errSubstring) + } + }) + } +} diff --git a/pkg/intel/metadata/cbnt/structure.go b/pkg/intel/metadata/cbnt/structure.go index 59618a8b..94d206c2 100644 --- a/pkg/intel/metadata/cbnt/structure.go +++ b/pkg/intel/metadata/cbnt/structure.go @@ -1,70 +1,125 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate manifestcodegen - package cbnt import ( - "encoding/binary" + "fmt" "io" "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" ) -var ( - binaryOrder = binary.LittleEndian -) +// StructureID is the magic ID string used to identify the structure type +// in the manifest +type StructureID [8]byte -// StructInfo is the common part of any structure of a manifest -type StructInfo struct { +type StructInfoCBNT struct { + Common ID StructureID `json:"StructInfoID"` Version uint8 `json:"StructInfoVersion"` Variable0 uint8 `json:"StructInfoVariable0"` ElementSize uint16 `json:"StructInfoElementSize"` } -// StructInfo just returns StructInfo, it is a handy method if StructInfo -// is included anonymously to another type. -func (s StructInfo) StructInfo() StructInfo { - return s +// ReadFrom reads the StructInfo from 'r' in format defined in the document #575623. +func (s StructInfoCBNT) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil } -// StructureID is the magic ID string used to identify the structure type -// in the manifest -type StructureID [8]byte +// Validate (recursively) checks the structure if there are any unexpected values. +func (s StructInfoCBNT) Validate() error { + // ID, version and element size might differ, only thing that we can always validate is + // Variable0. + if s.Variable0 != 0 { + return fmt.Errorf("field 'Variable0' expects value '0', but has %v", s.Variable0) + } + return nil +} -// String returns the ID as a string. -func (s StructureID) String() string { - return string(s[:]) +// WriteTo writes the StructInfo into 'w' in format defined in +// the document #575623. +func (s StructInfoCBNT) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s StructInfoCBNT) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "ID", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.ID }, + Type: ManifestFieldArrayStatic, + }, + { + ID: 1, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Version }, + Type: ManifestFieldEndValue, + }, + { + ID: 2, + Name: "Variable 0", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Variable0 }, + Type: ManifestFieldEndValue, + }, + { + ID: 3, + Name: "Element Size", + Size: func() uint64 { return 2 }, + Value: func() any { return &s.ElementSize }, + Type: ManifestFieldEndValue, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s StructInfoCBNT) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("StructInfoCBnT: %v", err) + } + + return ret, nil } -// Structure is an abstraction of a structure of a manifest. -type Structure interface { - io.ReaderFrom - io.WriterTo - TotalSize() uint64 - // PrettyString returns the whole object as a structured string. - PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string +// OffsetOf returns the offset of the structure's field of a given id. +func (s StructInfoCBNT) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("StructInfoCBnT: %v", err) + } + + return ret, nil } -// Element is an abstraction of an element of a manifest. -type Element interface { - Structure - ReadDataFrom(r io.Reader) (int64, error) - GetStructInfo() StructInfo - SetStructInfo(StructInfo) +// Size returns the total size of the StructInfo. +func (s StructInfoCBNT) TotalSize() uint64 { + return s.Common.TotalSize(s) } -// ElementsContainer is an abstraction of set of elements of a manifest (for -// example: the root structure of BPM). -type ElementsContainer interface { - Structure - GetFieldByStructID(structID string) interface{} +// PrettyString returns the content of the structure in an easy-to-read format. +func (s StructInfoCBNT) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, s, "Struct Info", opts...) } -// Manifest is an abstract manifest. -type Manifest interface { - Structure +// StructInfo just returns StructInfo, it is a handy method if StructInfo +// is included anonymously to another type. +func (s StructInfoCBNT) StructInfo() StructInfo { + return s +} + +// String returns the ID as a string. +func (s StructureID) String() string { + return string(s[:]) } diff --git a/pkg/intel/metadata/cbnt/structure_bg.go b/pkg/intel/metadata/cbnt/structure_bg.go new file mode 100644 index 00000000..6d15956b --- /dev/null +++ b/pkg/intel/metadata/cbnt/structure_bg.go @@ -0,0 +1,96 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "fmt" + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +type StructInfoBG struct { + Common + ID StructureID `json:"StructInfoID"` + Version uint8 `json:"StructInfoVersion"` +} + +// ReadFrom reads the StructInfo from 'r' in format defined in the document #575623. +func (s StructInfoBG) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil +} + +// Validate (recursively) checks the structure if there are any unexpected values. +func (s StructInfoBG) Validate() error { + // dummy + return nil +} + +// WriteTo writes the StructInfo into 'w' in format defined in +// the document #575623. +func (s StructInfoBG) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor +func (s StructInfoBG) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "ID", + Size: func() uint64 { return 8 }, + Value: func() any { return &s.ID }, + Type: ManifestFieldArrayStatic, + }, + { + ID: 1, + Name: "Version", + Size: func() uint64 { return 1 }, + Value: func() any { return &s.Version }, + Type: ManifestFieldEndValue, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s StructInfoBG) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s StructInfoBG) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the StructInfo. +func (s StructInfoBG) TotalSize() uint64 { + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s StructInfoBG) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + return Common{}.PrettyString(depth, withHeader, s, "Struct Info", opts...) +} + +// StructInfo just returns StructInfo, it is a handy method if StructInfo +// is included anonymously to another type. +func (s StructInfoBG) StructInfo() StructInfo { + return s +} diff --git a/pkg/intel/metadata/cbnt/structure_manifestcodegen.go b/pkg/intel/metadata/cbnt/structure_manifestcodegen.go deleted file mode 100644 index ff575229..00000000 --- a/pkg/intel/metadata/cbnt/structure_manifestcodegen.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewStructInfo returns a new instance of StructInfo with -// all default values set. -func NewStructInfo() *StructInfo { - s := &StructInfo{} - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *StructInfo) Validate() error { - - return nil -} - -// ReadFrom reads the StructInfo from 'r' in format defined in the document #575623. -func (s *StructInfo) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // ID (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Read(r, binary.LittleEndian, s.ID[:]) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ID': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Version': %w", err) - } - totalN += int64(n) - } - - // Variable0 (ManifestFieldType: endValue) - { - n, err := 1, binary.Read(r, binary.LittleEndian, &s.Variable0) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Variable0': %w", err) - } - totalN += int64(n) - } - - // ElementSize (ManifestFieldType: endValue) - { - n, err := 2, binary.Read(r, binary.LittleEndian, &s.ElementSize) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'ElementSize': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *StructInfo) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *StructInfo) Rehash() { -} - -// WriteTo writes the StructInfo into 'w' in format defined in -// the document #575623. -func (s *StructInfo) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // ID (ManifestFieldType: arrayStatic) - { - n, err := 8, binary.Write(w, binary.LittleEndian, s.ID[:]) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ID': %w", err) - } - totalN += int64(n) - } - - // Version (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Version': %w", err) - } - totalN += int64(n) - } - - // Variable0 (ManifestFieldType: endValue) - { - n, err := 1, binary.Write(w, binary.LittleEndian, &s.Variable0) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Variable0': %w", err) - } - totalN += int64(n) - } - - // ElementSize (ManifestFieldType: endValue) - { - n, err := 2, binary.Write(w, binary.LittleEndian, &s.ElementSize) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'ElementSize': %w", err) - } - totalN += int64(n) - } - - return totalN, nil -} - -// IDSize returns the size in bytes of the value of field ID -func (s *StructInfo) IDTotalSize() uint64 { - return 8 -} - -// VersionSize returns the size in bytes of the value of field Version -func (s *StructInfo) VersionTotalSize() uint64 { - return 1 -} - -// Variable0Size returns the size in bytes of the value of field Variable0 -func (s *StructInfo) Variable0TotalSize() uint64 { - return 1 -} - -// ElementSizeSize returns the size in bytes of the value of field ElementSize -func (s *StructInfo) ElementSizeTotalSize() uint64 { - return 2 -} - -// IDOffset returns the offset in bytes of field ID -func (s *StructInfo) IDOffset() uint64 { - return 0 -} - -// VersionOffset returns the offset in bytes of field Version -func (s *StructInfo) VersionOffset() uint64 { - return s.IDOffset() + s.IDTotalSize() -} - -// Variable0Offset returns the offset in bytes of field Variable0 -func (s *StructInfo) Variable0Offset() uint64 { - return s.VersionOffset() + s.VersionTotalSize() -} - -// ElementSizeOffset returns the offset in bytes of field ElementSize -func (s *StructInfo) ElementSizeOffset() uint64 { - return s.Variable0Offset() + s.Variable0TotalSize() -} - -// Size returns the total size of the StructInfo. -func (s *StructInfo) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.IDTotalSize() - size += s.VersionTotalSize() - size += s.Variable0TotalSize() - size += s.ElementSizeTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *StructInfo) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "Struct Info", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is arrayStatic - lines = append(lines, pretty.SubValue(depth+1, "ID", "", &s.ID, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Variable 0", "", &s.Variable0, opts...)...) - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Element Size", "", &s.ElementSize, opts...)...) - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} diff --git a/pkg/intel/metadata/cbnt/structure_test.go b/pkg/intel/metadata/cbnt/structure_test.go new file mode 100644 index 00000000..1f2a46bc --- /dev/null +++ b/pkg/intel/metadata/cbnt/structure_test.go @@ -0,0 +1,201 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "reflect" + "strings" + "testing" +) + +func TestNewStructInfo(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + bgv BootGuardVersion + wantType string + wantNil bool + }{ + "version_1_0": {bgv: Version10, wantType: "*cbnt.StructInfoBG"}, + "version_2_0": {bgv: Version20, wantType: "*cbnt.StructInfoCBNT"}, + "version_2_1": {bgv: Version21, wantType: "*cbnt.StructInfoCBNT"}, + "unknown": {bgv: BootGuardVersion(0xFF), wantNil: true}, + } + + for name, tT := range tests { + name := name + tT := tT + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := NewStructInfo(tT.bgv) + if tT.wantNil { + if got != nil { + t.Errorf("NewStructInfo(%v) = %T, want nil", tT.bgv, got) + } + return + } + + if got == nil { + t.Fatalf("NewStructInfo(%v) = nil, want non-nil", tT.bgv) + } + + if gotType := reflect.TypeOf(got).String(); gotType != tT.wantType { + t.Errorf("NewStructInfo(%v) type = %s, want %s", tT.bgv, gotType, tT.wantType) + } + }) + } +} + +func TestStructInfoBGMethods(t *testing.T) { + t.Parallel() + + s := StructInfoBG{ID: StructureID{'S', 'T', 'R', 'U', 'C', 'T', 'B', 'G'}, Version: 0x10} + + if err := s.Validate(); err != nil { + t.Errorf("StructInfoBG.Validate() error = %v, want nil", err) + } + + if got := len(s.Layout()); got != 2 { + t.Errorf("len(StructInfoBG.Layout()) = %d, want %d", got, 2) + } + + if got := s.TotalSize(); got != 9 { + t.Errorf("StructInfoBG.TotalSize() = %d, want %d", got, 9) + } + + size0, err := s.SizeOf(0) + if err != nil { + t.Fatalf("StructInfoBG.SizeOf(0) error = %v, want nil", err) + } + if size0 != 8 { + t.Errorf("StructInfoBG.SizeOf(0) = %d, want %d", size0, 8) + } + + offset1, err := s.OffsetOf(1) + if err != nil { + t.Fatalf("StructInfoBG.OffsetOf(1) error = %v, want nil", err) + } + if offset1 != 8 { + t.Errorf("StructInfoBG.OffsetOf(1) = %d, want %d", offset1, 8) + } + + if _, err := s.SizeOf(99); err == nil { + t.Errorf("StructInfoBG.SizeOf(99) error = nil, want non-nil") + } + if _, err := s.OffsetOf(99); err == nil { + t.Errorf("StructInfoBG.OffsetOf(99) error = nil, want non-nil") + } + + var buf bytes.Buffer + n, err := s.WriteTo(&buf) + if err != nil { + t.Fatalf("StructInfoBG.WriteTo() error = %v, want nil", err) + } + if n != int64(s.TotalSize()) { + t.Errorf("StructInfoBG.WriteTo() bytes = %d, want %d", n, s.TotalSize()) + } + + var readTarget StructInfoBG + n, err = readTarget.ReadFrom(bytes.NewReader(buf.Bytes())) + if err != nil { + t.Fatalf("StructInfoBG.ReadFrom() error = %v, want nil", err) + } + if n != int64(s.TotalSize()) { + t.Errorf("StructInfoBG.ReadFrom() bytes = %d, want %d", n, s.TotalSize()) + } + + if got := s.StructInfo(); reflect.TypeOf(got).String() != "cbnt.StructInfoBG" { + t.Errorf("StructInfoBG.StructInfo() type = %T, want cbnt.StructInfoBG", got) + } + + pretty := s.PrettyString(0, true) + if !strings.Contains(pretty, "Struct Info") { + t.Errorf("StructInfoBG.PrettyString() = %q, want to contain %q", pretty, "Struct Info") + } +} + +func TestStructInfoCBNTMethods(t *testing.T) { + t.Parallel() + + s := StructInfoCBNT{ + ID: StructureID{'S', 'T', 'R', 'U', 'C', 'T', '2', '0'}, + Version: 0x20, + Variable0: 0, + ElementSize: 0x0020, + } + + if err := s.Validate(); err != nil { + t.Errorf("StructInfoCBNT.Validate() error = %v, want nil", err) + } + + if got := len(s.Layout()); got != 4 { + t.Errorf("len(StructInfoCBNT.Layout()) = %d, want %d", got, 4) + } + + if got := s.TotalSize(); got != 12 { + t.Errorf("StructInfoCBNT.TotalSize() = %d, want %d", got, 12) + } + + size3, err := s.SizeOf(3) + if err != nil { + t.Fatalf("StructInfoCBNT.SizeOf(3) error = %v, want nil", err) + } + if size3 != 2 { + t.Errorf("StructInfoCBNT.SizeOf(3) = %d, want %d", size3, 2) + } + + offset3, err := s.OffsetOf(3) + if err != nil { + t.Fatalf("StructInfoCBNT.OffsetOf(3) error = %v, want nil", err) + } + if offset3 != 10 { + t.Errorf("StructInfoCBNT.OffsetOf(3) = %d, want %d", offset3, 10) + } + + if _, err := s.SizeOf(99); err == nil { + t.Errorf("StructInfoCBNT.SizeOf(99) error = nil, want non-nil") + } + if _, err := s.OffsetOf(99); err == nil { + t.Errorf("StructInfoCBNT.OffsetOf(99) error = nil, want non-nil") + } + + var buf bytes.Buffer + n, err := s.WriteTo(&buf) + if err != nil { + t.Fatalf("StructInfoCBNT.WriteTo() error = %v, want nil", err) + } + if n != int64(s.TotalSize()) { + t.Errorf("StructInfoCBNT.WriteTo() bytes = %d, want %d", n, s.TotalSize()) + } + + var readTarget StructInfoCBNT + n, err = readTarget.ReadFrom(bytes.NewReader(buf.Bytes())) + if err != nil { + t.Fatalf("StructInfoCBNT.ReadFrom() error = %v, want nil", err) + } + if n != int64(s.TotalSize()) { + t.Errorf("StructInfoCBNT.ReadFrom() bytes = %d, want %d", n, s.TotalSize()) + } + + if got := s.StructInfo(); reflect.TypeOf(got).String() != "cbnt.StructInfoCBNT" { + t.Errorf("StructInfoCBNT.StructInfo() type = %T, want cbnt.StructInfoCBNT", got) + } + + pretty := s.PrettyString(0, true) + if !strings.Contains(pretty, "Struct Info") { + t.Errorf("StructInfoCBNT.PrettyString() = %q, want to contain %q", pretty, "Struct Info") + } +} + +func TestStructureIDString(t *testing.T) { + t.Parallel() + + id := StructureID{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'} + if got, want := id.String(), "ABCDEFGH"; got != want { + t.Errorf("StructureID.String() = %q, want %q", got, want) + } +} diff --git a/pkg/intel/metadata/cbnt/tpm_info_list.go b/pkg/intel/metadata/cbnt/tpm_info_list.go index 9456fc7a..be64d35e 100644 --- a/pkg/intel/metadata/cbnt/tpm_info_list.go +++ b/pkg/intel/metadata/cbnt/tpm_info_list.go @@ -1,44 +1,172 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate manifestcodegen - package cbnt -// TPM2PCRExtendPolicySupport defined TPM2 PCR Extend policy support. -type TPM2PCRExtendPolicySupport uint8 +import ( + "encoding/binary" + "fmt" + "io" + "strings" -// Possible values of TPM2PCRExtendPolicySupport -const ( - TPM2PCRExtendIllegal TPM2PCRExtendPolicySupport = 0 - TPM2PCRExtendMaximumAgilityPolicy TPM2PCRExtendPolicySupport = 1 - TPM2PCRExtendMaximumPerformancePolicy TPM2PCRExtendPolicySupport = 2 - TPM2PCRExtendBothPolicies TPM2PCRExtendPolicySupport = 3 + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" ) -// TPMFamilySupport defines TPM family support -type TPMFamilySupport uint8 +// TPMInfoList represents TPM capabilities supported by ACM +type TPMInfoList struct { + Common + Capabilities TPMCapabilities + Algorithms []Algorithm +} -// IsDiscreteTPM12Supported returns true if discrete TPM1.2 is supported. -// PrettyString-true: Discrete TPM1.2 is supported -// PrettyString-false: Discrete TPM1.2 is not supported -func (familySupport TPMFamilySupport) IsDiscreteTPM12Supported() bool { - return familySupport&1 != 0 +// NewTPMInfoList returns a new instance of TPMInfoList with +// all default values set. +func NewTPMInfoList() *TPMInfoList { + s := &TPMInfoList{} + return s } -// IsDiscreteTPM20Supported returns true if discrete TPM2.0 is supported. -// PrettyString-true: Discrete TPM2.0 is supported -// PrettyString-false: Discrete TPM2.0 is not supported -func (familySupport TPMFamilySupport) IsDiscreteTPM20Supported() bool { - return familySupport&2 != 0 +// ReadFrom reads the TPMInfoList from 'r' in format defined in the document #575623. +func (s *TPMInfoList) ReadFrom(r io.Reader) (int64, error) { + totalN, err := s.Common.ReadFrom(r, s) + if err != nil { + return 0, err + } + + return totalN, nil } -// IsFirmwareTPM20Supported returns true if firmware TPM2.0 is supported. -// PrettyString-true: Firmware TPM2.0 is supported -// PrettyString-false: Firmware TPM2.0 is not supported -func (familySupport TPMFamilySupport) IsFirmwareTPM20Supported() bool { - return familySupport&(1<<3) != 0 +// WriteTo writes the TPMInfoList into 'w' in format defined in +// the document #575623. +func (s *TPMInfoList) WriteTo(w io.Writer) (int64, error) { + return s.Common.WriteTo(w, s) +} + +// Layout returns the structure's layout descriptor. +func (s *TPMInfoList) Layout() []LayoutField { + return []LayoutField{ + { + ID: 0, + Name: "Capabilities", + Size: func() uint64 { return 4 }, + Value: func() any { return &s.Capabilities }, + Type: ManifestFieldEndValue, + }, + { + ID: 1, + Name: fmt.Sprintf("Algorithms: Array of \"TPM Info List\" of length %d", len(s.Algorithms)), + Size: func() uint64 { + size := uint64(binary.Size(uint16(0))) + for idx := range s.Algorithms { + size += s.Algorithms[idx].TotalSize() + } + return size + }, + Value: func() any { return &s.Algorithms }, + Type: ManifestFieldList, + ReadList: func(r io.Reader) (int64, error) { + var count uint16 + err := binary.Read(r, binary.LittleEndian, &count) + if err != nil { + return 0, fmt.Errorf("unable to read the count for field 'Algorithms': %w", err) + } + totalN := int64(binary.Size(count)) + s.Algorithms = make([]Algorithm, count) + + for idx := range s.Algorithms { + n, err := s.Algorithms[idx].ReadFrom(r) + if err != nil { + return totalN, fmt.Errorf("unable to read field 'Algorithms[%d]': %w", idx, err) + } + totalN += int64(n) + } + return totalN, nil + }, + WriteList: func(w io.Writer) (int64, error) { + count := uint16(len(s.Algorithms)) + if err := binary.Write(w, binary.LittleEndian, &count); err != nil { + return 0, fmt.Errorf("unable to write the count for field 'Algorithms': %w", err) + } + totalN := int64(binary.Size(count)) + + for idx := range s.Algorithms { + n, err := s.Algorithms[idx].WriteTo(w) + if err != nil { + return totalN, fmt.Errorf("unable to write field 'Algorithms[%d]': %w", idx, err) + } + totalN += int64(n) + } + + return totalN, nil + }, + }, + } +} + +// SizeOf returns the size of the structure's field of a given id. +func (s *TPMInfoList) SizeOf(id int) (uint64, error) { + ret, err := s.Common.SizeOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// OffsetOf returns the offset of the structure's field of a given id. +func (s *TPMInfoList) OffsetOf(id int) (uint64, error) { + ret, err := s.Common.OffsetOf(s, id) + if err != nil { + return ret, fmt.Errorf("HashList: %v", err) + } + + return ret, nil +} + +// Size returns the total size of the manifest +func (s *TPMInfoList) TotalSize() uint64 { + if s == nil { + return 0 + } + + return s.Common.TotalSize(s) +} + +// PrettyString returns the content of the structure in an easy-to-read format. +func (s *TPMInfoList) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + result := Common{}.PrettyString(depth, withHeader, s, "TPM Info List", opts...) + if depth < 1 { + return result + "\n" + } + return result +} + +// TPM2PCRExtendPolicySupport defined TPM2 PCR Extend policy support. +type TPM2PCRExtendPolicySupport uint8 + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (v TPM2PCRExtendPolicySupport) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "TPM 2 PCR Extend Policy Support", v)) + } + return strings.Join(lines, "\n") +} + +// TotalSize returns the total size measured through binary.Size. +func (v TPM2PCRExtendPolicySupport) TotalSize() uint64 { + return uint64(binary.Size(v)) +} + +// WriteTo writes the TPM2PCRExtendPolicySupport into 'w' in binary format. +func (v TPM2PCRExtendPolicySupport) WriteTo(w io.Writer) (int64, error) { + return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) +} + +// ReadFrom reads the TPM2PCRExtendPolicySupport from 'r' in binary format. +func (v TPM2PCRExtendPolicySupport) ReadFrom(r io.Reader) (int64, error) { + return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) } // TPMCapabilities defines TPM capabilities @@ -54,8 +182,91 @@ func (cap TPMCapabilities) TPMFamilySupport() TPMFamilySupport { return TPMFamilySupport((cap >> 2) & 15) } -// TPMInfoList represents TPM capabilities supported by ACM -type TPMInfoList struct { - Capabilities TPMCapabilities - Algorithms []Algorithm +// PrettyString returns the bits of the flags in an easy-to-read format. +func (cap TPMCapabilities) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "TPM Capabilities", cap)) + } + lines = append(lines, pretty.SubValue(depth+1, "TPM 2 PCR Extend Policy Support", "", cap.TPM2PCRExtendPolicySupport(), opts...)...) + lines = append(lines, pretty.SubValue(depth+1, "TPM Family Support", "", cap.TPMFamilySupport(), opts...)...) + return strings.Join(lines, "\n") +} + +// TotalSize returns the total size measured through binary.Size. +func (cap TPMCapabilities) TotalSize() uint64 { + return uint64(binary.Size(cap)) +} + +// WriteTo writes the TPMCapabilities into 'w' in binary format. +func (cap TPMCapabilities) WriteTo(w io.Writer) (int64, error) { + return int64(cap.TotalSize()), binary.Write(w, binary.LittleEndian, cap) +} + +// ReadFrom reads the TPMCapabilities from 'r' in binary format. +func (cap TPMCapabilities) ReadFrom(r io.Reader) (int64, error) { + return int64(cap.TotalSize()), binary.Read(r, binary.LittleEndian, cap) +} + +// TPMFamilySupport defines TPM family support +type TPMFamilySupport uint8 + +// PrettyString returns the bits of the flags in an easy-to-read format. +func (v TPMFamilySupport) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { + var lines []string + if withHeader { + lines = append(lines, pretty.Header(depth, "TPM Family Support", v)) + } + if v.IsDiscreteTPM12Supported() { + lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 12 Supported", "Discrete TPM1.2 is supported", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 12 Supported", "Discrete TPM1.2 is not supported", false, opts...)...) + } + if v.IsDiscreteTPM20Supported() { + lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 20 Supported", "Discrete TPM2.0 is supported", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 20 Supported", "Discrete TPM2.0 is not supported", false, opts...)...) + } + if v.IsFirmwareTPM20Supported() { + lines = append(lines, pretty.SubValue(depth+1, "Is Firmware TPM 20 Supported", "Firmware TPM2.0 is supported", true, opts...)...) + } else { + lines = append(lines, pretty.SubValue(depth+1, "Is Firmware TPM 20 Supported", "Firmware TPM2.0 is not supported", false, opts...)...) + } + return strings.Join(lines, "\n") +} + +// TotalSize returns the total size measured through binary.Size. +func (v TPMFamilySupport) TotalSize() uint64 { + return uint64(binary.Size(v)) +} + +// WriteTo writes the TPMFamilySupport into 'w' in binary format. +func (v TPMFamilySupport) WriteTo(w io.Writer) (int64, error) { + return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) +} + +// ReadFrom reads the TPMFamilySupport from 'r' in binary format. +func (v TPMFamilySupport) ReadFrom(r io.Reader) (int64, error) { + return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) +} + +// IsDiscreteTPM12Supported returns true if discrete TPM1.2 is supported. +// PrettyString-true: Discrete TPM1.2 is supported +// PrettyString-false: Discrete TPM1.2 is not supported +func (v TPMFamilySupport) IsDiscreteTPM12Supported() bool { + return v&1 != 0 +} + +// IsDiscreteTPM20Supported returns true if discrete TPM2.0 is supported. +// PrettyString-true: Discrete TPM2.0 is supported +// PrettyString-false: Discrete TPM2.0 is not supported +func (v TPMFamilySupport) IsDiscreteTPM20Supported() bool { + return v&2 != 0 +} + +// IsFirmwareTPM20Supported returns true if firmware TPM2.0 is supported. +// PrettyString-true: Firmware TPM2.0 is supported +// PrettyString-false: Firmware TPM2.0 is not supported +func (v TPMFamilySupport) IsFirmwareTPM20Supported() bool { + return v&(1<<3) != 0 } diff --git a/pkg/intel/metadata/cbnt/tpm_info_list_manifestcodegen.go b/pkg/intel/metadata/cbnt/tpm_info_list_manifestcodegen.go deleted file mode 100644 index e68bf58d..00000000 --- a/pkg/intel/metadata/cbnt/tpm_info_list_manifestcodegen.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !manifestcodegen -// +build !manifestcodegen - -// Code generated by "menifestcodegen". DO NOT EDIT. -// To reproduce: go run github.com/linuxboot/fiano/pkg/intel/metadata/common/manifestcodegen/cmd/manifestcodegen github.com/linuxboot/fiano/pkg/intel/metadata/cbnt - -package cbnt - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" -) - -var ( - // Just to avoid errors in "import" above in case if it wasn't used below - _ = binary.LittleEndian - _ = (fmt.Stringer)(nil) - _ = (io.Reader)(nil) - _ = pretty.Header - _ = strings.Join -) - -// NewTPMInfoList returns a new instance of TPMInfoList with -// all default values set. -func NewTPMInfoList() *TPMInfoList { - s := &TPMInfoList{} - s.Rehash() - return s -} - -// Validate (recursively) checks the structure if there are any unexpected -// values. It returns an error if so. -func (s *TPMInfoList) Validate() error { - - return nil -} - -// ReadFrom reads the TPMInfoList from 'r' in format defined in the document #575623. -func (s *TPMInfoList) ReadFrom(r io.Reader) (int64, error) { - totalN := int64(0) - - // Capabilities (ManifestFieldType: endValue) - { - n, err := 4, binary.Read(r, binary.LittleEndian, &s.Capabilities) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Capabilities': %w", err) - } - totalN += int64(n) - } - - // Algorithms (ManifestFieldType: list) - { - var count uint16 - err := binary.Read(r, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to read the count for field 'Algorithms': %w", err) - } - totalN += int64(binary.Size(count)) - s.Algorithms = make([]Algorithm, count) - - for idx := range s.Algorithms { - n, err := s.Algorithms[idx].ReadFrom(r) - if err != nil { - return totalN, fmt.Errorf("unable to read field 'Algorithms[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// RehashRecursive calls Rehash (see below) recursively. -func (s *TPMInfoList) RehashRecursive() { - s.Rehash() -} - -// Rehash sets values which are calculated automatically depending on the rest -// data. It is usually about the total size field of an element. -func (s *TPMInfoList) Rehash() { -} - -// WriteTo writes the TPMInfoList into 'w' in format defined in -// the document #575623. -func (s *TPMInfoList) WriteTo(w io.Writer) (int64, error) { - totalN := int64(0) - s.Rehash() - - // Capabilities (ManifestFieldType: endValue) - { - n, err := 4, binary.Write(w, binary.LittleEndian, &s.Capabilities) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Capabilities': %w", err) - } - totalN += int64(n) - } - - // Algorithms (ManifestFieldType: list) - { - count := uint16(len(s.Algorithms)) - err := binary.Write(w, binary.LittleEndian, &count) - if err != nil { - return totalN, fmt.Errorf("unable to write the count for field 'Algorithms': %w", err) - } - totalN += int64(binary.Size(count)) - for idx := range s.Algorithms { - n, err := s.Algorithms[idx].WriteTo(w) - if err != nil { - return totalN, fmt.Errorf("unable to write field 'Algorithms[%d]': %w", idx, err) - } - totalN += int64(n) - } - } - - return totalN, nil -} - -// CapabilitiesSize returns the size in bytes of the value of field Capabilities -func (s *TPMInfoList) CapabilitiesTotalSize() uint64 { - return 4 -} - -// AlgorithmsSize returns the size in bytes of the value of field Algorithms -func (s *TPMInfoList) AlgorithmsTotalSize() uint64 { - var size uint64 - size += uint64(binary.Size(uint16(0))) - for idx := range s.Algorithms { - size += s.Algorithms[idx].TotalSize() - } - return size -} - -// CapabilitiesOffset returns the offset in bytes of field Capabilities -func (s *TPMInfoList) CapabilitiesOffset() uint64 { - return 0 -} - -// AlgorithmsOffset returns the offset in bytes of field Algorithms -func (s *TPMInfoList) AlgorithmsOffset() uint64 { - return s.CapabilitiesOffset() + s.CapabilitiesTotalSize() -} - -// Size returns the total size of the TPMInfoList. -func (s *TPMInfoList) TotalSize() uint64 { - if s == nil { - return 0 - } - - var size uint64 - size += s.CapabilitiesTotalSize() - size += s.AlgorithmsTotalSize() - return size -} - -// PrettyString returns the content of the structure in an easy-to-read format. -func (s *TPMInfoList) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "TPM Info List", s)) - } - if s == nil { - return strings.Join(lines, "\n") - } - // ManifestFieldType is endValue - lines = append(lines, pretty.SubValue(depth+1, "Capabilities", "", &s.Capabilities, opts...)...) - // ManifestFieldType is list - lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("Algorithms: Array of \"TPM Info List\" of length %d", len(s.Algorithms)), s.Algorithms)) - for i := 0; i < len(s.Algorithms); i++ { - lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.Algorithms[i].PrettyString(depth+2, true))) - } - if depth < 1 { - lines = append(lines, "") - } - if depth < 2 { - lines = append(lines, "") - } - return strings.Join(lines, "\n") -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v TPM2PCRExtendPolicySupport) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "TPM 2 PCR Extend Policy Support", v)) - } - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v TPM2PCRExtendPolicySupport) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the TPM2PCRExtendPolicySupport into 'w' in binary format. -func (v TPM2PCRExtendPolicySupport) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the TPM2PCRExtendPolicySupport from 'r' in binary format. -func (v TPM2PCRExtendPolicySupport) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v TPMCapabilities) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "TPM Capabilities", v)) - } - lines = append(lines, pretty.SubValue(depth+1, "TPM 2 PCR Extend Policy Support", "", v.TPM2PCRExtendPolicySupport(), opts...)...) - lines = append(lines, pretty.SubValue(depth+1, "TPM Family Support", "", v.TPMFamilySupport(), opts...)...) - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v TPMCapabilities) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the TPMCapabilities into 'w' in binary format. -func (v TPMCapabilities) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the TPMCapabilities from 'r' in binary format. -func (v TPMCapabilities) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} - -// PrettyString returns the bits of the flags in an easy-to-read format. -func (v TPMFamilySupport) PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string { - var lines []string - if withHeader { - lines = append(lines, pretty.Header(depth, "TPM Family Support", v)) - } - if v.IsDiscreteTPM12Supported() { - lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 12 Supported", "Discrete TPM1.2 is supported", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 12 Supported", "Discrete TPM1.2 is not supported", false, opts...)...) - } - if v.IsDiscreteTPM20Supported() { - lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 20 Supported", "Discrete TPM2.0 is supported", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Is Discrete TPM 20 Supported", "Discrete TPM2.0 is not supported", false, opts...)...) - } - if v.IsFirmwareTPM20Supported() { - lines = append(lines, pretty.SubValue(depth+1, "Is Firmware TPM 20 Supported", "Firmware TPM2.0 is supported", true, opts...)...) - } else { - lines = append(lines, pretty.SubValue(depth+1, "Is Firmware TPM 20 Supported", "Firmware TPM2.0 is not supported", false, opts...)...) - } - return strings.Join(lines, "\n") -} - -// TotalSize returns the total size measured through binary.Size. -func (v TPMFamilySupport) TotalSize() uint64 { - return uint64(binary.Size(v)) -} - -// WriteTo writes the TPMFamilySupport into 'w' in binary format. -func (v TPMFamilySupport) WriteTo(w io.Writer) (int64, error) { - return int64(v.TotalSize()), binary.Write(w, binary.LittleEndian, v) -} - -// ReadFrom reads the TPMFamilySupport from 'r' in binary format. -func (v TPMFamilySupport) ReadFrom(r io.Reader) (int64, error) { - return int64(v.TotalSize()), binary.Read(r, binary.LittleEndian, v) -} diff --git a/pkg/intel/metadata/cbnt/tpm_info_list_test.go b/pkg/intel/metadata/cbnt/tpm_info_list_test.go new file mode 100644 index 00000000..861f6e9c --- /dev/null +++ b/pkg/intel/metadata/cbnt/tpm_info_list_test.go @@ -0,0 +1,245 @@ +// Copyright 2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "bytes" + "encoding/binary" + "strings" + "testing" +) + +func TestTPMInfoListNew(t *testing.T) { + t.Parallel() + + tpm := NewTPMInfoList() + if tpm == nil { + t.Fatal("NewTPMInfoList() = nil, want non-nil") + } + if tpm.Capabilities != 0 { + t.Errorf("NewTPMInfoList().Capabilities = %d, want %d", tpm.Capabilities, 0) + } + if len(tpm.Algorithms) != 0 { + t.Errorf("len(NewTPMInfoList().Algorithms) = %d, want %d", len(tpm.Algorithms), 0) + } +} + +func TestTPMInfoListReadWriteRoundTrip(t *testing.T) { + t.Parallel() + + want := &TPMInfoList{ + Capabilities: TPMCapabilities(0x3D), + Algorithms: []Algorithm{AlgSHA256, AlgSHA384, AlgSM3}, + } + + var buf bytes.Buffer + n, err := want.WriteTo(&buf) + if err != nil { + t.Fatalf("TPMInfoList.WriteTo() error = %v, want nil", err) + } + if n != int64(want.TotalSize()) { + t.Errorf("TPMInfoList.WriteTo() bytes = %d, want %d", n, want.TotalSize()) + } + + var got TPMInfoList + n, err = got.ReadFrom(&buf) + if err != nil { + t.Fatalf("TPMInfoList.ReadFrom() error = %v, want nil", err) + } + if n != int64(got.TotalSize()) { + t.Errorf("TPMInfoList.ReadFrom() bytes = %d, want %d", n, got.TotalSize()) + } + + if got.Capabilities != want.Capabilities { + t.Errorf("TPMInfoList round-trip Capabilities = 0x%x, want 0x%x", got.Capabilities, want.Capabilities) + } + if len(got.Algorithms) != len(want.Algorithms) { + t.Fatalf("len(TPMInfoList round-trip Algorithms) = %d, want %d", len(got.Algorithms), len(want.Algorithms)) + } + for idx := range want.Algorithms { + if got.Algorithms[idx] != want.Algorithms[idx] { + t.Errorf("TPMInfoList round-trip Algorithms[%d] = %v, want %v", idx, got.Algorithms[idx], want.Algorithms[idx]) + } + } +} + +func TestTPMInfoListMethods(t *testing.T) { + t.Parallel() + + tpm := &TPMInfoList{Algorithms: []Algorithm{AlgSHA256}} + + size0, err := tpm.SizeOf(0) + if err != nil { + t.Fatalf("TPMInfoList.SizeOf(0) error = %v, want nil", err) + } + if size0 != 4 { + t.Errorf("TPMInfoList.SizeOf(0) = %d, want %d", size0, 4) + } + + offset1, err := tpm.OffsetOf(1) + if err != nil { + t.Fatalf("TPMInfoList.OffsetOf(1) error = %v, want nil", err) + } + if offset1 != 4 { + t.Errorf("TPMInfoList.OffsetOf(1) = %d, want %d", offset1, 4) + } + + if _, err := tpm.SizeOf(99); err == nil { + t.Errorf("TPMInfoList.SizeOf(99) error = nil, want non-nil") + } + if _, err := tpm.OffsetOf(99); err == nil { + t.Errorf("TPMInfoList.OffsetOf(99) error = nil, want non-nil") + } + + var nilTPM *TPMInfoList + if got := nilTPM.TotalSize(); got != 0 { + t.Errorf("(*TPMInfoList)(nil).TotalSize() = %d, want %d", got, 0) + } + + pretty := tpm.PrettyString(0, true) + if !strings.Contains(pretty, "TPM Info List") { + t.Errorf("TPMInfoList.PrettyString() = %q, want to contain %q", pretty, "TPM Info List") + } +} + +func TestTPMCapabilitiesAccessors(t *testing.T) { + t.Parallel() + + cap := TPMCapabilities(0) + cap |= TPMCapabilities(TPM2PCRExtendBothPolicies) + cap |= TPMCapabilities(0x0D << 2) + + if got := cap.TPM2PCRExtendPolicySupport(); got != TPM2PCRExtendBothPolicies { + t.Errorf("TPMCapabilities.TPM2PCRExtendPolicySupport() = %v, want %v", got, TPM2PCRExtendBothPolicies) + } + + family := cap.TPMFamilySupport() + if !family.IsDiscreteTPM12Supported() { + t.Errorf("TPMFamilySupport.IsDiscreteTPM12Supported() = false, want true") + } + if !family.IsFirmwareTPM20Supported() { + t.Errorf("TPMFamilySupport.IsFirmwareTPM20Supported() = false, want true") + } + if family.IsDiscreteTPM20Supported() { + t.Errorf("TPMFamilySupport.IsDiscreteTPM20Supported() = true, want false") + } +} + +func TestTPMSimpleTypesReadWriteRoundTrip(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + write func(*bytes.Buffer) (int64, error) + read func(*bytes.Buffer) (any, error) + readFrom func(*bytes.Buffer) (int64, error) + wantSize int64 + want any + wantErr bool + errSub string + }{ + "TPM2PCRExtendPolicySupport": { + write: func(buf *bytes.Buffer) (int64, error) { + v := TPM2PCRExtendPolicySupport(0x12) + return v.WriteTo(buf) + }, + read: func(buf *bytes.Buffer) (any, error) { + var v TPM2PCRExtendPolicySupport + if err := binary.Read(buf, binary.LittleEndian, &v); err != nil { + return nil, err + } + return v, nil + }, + readFrom: func(buf *bytes.Buffer) (int64, error) { + var v TPM2PCRExtendPolicySupport + return (&v).ReadFrom(buf) + }, + wantSize: int64(TPM2PCRExtendPolicySupport(0).TotalSize()), + want: TPM2PCRExtendPolicySupport(0x12), + wantErr: true, + errSub: "invalid type", + }, + "TPMCapabilities": { + write: func(buf *bytes.Buffer) (int64, error) { + v := TPMCapabilities(0x1234ABCD) + return v.WriteTo(buf) + }, + read: func(buf *bytes.Buffer) (any, error) { + var v TPMCapabilities + if err := binary.Read(buf, binary.LittleEndian, &v); err != nil { + return nil, err + } + return v, nil + }, + readFrom: func(buf *bytes.Buffer) (int64, error) { + var v TPMCapabilities + return (&v).ReadFrom(buf) + }, + wantSize: int64(TPMCapabilities(0).TotalSize()), + want: TPMCapabilities(0x1234ABCD), + wantErr: true, + errSub: "invalid type", + }, + "TPMFamilySupport": { + write: func(buf *bytes.Buffer) (int64, error) { + v := TPMFamilySupport(0x0D) + return v.WriteTo(buf) + }, + read: func(buf *bytes.Buffer) (any, error) { + var v TPMFamilySupport + if err := binary.Read(buf, binary.LittleEndian, &v); err != nil { + return nil, err + } + return v, nil + }, + readFrom: func(buf *bytes.Buffer) (int64, error) { + var v TPMFamilySupport + return (&v).ReadFrom(buf) + }, + wantSize: int64(TPMFamilySupport(0).TotalSize()), + want: TPMFamilySupport(0x0D), + wantErr: true, + errSub: "invalid type", + }, + } + + for name, tt := range tests { + name := name + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + var writeBuf bytes.Buffer + n, err := tt.write(&writeBuf) + if err != nil { + t.Fatalf("%s.WriteTo() error = %v, want nil", name, err) + } + if n != tt.wantSize { + t.Errorf("%s.WriteTo() bytes = %d, want %d", name, n, tt.wantSize) + } + + decoded, err := tt.read(bytes.NewBuffer(writeBuf.Bytes())) + if err != nil { + t.Fatalf("binary.Read(%s) error = %v, want nil", name, err) + } + if decoded != tt.want { + t.Errorf("%s round-trip decoded value = %v, want %v", name, decoded, tt.want) + } + + n, err = tt.readFrom(bytes.NewBuffer(writeBuf.Bytes())) + if tt.wantErr { + if err == nil { + t.Errorf("%s.ReadFrom() error = nil, want non-nil (current implementation uses value receiver)", name) + } else if tt.errSub != "" && !strings.Contains(err.Error(), tt.errSub) { + t.Errorf("%s.ReadFrom() error = %q, want substring %q", name, err.Error(), tt.errSub) + } + } else if err != nil { + t.Errorf("%s.ReadFrom() error = %v, want nil", name, err) + } + if n != tt.wantSize { + t.Errorf("%s.ReadFrom() bytes = %d, want %d", name, n, tt.wantSize) + } + }) + } +} diff --git a/pkg/intel/metadata/cbnt/types.go b/pkg/intel/metadata/cbnt/types.go new file mode 100644 index 00000000..0882e537 --- /dev/null +++ b/pkg/intel/metadata/cbnt/types.go @@ -0,0 +1,61 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cbnt + +import ( + "io" + + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" +) + +// All custom types definitions for the metadata pkg that are shared +// and/or duplicated (for e.g. used both in bg and cbnt packages wo. +// any modifications) +type ( + ManifestFieldType string + + LayoutField struct { + ID int + Name string + Size func() uint64 + Value func() any + Type ManifestFieldType + // optional list reader, only to be used for types that contain + // ManifestFieldList + ReadList func(r io.Reader) (int64, error) + // optional list writer to be used for types that contain + // ManifestFieldList + WriteList func(w io.Writer) (int64, error) + } + + // Structure is an abstraction of a structure of a manifest. + Structure interface { + io.ReaderFrom + io.WriterTo + TotalSize() uint64 + SizeOf(id int) (uint64, error) + OffsetOf(id int) (uint64, error) + Layout() []LayoutField + Validate() error + // PrettyString returns the whole object as a structured string. + PrettyString(depth uint, withHeader bool, opts ...pretty.Option) string + } + + // Element is an abstraction of an element of a manifest. + Element interface { + Structure + GetStructInfo() StructInfo + SetStructInfo(StructInfo) + } + + Manifest interface { + Structure + } + + // StructureList is an abstraction of slice of Structures + StructureList interface { + Structures() []Structure + } +) diff --git a/pkg/intel/metadata/common/bgheader/bgheader.go b/pkg/intel/metadata/common/bgheader/bgheader.go deleted file mode 100644 index 92aaf6b8..00000000 --- a/pkg/intel/metadata/common/bgheader/bgheader.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2017-2023 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package bgheader - -import ( - "encoding/binary" - "fmt" - "io" -) - -var ( - binaryOrder = binary.LittleEndian -) - -type structInfo struct { - ID structureID `json:"StructInfoID"` - Version uint8 `json:"StructInfoVersion"` -} - -type structureID [8]byte - -type BootGuardVersion uint8 - -const ( - Version10 BootGuardVersion = 1 - Version20 BootGuardVersion = 2 -) - -func (bgv BootGuardVersion) String() string { - switch bgv { - case Version10: - return "1.0" - case Version20: - return "2.0" - } - return "unknown" -} - -func DetectBGV(r io.ReadSeeker) (BootGuardVersion, error) { - var s structInfo - err := binary.Read(r, binaryOrder, &s) - if err != nil { - return 0, fmt.Errorf("unable to read field 'ID': %w", err) - } - _, err = r.Seek(0, 0) - if err != nil { - return 0, err - } - if s.Version >= 0x20 { - return Version20, nil - } else if (s.Version < 0x20) && (s.Version >= 0x10) { - return Version10, nil - } else { - return 0, fmt.Errorf("couldn't detect version") - } -} diff --git a/pkg/intel/metadata/common/examples/cmd/bpmdump/main.go b/pkg/intel/metadata/common/examples/cmd/bpmdump/main.go index 0aa50460..55747789 100644 --- a/pkg/intel/metadata/common/examples/cmd/bpmdump/main.go +++ b/pkg/intel/metadata/common/examples/cmd/bpmdump/main.go @@ -9,7 +9,7 @@ import ( "log" "os" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" ) func assertNoError(err error) { @@ -22,7 +22,7 @@ func main() { f, err := os.Open(os.Args[1]) assertNoError(err) - m := &cbntbootpolicy.Manifest{} + m := &cbntbootpolicy.ManifestCBnT{} _, err = m.ReadFrom(f) assertNoError(err) diff --git a/pkg/intel/metadata/common/integration/README.md b/pkg/intel/metadata/common/integration/README.md new file mode 100644 index 00000000..619249f8 --- /dev/null +++ b/pkg/intel/metadata/common/integration/README.md @@ -0,0 +1,42 @@ +# Integration Tests + +Since the majority of types share the same methods, most tests can be generalized. + +For each manifest there are sample binaries corresponding to different versions: + - `cbnt/bootpolicy/testdata/bpm10.bin` - BPM complying with Boot Guard 1.0 + - `cbnt/bootpolicy/testdata/bpm20.bin` - BPM complying with CBnT 2.0 + - `cbnt/bootpolicy/testdata/bpm21.bin` - BPM complying with CBnT 2.1 + - `cbnt/keymanifest/testdata/km_bg.bin` - KM complying with Boot Guard 1.0 + - `cbnt/keymanifest/testdata/km_cbnt.bin` - KM complying with CBnT 2.x + +The integration test entrypoint is `ManifestReadWrite()` from `common/integration/read_write.go`. +It reads the provided binary into a Manifest implementation and then validates the full read/write contract: + +- reads exactly the manifest bytes and ignores trailing data +- verifies `SizeOf()` and `OffsetOf()` for all top-level fields +- verifies `GetStructInfo()` and `SetStructInfo()` accessors +- validates manifest invariants via `Validate()` +- verifies `TotalSize()` matches bytes consumed by `ReadFrom()` +- checks `Print()` output against signed/unsigned expected patterns +- serializes with `WriteTo()` and verifies a byte-for-byte round trip against the original input from `ReadFrom()` +- recursively verifies size/offset behavior for nested structures and structure lists + +The helper also checks that `PrettyString()` output is stable before and after serialization (note: it the coverage is low here given that the output is hard to generalize). + +## Adding integration tests for a new manifest version + +1. Add the sample binary to the package `testdata/` directory. +2. Instantiate the manifest with the right version in `manifest_test.go`. +3. Call `integration.ManifestReadWrite(t, m, "testdata/.bin")`. + +Example: + +```go +func TestReadWriteCBNT2x(t *testing.T) { + m, err := NewManifest(cbnt.Version2x) + if err != nil { + t.Fatalf("%v", err) + } + integration.ManifestReadWrite(t, m, "testdata/bpm2x.bin") +} +``` diff --git a/pkg/intel/metadata/common/integration/read_write.go b/pkg/intel/metadata/common/integration/read_write.go new file mode 100644 index 00000000..8ff365c0 --- /dev/null +++ b/pkg/intel/metadata/common/integration/read_write.go @@ -0,0 +1,181 @@ +// Copyright 2017-2026 the LinuxBoot Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package integration + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "testing" + + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/common/pretty" + "github.com/stretchr/testify/require" +) + +func sizeList(m cbnt.Manifest) map[int]uint64 { + var ret map[int]uint64 + ret = make(map[int]uint64) + + for i, f := range m.Layout() { + ret[i] = f.Size() + } + + return ret +} + +func sizeAndOffset(t *testing.T, list map[int]uint64, m cbnt.Manifest) uint64 { + // Sizes + for i := 0; i < len(m.Layout()); i++ { + s, err := m.SizeOf(i) + require.NoError(t, err) + require.Equal(t, list[i], s) + } + + // Offsets + s, err := m.OffsetOf(0) + require.NoError(t, err) + require.Equal(t, uint64(0), s) + + prev := list[0] + for i := 1; i < len(m.Layout()); i++ { + s, err := m.OffsetOf(i) + require.NoError(t, err) + require.Equal(t, prev, s) + prev += list[i] + } + + return prev +} + +func ManifestReadWrite(t *testing.T, m cbnt.Manifest, testDataFilePath string) { + testData, err := os.ReadFile(testDataFilePath) + require.NoError(t, err) + + nR, err := m.ReadFrom(bytes.NewReader(append(testData, []byte(`extra bytes`)...))) + require.NoError(t, err) + require.Equal(t, int64(len(testData)), nR) + + // We have to read the values AFTER the read, otherwise all the fields of dynamic + // type will be incorrect (obviously). + list := sizeList(m) + + sizeAndOffset(t, list, m) + + // Getters + l := m.Layout() + field0 := l[0] + val := field0.Value + + // So same as above we don't know here what exact type we are testing, but we know it should implement getter and setter. + // Thus we can make sneaky type assertion to an interface that only has these methids, and let the test fail + // if the actual type that we are testing does not contain this methods. + type structInfoAccessor interface { + GetStructInfo() cbnt.StructInfo + SetStructInfo(cbnt.StructInfo) + Print() + } + + accessor, ok := m.(structInfoAccessor) + require.True(t, ok, "Manifest must implement GetStructInfo() and SetStructInfo()") + + originalInfo := accessor.GetStructInfo() + type nestedStructInfoAccessor interface { + GetStructInfo() cbnt.StructInfo + } + + var expectedInfo cbnt.StructInfo + if nestedAccessor, ok := val().(nestedStructInfoAccessor); ok { + expectedInfo = nestedAccessor.GetStructInfo() + } else { + var structInfoOK bool + expectedInfo, structInfoOK = val().(cbnt.StructInfo) + require.True(t, structInfoOK, "Layout[0] must be StructInfo or expose GetStructInfo()") + } + require.Equal(t, expectedInfo, originalInfo, "Getter should return the value from Layout") + + accessor.SetStructInfo(expectedInfo) + require.Equal(t, originalInfo, accessor.GetStructInfo(), "Getter should match the value just set") + + // Validate + err = m.Validate() + require.NoError(t, err) + + require.Equal(t, nR, int64(m.TotalSize())) + + // Print and PrettyString on WriteTo + prettyString := m.PrettyString(0, true) + oldStdout := os.Stdout + r, w, err := os.Pipe() + require.NoError(t, err, "Failed to create pipe for stdout") + os.Stdout = w + + accessor.Print() + + w.Close() + os.Stdout = oldStdout + + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + require.NoError(t, err, "Failed to read from pipe") + actualOutput := buf.String() + + expectedKMUnsigned := fmt.Sprintf("%v\n --KeyAndSignature--\n\tKey Manifest not signed!\n\n", + m.PrettyString(1, true, pretty.OptionOmitKeySignature(true))) + + expectedKMSigned := fmt.Sprintf("%v\n", + m.PrettyString(1, true, pretty.OptionOmitKeySignature(false))) + + // Differs too much, thus we just grep for these two strings + expectedBPMUnsigned := " --PMSE--\n\tBoot Policy Manifest not signed!\n\n" + expectedBPMSigned := "Sig Scheme:" + + require.True(t, actualOutput == expectedKMSigned || actualOutput == expectedKMUnsigned || + strings.Contains(actualOutput, expectedBPMUnsigned) || strings.Contains(actualOutput, expectedBPMSigned), + "Print() output did not match either the signed or unsigned PrettyString format") + + var out bytes.Buffer + nW, err := m.WriteTo(&out) + require.NoError(t, err) + + newPrettyString := m.PrettyString(0, true) + require.Equal(t, prettyString, newPrettyString, newPrettyString) + require.Equal(t, string(testData), out.String()) + require.Equal(t, nW, nR) + require.Equal(t, nW, int64(out.Len())) + + // Sub Structures, for all of the fields of the manifest + // that we are testing, we can check whether these are implementing + // cbnt.Structure, and basically run exactly the same tests as we did + // for the manifest itself (well almost exactlu the same :D). + for _, f := range m.Layout() { + subStruct := f.Value() + if subStruct == nil { + continue + } + if f.Size() == 0 { + continue + } + + accessor, ok := subStruct.(cbnt.Structure) + if ok { + list := sizeList(accessor) + total := sizeAndOffset(t, list, accessor) + require.Equal(t, total, accessor.TotalSize()) + } + + // special case for []Hash + hashAccessor, ok := subStruct.(cbnt.StructureList) + if ok { + for _, i := range hashAccessor.Structures() { + list := sizeList(i) + total := sizeAndOffset(t, list, i) + require.Equal(t, total, i.TotalSize()) + } + } + } +} diff --git a/pkg/intel/metadata/common/manifestcodegen/README.md b/pkg/intel/metadata/common/manifestcodegen/README.md new file mode 100644 index 00000000..9dd81b13 --- /dev/null +++ b/pkg/intel/metadata/common/manifestcodegen/README.md @@ -0,0 +1,63 @@ +## Manifestcodegen (DEPRECATED) + +This tool is deprecated, left here for reference. + +Prequesites: +Open another terminal and go get the sm2/sm3 crypto library into your gopath. +The code generator disables go modules. +``` +GOPATH=your/go/path go get github.com/tjfoc/gmsm/sm2 +``` + +To generalize all the logic related to Boot Policy Manifest and Key Manifest +we use code generation. Therefore it is enough to create structure declarations +and run command from this directory: +``` +go run ./../common/manifestcodegen/cmd/manifestcodegen/ . ./bootpolicy ./key +``` + +It will performe the code autogeneration in directories: +* Current directory (`.`), which contains common structures for different manifests; +* Boot Policy Manifest directory (`./bootpolicy`); +* and Key Manifest directory (`./key`). + +To check if the files are in the up-to-date state, one may add option `-check`: +``` +go run ./../common/manifestcodegen/cmd/manifestcodegen/ -check . ./bootpolicy ./key +``` + +Or if it is required to debug/trace the behavior of autogenerated code, one +may add option `-trace`: +``` +go run ./../common/manifestcodegen/cmd/manifestcodegen/ -trace . ./bootpolicy ./key +``` + +In this case the code will write a verbose log into stdout. + +If you need to edit the template, please edit file: `./common/manifestcodegen/cmd/manifestcodegen/template_methods.tpl.go`. + +# Field tags + +There are few special struct field tags which are recognized by the code +generator: +* `id` -- defines the element Structure ID string (for example `__TXTS__`). +* `version` -- defines the value of `StructVersion` (see document #575623). +* `countType` -- used only for slices and it defines which variable type is + used to store amount of items of the slice. Arrays in a structure in a Manifest + is almost always prepended with a count variable, and we automatically map + it to the real amount of elements of our slice. And to do that we need to know + the bitsize of the counter, therefore this tag exists. +* `countValue` -- (see also `countType`) sometimes a counter requires special + transformations before it could be maped into the real amount of elements + of a slice. `countValue` allows to define a function to calculate the + real count value. +* `require` -- defines the value required by the document #575623. +* `default` -- defines the default value. +* `prettyValue` -- defines the function which prints the value in a pretty format. +* `rehashValue` -- is used to receive an auto-updated value, for example it could + be handy to automatically update size-fields. + +See also: +``` +grep -RIn field.TagGet ./ +``` diff --git a/pkg/intel/metadata/common/unittest/read_write.go b/pkg/intel/metadata/common/unittest/read_write.go deleted file mode 100644 index bd03a3e8..00000000 --- a/pkg/intel/metadata/common/unittest/read_write.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package unittest - -import ( - "bytes" - "os" - "testing" - - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/stretchr/testify/require" -) - -func CBNTManifestReadWrite(t *testing.T, m cbnt.Manifest, testDataFilePath string) { - testData, err := os.ReadFile(testDataFilePath) - require.NoError(t, err) - - nR, err := m.ReadFrom(bytes.NewReader(append(testData, []byte(`extra bytes`)...))) - require.NoError(t, err) - require.Equal(t, int64(len(testData)), nR) - require.Equal(t, nR, int64(m.TotalSize())) - - prettyString := m.PrettyString(0, true) - - var out bytes.Buffer - nW, err := m.WriteTo(&out) - require.NoError(t, err) - - newPrettyString := m.PrettyString(0, true) - require.Equal(t, prettyString, newPrettyString, newPrettyString) - require.Equal(t, string(testData), out.String()) - require.Equal(t, nW, nR) - require.Equal(t, nW, int64(out.Len())) -} - -func BGManifestReadWrite(t *testing.T, m bg.Manifest, testDataFilePath string) { - testData, err := os.ReadFile(testDataFilePath) - require.NoError(t, err) - - nR, err := m.ReadFrom(bytes.NewReader(testData)) - require.NoError(t, err) - require.Equal(t, int64(len(testData)), nR) - require.Equal(t, nR, int64(m.TotalSize())) - - prettyString := m.PrettyString(0, true) - - var out bytes.Buffer - nW, err := m.WriteTo(&out) - require.NoError(t, err) - - newPrettyString := m.PrettyString(0, true) - require.Equal(t, prettyString, newPrettyString, newPrettyString) - require.Equal(t, string(testData), out.String()) - require.Equal(t, nW, nR) - require.Equal(t, nW, int64(out.Len())) -} diff --git a/pkg/intel/metadata/fit/calc_offset.go b/pkg/intel/metadata/fit/calc_offset.go index 37434e97..d613fd8c 100644 --- a/pkg/intel/metadata/fit/calc_offset.go +++ b/pkg/intel/metadata/fit/calc_offset.go @@ -4,7 +4,7 @@ package fit -import "github.com/linuxboot/fiano/pkg/intel/metadata/fit/consts" +import "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" // CalculatePhysAddrFromOffset calculates the physical address (address to a // region mapped from the SPI chip) using an offset withtin an image, relatively @@ -15,7 +15,7 @@ import "github.com/linuxboot/fiano/pkg/intel/metadata/fit/consts" // CalculatePhysAddrFromOffset(0x01, 0x2000) == 0xffffe001 // CalculatePhysAddrFromOffset(0x40, 0x2000) == 0xffffe040 func CalculatePhysAddrFromOffset(offset uint64, imageSize uint64) uint64 { - startAddr := consts.BasePhysAddr - imageSize + startAddr := cbnt.BasePhysAddr - imageSize return startAddr + offset } @@ -28,7 +28,7 @@ func CalculatePhysAddrFromOffset(offset uint64, imageSize uint64) uint64 { // CalculateOffsetFromPhysAddr(0xffffffff, 0x1000) == 0xfff // CalculateOffsetFromPhysAddr(0xffffffc0, 0x1000) == 0xfc0 func CalculateOffsetFromPhysAddr(physAddr uint64, imageSize uint64) uint64 { - startAddr := consts.BasePhysAddr - imageSize + startAddr := cbnt.BasePhysAddr - imageSize return physAddr - startAddr } @@ -41,5 +41,5 @@ func CalculateOffsetFromPhysAddr(physAddr uint64, imageSize uint64) uint64 { // CalculateTailOffsetFromPhysAddr(0xffffffff) == 0x01 // CalculateTailOffsetFromPhysAddr(0xffffffc0) == 0x40 func CalculateTailOffsetFromPhysAddr(physAddr uint64) uint64 { - return consts.BasePhysAddr - physAddr + return cbnt.BasePhysAddr - physAddr } diff --git a/pkg/intel/metadata/fit/consts/consts.go b/pkg/intel/metadata/fit/consts/consts.go deleted file mode 100644 index 58904bed..00000000 --- a/pkg/intel/metadata/fit/consts/consts.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package consts - -const ( - // BasePhysAddr is the absolute physical address where the firmware image ends. - // - // See Figure 2.1 in https://www.intel.com/content/dam/www/public/us/en/documents/guides/fit-bios-specification.pdf - // - // Note: A firmware image grows towards lower addresses. So an image will be mapped to addresses: - // [ BasePhysAddr-length .. BasePhysAddr ) - // - // Note: SPI chip is mapped into this region. So we actually work directly with data of the SPI chip - // - // See also CalculatePhysAddrOfOffset(). - BasePhysAddr = 1 << 32 // "4GB" - - // FITPointerOffset is the offset of the physical address of the FIT pointer. - // See "1 Firmware Interface Table" in "Firmware Interface Table" specification: - // * https://www.intel.com/content/dam/www/public/us/en/documents/guides/fit-bios-specification.pdf - FITPointerOffset = 0x40 - - // FITPointerPhysAddr is the physical address of the FIT pointer. - // See "1 Firmware Interface Table" in "Firmware Interface Table" specification: - // * https://www.intel.com/content/dam/www/public/us/en/documents/guides/fit-bios-specification.pdf - FITPointerPhysAddr = BasePhysAddr - FITPointerOffset - - // FITPointerSize is the size of the FIT pointer. - // It is suggested to be 0x10 bytes because of "Figure 1-1" of the specification. - FITPointerSize = 0x10 -) diff --git a/pkg/intel/metadata/fit/consts/fit.go b/pkg/intel/metadata/fit/consts/fit.go deleted file mode 100644 index a6101acf..00000000 --- a/pkg/intel/metadata/fit/consts/fit.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2017-2021 the LinuxBoot Authors. All rights reserved -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package consts - -const ( - // FITHeadersMagic is the magic string, expected in the beginning of the - // first FIT entry - FITHeadersMagic = "_FIT_ " -) diff --git a/pkg/intel/metadata/fit/ent_boot_policy_manifest.go b/pkg/intel/metadata/fit/ent_boot_policy_manifest.go index 6073b4ac..8608738e 100644 --- a/pkg/intel/metadata/fit/ent_boot_policy_manifest.go +++ b/pkg/intel/metadata/fit/ent_boot_policy_manifest.go @@ -10,9 +10,8 @@ import ( "fmt" "io" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/bgheader" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" ) // EntryBootPolicyManifestRecord represents a FIT entry of type "Boot Policy Manifest" (0x0C) @@ -41,27 +40,44 @@ func (entry *EntryBootPolicyManifestRecord) Reader() *bytes.Reader { } // ParseData creates EntryBootPolicyManifestRecord from EntryBootPolicyManifest -func (entry *EntryBootPolicyManifestRecord) ParseData() (*bgbootpolicy.Manifest, *cbntbootpolicy.Manifest, error) { +func (entry *EntryBootPolicyManifestRecord) ParseData() (*cbntbootpolicy.Manifest, *cbntbootpolicy.Manifest, error) { r := bytes.NewReader(entry.DataSegmentBytes) - version, err := bgheader.DetectBGV(r) + version, err := cbnt.DetectBGV(r) if err != nil { return nil, nil, err } switch version { - case bgheader.Version10: - manifest := bgbootpolicy.NewManifest() + case cbnt.Version10: + manifest, err := cbntbootpolicy.NewManifest(cbnt.Version10) + if err != nil { + return nil, nil, nil + } + _, err = manifest.ReadFrom(r) + if err != nil && !errors.Is(err, io.EOF) { + return nil, nil, err + } + return &manifest, nil, nil + case cbnt.Version20: + manifest, err := cbntbootpolicy.NewManifest(cbnt.Version20) + if err != nil { + return nil, nil, err + } _, err = manifest.ReadFrom(r) if err != nil && !errors.Is(err, io.EOF) { return nil, nil, err } - return manifest, nil, nil - case bgheader.Version20: - manifest := cbntbootpolicy.NewManifest() + return nil, &manifest, nil + + case cbnt.Version21: + manifest, err := cbntbootpolicy.NewManifest(cbnt.Version21) + if err != nil { + return nil, nil, err + } _, err = manifest.ReadFrom(r) if err != nil && !errors.Is(err, io.EOF) { return nil, nil, err } - return nil, manifest, nil + return nil, &manifest, nil default: return nil, nil, fmt.Errorf("failed to parse BootPolicyManifest, err: %v", err) } @@ -69,7 +85,7 @@ func (entry *EntryBootPolicyManifestRecord) ParseData() (*bgbootpolicy.Manifest, // ParseBootPolicyManifest returns a boot policy manifest if it was able to // parse one. -func (table Table) ParseBootPolicyManifest(firmware []byte) (*bgbootpolicy.Manifest, *cbntbootpolicy.Manifest, error) { +func (table Table) ParseBootPolicyManifest(firmware []byte) (*cbntbootpolicy.Manifest, *cbntbootpolicy.Manifest, error) { hdr := table.First(EntryTypeBootPolicyManifest) if hdr == nil { return nil, nil, ErrNotFound{} diff --git a/pkg/intel/metadata/fit/ent_key_manifest_record.go b/pkg/intel/metadata/fit/ent_key_manifest_record.go index dbac730c..8d1d188e 100644 --- a/pkg/intel/metadata/fit/ent_key_manifest_record.go +++ b/pkg/intel/metadata/fit/ent_key_manifest_record.go @@ -10,9 +10,8 @@ import ( "fmt" "io" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgkey" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/bgheader" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + keymanifest "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" ) // EntryKeyManifestRecord represents a FIT entry of type "Key Manifest Record" (0x0B) @@ -41,27 +40,33 @@ func (entry *EntryKeyManifestRecord) Reader() *bytes.Reader { } // ParseData creates EntryKeyManifestRecord from EntryKeyManifest -func (entry *EntryKeyManifestRecord) ParseData() (*bgkey.Manifest, *cbntkey.Manifest, error) { +func (entry *EntryKeyManifestRecord) ParseData() (*keymanifest.Manifest, *keymanifest.Manifest, error) { r := bytes.NewReader(entry.DataSegmentBytes) - version, err := bgheader.DetectBGV(r) + version, err := cbnt.DetectBGV(r) if err != nil { return nil, nil, err } switch version { - case bgheader.Version10: - manifest := bgkey.NewManifest() + case cbnt.Version10: + manifest, err := keymanifest.NewManifest(cbnt.Version10) + if err != nil { + return nil, nil, err + } _, err = manifest.ReadFrom(r) if err != nil && !errors.Is(err, io.EOF) { return nil, nil, err } - return manifest, nil, nil - case bgheader.Version20: - manifest := cbntkey.NewManifest() + return &manifest, nil, nil + case cbnt.Version20: + manifest, err := keymanifest.NewManifest(cbnt.Version20) + if err != nil { + return nil, nil, err + } _, err = manifest.ReadFrom(r) if err != nil && !errors.Is(err, io.EOF) { return nil, nil, err } - return nil, manifest, nil + return nil, &manifest, nil default: return nil, nil, fmt.Errorf("failed to parse KeyManifest, err: %v", err) } @@ -69,7 +74,7 @@ func (entry *EntryKeyManifestRecord) ParseData() (*bgkey.Manifest, *cbntkey.Mani // ParseKeyManifest returns a key manifest if it was able to // parse one. -func (table Table) ParseKeyManifest(firmware []byte) (*bgkey.Manifest, *cbntkey.Manifest, error) { +func (table Table) ParseKeyManifest(firmware []byte) (*keymanifest.Manifest, *keymanifest.Manifest, error) { hdr := table.First(EntryTypeKeyManifestRecord) if hdr == nil { return nil, nil, ErrNotFound{} diff --git a/pkg/intel/metadata/fit/ent_startup_ac_module_entry.go b/pkg/intel/metadata/fit/ent_startup_ac_module_entry.go index 90dd0df2..f06d1495 100644 --- a/pkg/intel/metadata/fit/ent_startup_ac_module_entry.go +++ b/pkg/intel/metadata/fit/ent_startup_ac_module_entry.go @@ -23,7 +23,12 @@ type EntrySACM struct{ EntryBase } var _ EntryCustomGetDataSegmentSizer = (*EntrySACM)(nil) func (entry *EntrySACM) CustomGetDataSegmentSize(firmware io.ReadSeeker) (uint64, error) { - offset, err := entry.Headers.getDataSegmentOffset(firmware) + firmwareSizeUsed, err := FirmwareSizeUsedFromSeeker(firmware) + if err != nil { + return 0, fmt.Errorf("unable to detect firmware size: %w", err) + } + + offset, err := entry.Headers.getDataSegmentOffset(firmwareSizeUsed) if err != nil { return 0, fmt.Errorf("unable to detect data segment offset: %w", err) } @@ -113,6 +118,9 @@ const ( // ACHeaderVersion4 is version "4.0 for SINIT ACM of BtG" ACHeaderVersion4 = ACModuleHeaderVersion(0x00040000) + + // ACHeaderVersion4 is version "5.0 for SINIT ACM of BtG" + ACHeaderVersion5 = ACModuleHeaderVersion(0x00050004) ) func (ver ACModuleHeaderVersion) GoString() string { @@ -346,6 +354,28 @@ func (entryData EntrySACMDataCommon) KeySizeBinaryOffset() uint { return 120 } +var entrySACMData0Size = uint(binary.Size(EntrySACMData0{})) + +type SACMDataRW interface { + EntrySACMData0 | EntrySACMData3 | EntrySACMData4 | EntrySACMData5 +} + +func readSACMData[T SACMDataRW](entryData *T, r io.Reader) (int64, error) { + err := binary.Read(r, binary.LittleEndian, entryData) + if err != nil { + return -1, err + } + return int64(binary.Size(*entryData)), nil +} + +func writeSACMData[T SACMDataRW](entryData *T, w io.Writer) (int64, error) { + err := binary.Write(w, binary.LittleEndian, entryData) + if err != nil { + return -1, err + } + return int64(binary.Size(*entryData)), nil +} + // EntrySACMData0 is the structure for ACM of version 0.0. type EntrySACMData0 struct { EntrySACMDataCommon @@ -356,8 +386,6 @@ type EntrySACMData0 struct { Scratch [572]byte } -var entrySACMData0Size = uint(binary.Size(EntrySACMData0{})) - // Read parses the ACM v0 headers func (entryData *EntrySACMData0) Read(b []byte) (int, error) { n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b)) @@ -366,11 +394,7 @@ func (entryData *EntrySACMData0) Read(b []byte) (int, error) { // ReadFrom parses the ACM v0 headers func (entryData *EntrySACMData0) ReadFrom(r io.Reader) (int64, error) { - err := binary.Read(r, binary.LittleEndian, entryData) - if err != nil { - return -1, err - } - return int64(entrySACMData0Size), nil + return readSACMData(entryData, r) } // Write compiles the SACM v0 headers into a binary representation @@ -381,11 +405,7 @@ func (entryData *EntrySACMData0) Write(b []byte) (int, error) { // WriteTo compiles the SACM v0 headers into a binary representation func (entryData *EntrySACMData0) WriteTo(w io.Writer) (int64, error) { - err := binary.Write(w, binary.LittleEndian, entryData) - if err != nil { - return -1, err - } - return int64(entrySACMData0Size), nil + return writeSACMData(entryData, w) } // GetRSAPubKey returns the RSA public key @@ -427,19 +447,9 @@ type EntrySACMData3 struct { var entrySACMData3Size = uint(binary.Size(EntrySACMData3{})) -// Read parses the ACM v3 headers -func (entryData *EntrySACMData3) Read(b []byte) (int, error) { - n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b)) - return int(n), err -} - // ReadFrom parses the ACM v3 headers func (entryData *EntrySACMData3) ReadFrom(r io.Reader) (int64, error) { - err := binary.Read(r, binary.LittleEndian, entryData) - if err != nil { - return -1, err - } - return int64(entrySACMData3Size), nil + return readSACMData(entryData, r) } // Write compiles the SACM v3 headers into a binary representation @@ -450,11 +460,7 @@ func (entryData *EntrySACMData3) Write(b []byte) (int, error) { // WriteTo compiles the SACM v3 headers into a binary representation func (entryData *EntrySACMData3) WriteTo(w io.Writer) (int64, error) { - err := binary.Write(w, binary.LittleEndian, entryData) - if err != nil { - return -1, err - } - return int64(entrySACMData3Size), nil + return writeSACMData(entryData, w) } // GetRSAPubKey returns the RSA public key @@ -497,8 +503,6 @@ type EntrySACMData4 struct { Scratch [3584]byte } -var entrySACMData4Size = uint(binary.Size(EntrySACMData4{})) - // Read parses the ACM v3 headers func (entryData *EntrySACMData4) Read(b []byte) (int, error) { n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b)) @@ -507,11 +511,7 @@ func (entryData *EntrySACMData4) Read(b []byte) (int, error) { // ReadFrom parses the ACM v3 headers func (entryData *EntrySACMData4) ReadFrom(r io.Reader) (int64, error) { - err := binary.Read(r, binary.LittleEndian, entryData) - if err != nil { - return -1, err - } - return int64(entrySACMData3Size), nil + return readSACMData(entryData, r) } // Write compiles the SACM v3 headers into a binary representation @@ -522,11 +522,7 @@ func (entryData *EntrySACMData4) Write(b []byte) (int, error) { // WriteTo compiles the SACM v3 headers into a binary representation func (entryData *EntrySACMData4) WriteTo(w io.Writer) (int64, error) { - err := binary.Write(w, binary.LittleEndian, entryData) - if err != nil { - return -1, err - } - return int64(entrySACMData4Size), nil + return writeSACMData(entryData, w) } // GetRSAPubKey returns the RSA public key @@ -557,6 +553,67 @@ func (entryData *EntrySACMData4) GetXMSSSig() []byte { return entryData.XMSSSig[ // GetScratch returns the Scratch field value func (entryData *EntrySACMData4) GetScratch() []byte { return entryData.Scratch[:] } +type EntrySACMData5 struct { + EntrySACMDataCommon + + RSAPubKey [384]byte + RSASig [384]byte + XMSSPubKey [64]byte + XMSSSig [2692]byte + Reserved [60]byte + Scratch [3584]byte +} + +// Read parses the ACM v5 headers +func (entryData *EntrySACMData5) Read(b []byte) (int, error) { + n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b)) + return int(n), err +} + +// ReadFrom parses the ACM v3 headers +func (entryData *EntrySACMData5) ReadFrom(r io.Reader) (int64, error) { + return readSACMData(entryData, r) +} + +// Write compiles the SACM v3 headers into a binary representation +func (entryData *EntrySACMData5) Write(b []byte) (int, error) { + n, err := entryData.WriteTo(bytesextra.NewReadWriteSeeker(b)) + return int(n), err +} + +// WriteTo compiles the SACM v3 headers into a binary representation +func (entryData *EntrySACMData5) WriteTo(w io.Writer) (int64, error) { + return writeSACMData(entryData, w) +} + +// GetRSAPubKey returns the RSA public key +func (entryData *EntrySACMData5) GetRSAPubKey() rsa.PublicKey { + pubKey := rsa.PublicKey{ + N: big.NewInt(0), + E: 0x10001, // see Table 9. "RSAPubExp" of https://www.intel.com/content/www/us/en/software-developers/txt-software-development-guide.html + } + pubKey.N.SetBytes(entryData.RSAPubKey[:]) + return pubKey +} + +// GetRSASig returns the RSA signature. +func (entryData *EntrySACMData5) GetRSASig() []byte { return entryData.RSASig[:] } + +// RSASigBinaryOffset returns the RSA signature offset +func (entryData *EntrySACMData5) RSASigBinaryOffset() uint64 { + return uint64(binary.Size(entryData.EntrySACMDataCommon)) + + uint64(binary.Size(entryData.RSAPubKey)) +} + +// GetXMSSPubKey returns the XMSS public key +func (entryData *EntrySACMData5) GetXMSSPubKey() []byte { return entryData.XMSSPubKey[:] } + +// GetXMSSSig returns the XMSS signature. +func (entryData *EntrySACMData5) GetXMSSSig() []byte { return entryData.XMSSSig[:] } + +// GetScratch returns the Scratch field value +func (entryData *EntrySACMData5) GetScratch() []byte { return entryData.Scratch[:] } + // Read parses the ACM func (entryData *EntrySACMData) Read(b []byte) (int, error) { n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b)) @@ -612,6 +669,8 @@ func (entryData *EntrySACMData) GetCommon() *EntrySACMDataCommon { return &data.EntrySACMDataCommon case *EntrySACMData4: return &data.EntrySACMDataCommon + case *EntrySACMData5: + return &data.EntrySACMDataCommon } return nil } @@ -673,6 +732,9 @@ func ParseSACMData(r io.Reader) (*EntrySACMData, error) { case ACHeaderVersion4: result.EntrySACMDataInterface = &EntrySACMData4{EntrySACMDataCommon: common} requiredKeySize = uint64(len(EntrySACMData4{}.RSAPubKey)) + case ACHeaderVersion5: + result.EntrySACMDataInterface = &EntrySACMData5{EntrySACMDataCommon: common} + requiredKeySize = uint64(len(EntrySACMData5{}.RSAPubKey)) default: return result, &ErrUnknownACMHeaderVersion{ACHeaderVersion: common.HeaderVersion} } diff --git a/pkg/intel/metadata/fit/entry.go b/pkg/intel/metadata/fit/entry.go index 793f289a..5fd4e6f2 100644 --- a/pkg/intel/metadata/fit/entry.go +++ b/pkg/intel/metadata/fit/entry.go @@ -11,8 +11,8 @@ import ( "strings" "github.com/hashicorp/go-multierror" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" "github.com/linuxboot/fiano/pkg/intel/metadata/fit/check" - "github.com/linuxboot/fiano/pkg/intel/metadata/fit/consts" "github.com/xaionaro-go/bytesextra" ) @@ -148,7 +148,7 @@ func (entries Entries) String() string { // // What will happen: // 1. The FIT headers will be written by offset headersOffset. -// 2. The FIT pointer will be written at consts.FITPointerOffset offset from the end of the image. +// 2. The FIT pointer will be written at cbnt.FITPointerOffset offset from the end of the image. // 3. Data referenced by FIT headers will be written at offsets accordingly to Address fields (in the headers). // // Consider calling Rehash() before Inject()/InjectTo() @@ -171,7 +171,7 @@ func (entries Entries) InjectTo(w io.WriteSeeker, headersOffset uint64) error { // Write FIT pointer - if _, err := w.Seek(-consts.FITPointerOffset, io.SeekEnd); err != nil { + if _, err := w.Seek(-cbnt.FITPointerOffset, io.SeekEnd); err != nil { return fmt.Errorf("unable to Seek(%d, %d) to write FIT pointer: %w", headersOffset, io.SeekStart, err) } pointerValue := CalculatePhysAddrFromOffset(headersOffset, uint64(imageSize)) @@ -227,25 +227,36 @@ func readBytesFromReader(r io.Reader, size uint64) ([]byte, error) { return result, nil } -// EntryDataSegmentSize returns the coordinates of the data segment size associates with the entry. -func EntryDataSegmentSize(entry Entry, firmware io.ReadSeeker) (uint64, error) { +func FirmwareSizeUsedFromSeeker(firmware io.ReadSeeker) (uint64, error) { + firmwareSize, err := firmware.Seek(0, io.SeekEnd) + if err != nil || firmwareSize < 0 { + return 0, fmt.Errorf("unable to determine firmware size; result: %d; err: %w", firmwareSize, err) + } + + firmwareSizeUsed := uint64(firmwareSize) + if ifdSize, ifdErr := flashSizeFromIFD(firmware, firmwareSizeUsed); ifdErr == nil && ifdSize > 0 && ifdSize <= firmwareSizeUsed { + firmwareSizeUsed = ifdSize + } + + return firmwareSizeUsed, nil +} + +func EntryDataSegmentSize(entry Entry, firmware io.ReadSeeker, firmwareSizeUsed uint64) (uint64, error) { if sizeGetter, ok := entry.(EntryCustomGetDataSegmentSizer); ok { return sizeGetter.CustomGetDataSegmentSize(firmware) - } else { - return entry.GetEntryBase().Headers.mostCommonGetDataSegmentSize(), nil } + return entry.GetEntryBase().Headers.mostCommonGetDataSegmentSize(), nil } -// EntryDataSegmentCoordinates returns the coordinates of the data segment coordinates associates with the entry. -func EntryDataSegmentCoordinates(entry Entry, firmware io.ReadSeeker) (uint64, uint64, error) { +func EntryDataSegmentCoordinates(entry Entry, firmware io.ReadSeeker, firmwareSizeUsed uint64) (uint64, uint64, error) { var err error - offset, addErr := entry.GetEntryBase().Headers.getDataSegmentOffset(firmware) + offset, addErr := entry.GetEntryBase().Headers.getDataSegmentOffset(firmwareSizeUsed) if addErr != nil { err = multierror.Append(err, fmt.Errorf("unable to get data segment offset: %w", err)) } - size, addErr := EntryDataSegmentSize(entry, firmware) + size, addErr := EntryDataSegmentSize(entry, firmware, firmwareSizeUsed) if addErr != nil { err = multierror.Append(err, fmt.Errorf("unable to get data segment size: %w", err)) } @@ -254,20 +265,23 @@ func EntryDataSegmentCoordinates(entry Entry, firmware io.ReadSeeker) (uint64, u } // If possible then make a slice of existing data; if not then copy. -func sliceOrCopyBytesFrom(r io.ReadSeeker, startIdx, endIdx uint64) ([]byte, error) { +func sliceOrCopyBytesFrom(r io.ReadSeeker, startIdx, endIdx, firmwareSizeUsed uint64) ([]byte, error) { switch r := r.(type) { case *bytesextra.ReadWriteSeeker: - if err := check.BytesRange(uint(len(r.Storage)), int(startIdx), int(endIdx)); err != nil { + if err := check.BytesRange(uint(firmwareSizeUsed), int(startIdx), int(endIdx)); err != nil { return nil, err } return r.Storage[startIdx:endIdx], nil default: + if err := check.BytesRange(uint(firmwareSizeUsed), int(startIdx), int(endIdx)); err != nil { + return nil, err + } return copyBytesFrom(r, startIdx, endIdx) } } -func entryInitDataSegmentBytes(entry Entry, firmware io.ReadSeeker) error { - dataSegmentOffset, dataSegmentSize, err := EntryDataSegmentCoordinates(entry, firmware) +func entryInitDataSegmentBytes(entry Entry, firmware io.ReadSeeker, firmwareSizeUsed uint64) error { + dataSegmentOffset, dataSegmentSize, err := EntryDataSegmentCoordinates(entry, firmware, firmwareSizeUsed) if err != nil { return fmt.Errorf("unable to get data segment coordinates of entry %T: %w", entry, err) } @@ -278,7 +292,7 @@ func entryInitDataSegmentBytes(entry Entry, firmware io.ReadSeeker) error { base := entry.GetEntryBase() - base.DataSegmentBytes, err = sliceOrCopyBytesFrom(firmware, dataSegmentOffset, dataSegmentOffset+dataSegmentSize) + base.DataSegmentBytes, err = sliceOrCopyBytesFrom(firmware, dataSegmentOffset, dataSegmentOffset+dataSegmentSize, firmwareSizeUsed) if err != nil { return fmt.Errorf("unable to copy data segment bytes from the firmware image (offset:%d, size:%d): %w", dataSegmentOffset, dataSegmentSize, err) } @@ -287,7 +301,7 @@ func entryInitDataSegmentBytes(entry Entry, firmware io.ReadSeeker) error { } // NewEntry returns a new entry using headers and firmware image -func NewEntry(hdr *EntryHeaders, firmware io.ReadSeeker) Entry { +func NewEntry(hdr *EntryHeaders, firmware io.ReadSeeker, firmwareSizeUsed uint64) Entry { entry := hdr.Type().newEntry() if entry == nil { entry = &EntryUnknown{} @@ -295,7 +309,7 @@ func NewEntry(hdr *EntryHeaders, firmware io.ReadSeeker) Entry { base := entry.GetEntryBase() base.Headers = *hdr - err := entryInitDataSegmentBytes(entry, firmware) + err := entryInitDataSegmentBytes(entry, firmware, firmwareSizeUsed) if err != nil { base.HeadersErrors = append(base.HeadersErrors, err) } diff --git a/pkg/intel/metadata/fit/entry_headers.go b/pkg/intel/metadata/fit/entry_headers.go index 59c821c8..ec6f05f7 100644 --- a/pkg/intel/metadata/fit/entry_headers.go +++ b/pkg/intel/metadata/fit/entry_headers.go @@ -300,12 +300,17 @@ func (f *TypeAndIsChecksumValid) UnmarshalJSON(b []byte) error { // GetEntry returns a full entry (headers + data) func (hdr EntryHeaders) GetEntry(firmware []byte) Entry { - return hdr.GetEntryFrom(bytesextra.NewReadWriteSeeker(firmware)) + rw := bytesextra.NewReadWriteSeeker(firmware) + firmwareSizeUsed := uint64(len(firmware)) + if ifdSize, ifdErr := flashSizeFromIFD(rw, firmwareSizeUsed); ifdErr == nil && ifdSize > 0 && ifdSize <= firmwareSizeUsed { + firmwareSizeUsed = ifdSize + } + return hdr.GetEntryFrom(rw, firmwareSizeUsed) } // GetEntryFrom returns a full entry (headers + data) -func (hdr EntryHeaders) GetEntryFrom(firmware io.ReadSeeker) Entry { - return NewEntry(hdr.copy(), firmware) +func (hdr EntryHeaders) GetEntryFrom(firmware io.ReadSeeker, firmwareSizeUsed uint64) Entry { + return NewEntry(hdr.copy(), firmware, firmwareSizeUsed) } // Type returns the type of the FIT entry @@ -365,15 +370,11 @@ func (hdr *EntryHeaders) CalculateChecksum() uint8 { return result } -// getDataSegmentCoordinates returns the offset of the data segment -// associated with the entry. -func (hdr *EntryHeaders) getDataSegmentOffset(firmware io.Seeker) (uint64, error) { - firmwareSize, err := firmware.Seek(0, io.SeekEnd) - if err != nil { - return 0, fmt.Errorf("unable to get the size of the firmware: %w", err) +func (hdr *EntryHeaders) getDataSegmentOffset(firmwareSizeUsed uint64) (uint64, error) { + if firmwareSizeUsed == 0 { + return 0, fmt.Errorf("firmware size is zero") } - - return hdr.Address.Offset(uint64(firmwareSize)), nil + return hdr.Address.Offset(firmwareSizeUsed), nil } // mostCommonGetDataSegmentCoordinates returns the length of the data segment diff --git a/pkg/intel/metadata/fit/entry_test.go b/pkg/intel/metadata/fit/entry_test.go index 818aca9f..13390953 100644 --- a/pkg/intel/metadata/fit/entry_test.go +++ b/pkg/intel/metadata/fit/entry_test.go @@ -10,7 +10,8 @@ import ( "strings" "testing" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + keymanifest "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" "github.com/stretchr/testify/require" "github.com/xaionaro-go/bytesextra" ) @@ -51,7 +52,7 @@ func TestRehashEntry(t *testing.T) { // Validating that DataSize() calculates sizes consistently with RehashEntry() if entryType != EntryTypeStartupACModuleEntry { - dataSize, err := EntryDataSegmentSize(entry, nil) + dataSize, err := EntryDataSegmentSize(entry, nil, 0) require.NoError(t, err) if dataSize != 0 && dataSize != uint64(len(entry.GetEntryBase().DataSegmentBytes)) { t.Errorf("wrong DataSize 0x%X for type %s", dataSize, entryType) @@ -68,9 +69,10 @@ func getSampleEntries(t *testing.T) Entries { kmEntry := &EntryKeyManifestRecord{} { - km := cbntkey.NewManifest() + km, err := keymanifest.NewManifest(cbnt.Version20) + require.NoError(t, err) var buf bytes.Buffer - _, err := km.WriteTo(&buf) + _, err = km.WriteTo(&buf) require.NoError(t, err) kmEntry.DataSegmentBytes = buf.Bytes() } diff --git a/pkg/intel/metadata/fit/errors.go b/pkg/intel/metadata/fit/errors.go index 08ae7976..6c67d7c0 100644 --- a/pkg/intel/metadata/fit/errors.go +++ b/pkg/intel/metadata/fit/errors.go @@ -7,7 +7,7 @@ package fit import ( "fmt" - "github.com/linuxboot/fiano/pkg/intel/metadata/fit/consts" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" ) // ErrACMInvalidKeySize means ACM entry has invalid key size @@ -47,7 +47,7 @@ type ErrExpectedFITHeadersMagic struct { func (err *ErrExpectedFITHeadersMagic) Error() string { return fmt.Sprintf("string '%s' was expected as the Address value of the FIT header entry, but received: '%s'", - consts.FITHeadersMagic, err.Received) + cbnt.FITHeadersMagic, err.Received) } // ErrNotFound literally means "not found". diff --git a/pkg/intel/metadata/fit/get_entries.go b/pkg/intel/metadata/fit/get_entries.go index 94a57709..50e75e50 100644 --- a/pkg/intel/metadata/fit/get_entries.go +++ b/pkg/intel/metadata/fit/get_entries.go @@ -18,10 +18,10 @@ func GetEntries(firmware []byte) (Entries, error) { // GetEntriesFrom returns parsed FIT-entries func GetEntriesFrom(firmware io.ReadSeeker) (Entries, error) { - table, err := GetTableFrom(firmware) + table, firmwareSizeUsed, err := GetTableFrom(firmware) if err != nil { return nil, fmt.Errorf("unable to get FIT table: %w", err) } - return table.GetEntriesFrom(firmware), nil + return table.GetEntriesFrom(firmware, firmwareSizeUsed), nil } diff --git a/pkg/intel/metadata/fit/table.go b/pkg/intel/metadata/fit/table.go index 63097b4b..5d7f84ab 100644 --- a/pkg/intel/metadata/fit/table.go +++ b/pkg/intel/metadata/fit/table.go @@ -11,8 +11,9 @@ import ( "io" "strings" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" "github.com/linuxboot/fiano/pkg/intel/metadata/fit/check" - "github.com/linuxboot/fiano/pkg/intel/metadata/fit/consts" + "github.com/linuxboot/fiano/pkg/uefi" "github.com/xaionaro-go/bytesextra" ) @@ -22,13 +23,18 @@ type Table []EntryHeaders // GetEntries returns parsed FIT-entries func (table Table) GetEntries(firmware []byte) (result Entries) { - return table.GetEntriesFrom(bytesextra.NewReadWriteSeeker(firmware)) + rw := bytesextra.NewReadWriteSeeker(firmware) + firmwareSizeUsed := uint64(len(firmware)) + if ifdSize, ifdErr := flashSizeFromIFD(rw, firmwareSizeUsed); ifdErr == nil && ifdSize > 0 && ifdSize <= firmwareSizeUsed { + firmwareSizeUsed = ifdSize + } + return table.GetEntriesFrom(rw, firmwareSizeUsed) } -// GetEntriesFrom returns parsed FIT-entries -func (table Table) GetEntriesFrom(firmware io.ReadSeeker) (result Entries) { +// GetEntriesFrom returns parsed FIT-entries using provided firmware size. +func (table Table) GetEntriesFrom(firmware io.ReadSeeker, firmwareSizeUsed uint64) (result Entries) { for _, headers := range table { - result = append(result, headers.GetEntryFrom(firmware)) + result = append(result, headers.GetEntryFrom(firmware, firmwareSizeUsed)) } return } @@ -91,7 +97,7 @@ func (table Table) WriteTo(w io.Writer) (n int64, err error) { // WriteToFirmwareImage finds the position of FIT in a firmware image and writes the table there. func (table Table) WriteToFirmwareImage(w io.ReadWriteSeeker) (n int64, err error) { - startIdx, _, err := GetHeadersTableRangeFrom(w) + startIdx, _, _, err := GetHeadersTableRangeFrom(w) if err != nil { return 0, fmt.Errorf("unable to find the beginning of the FIT: %w", err) } @@ -131,14 +137,14 @@ func ParseTable(b []byte) (Table, error) { // GetPointerCoordinates returns the position of the FIT pointer within // the firmware. func GetPointerCoordinates(firmwareSize uint64) (startIdx, endIdx int64) { - startIdx = int64(firmwareSize) - consts.FITPointerOffset - endIdx = startIdx + consts.FITPointerSize + startIdx = int64(firmwareSize) - cbnt.FITPointerOffset + endIdx = startIdx + cbnt.FITPointerSize return } -// GetHeadersTableRangeFrom returns the starting and ending indexes of the FIT -// headers table within the firmware image. -func GetHeadersTableRangeFrom(firmware io.ReadSeeker) (startIdx, endIdx uint64, err error) { +// GetHeadersTableRangeFromWithSize returns the starting and ending indexes of the FIT +// headers table within the firmware image and firmware size used. +func GetHeadersTableRangeFrom(firmware io.ReadSeeker) (startIdx, endIdx, firmwareSizeUsed uint64, err error) { /* An example: @@ -180,22 +186,27 @@ func GetHeadersTableRangeFrom(firmware io.ReadSeeker) (startIdx, endIdx uint64, firmwareSize, err := firmware.Seek(0, io.SeekEnd) if err != nil || firmwareSize < 0 { - return 0, 0, fmt.Errorf("unable to determine firmware size; result: %d; err: %w", firmwareSize, err) + return 0, 0, 0, fmt.Errorf("unable to determine firmware size; result: %d; err: %w", firmwareSize, err) + } + + firmwareSizeUsed = uint64(firmwareSize) + if ifdSize, ifdErr := flashSizeFromIFD(firmware, firmwareSizeUsed); ifdErr == nil && ifdSize > 0 && ifdSize <= uint64(firmwareSize) { + firmwareSizeUsed = ifdSize } - fitPointerStartIdx, fitPointerEndIdx := GetPointerCoordinates(uint64(firmwareSize)) + fitPointerStartIdx, fitPointerEndIdx := GetPointerCoordinates(firmwareSizeUsed) - if err := check.BytesRange(uint(firmwareSize), int(fitPointerStartIdx), int(fitPointerEndIdx)); err != nil { - return 0, 0, fmt.Errorf("invalid fit pointer bytes range: %w", err) + if err := check.BytesRange(uint(firmwareSizeUsed), int(fitPointerStartIdx), int(fitPointerEndIdx)); err != nil { + return 0, 0, firmwareSizeUsed, fmt.Errorf("invalid fit pointer bytes range: %w", err) } - fitPointerBytes, err := sliceOrCopyBytesFrom(firmware, uint64(fitPointerStartIdx), uint64(fitPointerEndIdx)) + fitPointerBytes, err := sliceOrCopyBytesFrom(firmware, uint64(fitPointerStartIdx), uint64(fitPointerEndIdx), firmwareSizeUsed) if err != nil { - return 0, 0, fmt.Errorf("unable to get FIT pointer value: %w", err) + return 0, 0, firmwareSizeUsed, fmt.Errorf("unable to get FIT pointer value: %w", err) } fitPointerValue := binary.LittleEndian.Uint64(fitPointerBytes) fitPointerOffset := CalculateTailOffsetFromPhysAddr(fitPointerValue) - startIdx = uint64(firmwareSize) - fitPointerOffset + startIdx = firmwareSizeUsed - fitPointerOffset // OK, now we need to calculate the end of the headers... // @@ -203,7 +214,7 @@ func GetHeadersTableRangeFrom(firmware io.ReadSeeker) (startIdx, endIdx uint64, // size is the size of the table. So let's just use it. firstHeaderEndIdx := startIdx + uint64(entryHeadersSize) - if err = check.BytesRange(uint(firmwareSize), int(startIdx), int(firstHeaderEndIdx)); err != nil { + if err = check.BytesRange(uint(firmwareSizeUsed), int(startIdx), int(firstHeaderEndIdx)); err != nil { err = fmt.Errorf("invalid the first entry bytes range: %w", err) return } @@ -229,7 +240,7 @@ func GetHeadersTableRangeFrom(firmware io.ReadSeeker) (startIdx, endIdx uint64, err = fmt.Errorf("unable to read the Address value of the FIT header entry: %w", err) return } - if !bytes.Equal([]byte(consts.FITHeadersMagic), buf.Bytes()) { + if !bytes.Equal([]byte(cbnt.FITHeadersMagic), buf.Bytes()) { err = &ErrExpectedFITHeadersMagic{Received: buf.Bytes()} return } @@ -238,7 +249,7 @@ func GetHeadersTableRangeFrom(firmware io.ReadSeeker) (startIdx, endIdx uint64, // parseHeaders it. endIdx = startIdx + uint64(tableMeta.Size.Uint32()<<4) // See 4.2.5 - if err = check.BytesRange(uint(firmwareSize), int(startIdx), int(endIdx)); err != nil { + if err = check.BytesRange(uint(firmwareSizeUsed), int(startIdx), int(endIdx)); err != nil { err = fmt.Errorf("invalid entries bytes range: %w", err) return } @@ -246,27 +257,65 @@ func GetHeadersTableRangeFrom(firmware io.ReadSeeker) (startIdx, endIdx uint64, return } +func flashSizeFromIFD(firmware io.ReadSeeker, firmwareSize uint64) (uint64, error) { + if firmwareSize < uefi.FlashDescriptorLength { + return 0, fmt.Errorf("firmware size too small for flash descriptor: %d", firmwareSize) + } + + buf, err := sliceOrCopyBytesFrom(firmware, 0, uefi.FlashDescriptorLength, firmwareSize) + if err != nil { + return 0, fmt.Errorf("unable to read flash descriptor: %w", err) + } + + fd := uefi.FlashDescriptor{} + fd.SetBuf(buf) + if err := fd.ParseFlashDescriptor(); err != nil { + return 0, err + } + + var maxEnd uint64 + for _, fr := range fd.Region.FlashRegions { + if !fr.Valid() { + continue + } + end := uint64(fr.EndOffset()) + if end > maxEnd { + maxEnd = end + } + } + if maxEnd == 0 { + return 0, fmt.Errorf("no valid regions in flash descriptor") + } + + return maxEnd, nil +} + // GetTable returns the table of FIT entries of the firmware image. func GetTable(firmware []byte) (Table, error) { - return GetTableFrom(bytesextra.NewReadWriteSeeker(firmware)) + table, _, err := GetTableFrom(bytesextra.NewReadWriteSeeker(firmware)) + if err != nil { + return nil, err + } + + return table, nil } // GetTableFrom returns the table of FIT entries of the firmware image. -func GetTableFrom(firmware io.ReadSeeker) (Table, error) { - startIdx, endIdx, err := GetHeadersTableRangeFrom(firmware) +func GetTableFrom(firmware io.ReadSeeker) (Table, uint64, error) { + startIdx, endIdx, firmwareSizeUsed, err := GetHeadersTableRangeFrom(firmware) if err != nil { - return nil, fmt.Errorf("unable to locate the table coordinates (does the image contain FIT?): %w", err) + return nil, 0, fmt.Errorf("unable to locate the table coordinates (does the image contain FIT?): %w", err) } - tableBytes, err := sliceOrCopyBytesFrom(firmware, startIdx, endIdx) + tableBytes, err := sliceOrCopyBytesFrom(firmware, startIdx, endIdx, firmwareSizeUsed) if err != nil { - return nil, fmt.Errorf("unable to copy bytes from the firmware: %w", err) + return nil, 0, fmt.Errorf("unable to copy bytes from the firmware: %w", err) } result, err := ParseTable(tableBytes) if err != nil { - return nil, fmt.Errorf("unable to parse the table: %w", err) + return nil, 0, fmt.Errorf("unable to parse the table: %w", err) } - return result, nil + return result, firmwareSizeUsed, nil }