diff --git a/README.md b/README.md index 8453dd9f..ca250ee6 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ The Converged Security Suite implements all necessary tools for Intel platform s | Intel Trusted Execution Technology CBnT Extension | Missing | Supported | | Intel Boot Guard 1.0 | Supported | Supported | | Intel Boot Guard 2.0 | Supported | Supported | +| Intel Boot Guard 2.1 | Supported | Supported | | Intel Platform Firmware Resilience | N/A | Partly Supported | Documentation diff --git a/cmd/core/bg-prov/cmd.go b/cmd/core/bg-prov/cmd.go index 3f490b7b..6349e249 100644 --- a/cmd/core/bg-prov/cmd.go +++ b/cmd/core/bg-prov/cmd.go @@ -7,13 +7,9 @@ import ( "fmt" "os" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgkey" "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/bgheader" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" + keymanifest "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" log "github.com/sirupsen/logrus" @@ -32,46 +28,46 @@ type versionCmd struct{} type templateCmdv1 struct { Path string `arg:"" required:"" name:"path" help:"Path to the newly generated JSON configuration file." type:"path"` // CBnT Manifest Header args - SVN bg.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"` - ACMSVN bg.SVN `flag optional name:"acmsvn" help:"Authorized ACM Security Version Number"` - NEMS bgbootpolicy.Size4K `flag optional name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` + SVN cbnt.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"` + ACMSVN cbnt.SVN `flag optional name:"acmsvn" help:"Authorized ACM Security Version Number"` + NEMS bootpolicy.Size4K `flag optional name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` // IBB args - PBET bgbootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` - IBBSegFlags bgbootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` - MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` - VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` - PMRLBase uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` - PMRLLimit uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` - EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` - IbbHash string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithm. E.g.: SHA256, SHA384, SM3"` + PBET bootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` + IBBSegFlags bootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` + MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` + VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` + PMRLBase uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` + PMRLLimit uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` + EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` + IbbHash string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithm. E.g.: SHA256, SHA384, SM3"` } type templateCmdv2 struct { Path string `arg:"" required:"" name:"path" help:"Path to the newly generated JSON configuration file." type:"path"` // CBnT Manifest Header args - Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` - SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` - ACMSVN cbnt.SVN `flag:"" optional:"" name:"acmsvn" help:"Authorized ACM Security Version Number"` - NEMS cbntbootpolicy.Size4K `flag:"" optional:"" name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` + Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` + SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` + ACMSVN cbnt.SVN `flag:"" optional:"" name:"acmsvn" help:"Authorized ACM Security Version Number"` + NEMS bootpolicy.Size4K `flag:"" optional:"" name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` // IBB args - PBET cbntbootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` - IBBSegFlags cbntbootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` - MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` - VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` - DMABase0 uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` - DMASize0 uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` - DMABase1 uint64 `flag:"" optional:"" name:"dmabase1" help:"High DMA protected range base."` - DMASize1 uint64 `flag:"" optional:"" name:"dmasize1" help:"High DMA protected range limit."` - EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` - IbbHash []string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithms. E.g.: SHA256, SHA384, SM3"` + PBET bootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` + IBBSegFlags bootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` + MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` + VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` + DMABase0 uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` + DMASize0 uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` + DMABase1 uint64 `flag:"" optional:"" name:"dmabase1" help:"High DMA protected range base."` + DMASize1 uint64 `flag:"" optional:"" name:"dmasize1" help:"High DMA protected range limit."` + EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` + IbbHash []string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithms. E.g.: SHA256, SHA384, SM3"` // TXT args - SintMin uint8 `flag:"" optional:"" name:"sintmin" help:"OEM authorized SinitMinSvn value"` - TXTFlags cbntbootpolicy.TXTControlFlags `flag:"" optional:"" name:"txtflags" help:"TXT Element control flags"` - PowerDownInterval cbntbootpolicy.Duration16In5Sec `flag:"" optional:"" name:"powerdowninterval" help:"Duration of Power Down in 5 sec increments"` - ACPIBaseOffset uint16 `flag:"" optional:"" name:"acpibaseoffset" help:"ACPI IO offset."` - PowermBaseOffset uint32 `flag:"" optional:"" name:"powermbaseoffset" help:"ACPI MMIO offset."` - CMOSOff0 uint8 `flag:"" optional:"" name:"cmosoff0" help:"CMOS byte in bank 0 to store platform wakeup time"` - CMOSOff1 uint8 `flag:"" optional:"" name:"cmosoff1" help:"Second CMOS byte in bank 0 to store platform wakeup time"` + SintMin uint8 `flag:"" optional:"" name:"sintmin" help:"OEM authorized SinitMinSvn value"` + TXTFlags bootpolicy.TXTControlFlags `flag:"" optional:"" name:"txtflags" help:"TXT Element control flags"` + PowerDownInterval bootpolicy.Duration16In5Sec `flag:"" optional:"" name:"powerdowninterval" help:"Duration of Power Down in 5 sec increments"` + ACPIBaseOffset uint16 `flag:"" optional:"" name:"acpibaseoffset" help:"ACPI IO offset."` + PowermBaseOffset uint32 `flag:"" optional:"" name:"powermbaseoffset" help:"ACPI MMIO offset."` + CMOSOff0 uint8 `flag:"" optional:"" name:"cmosoff0" help:"CMOS byte in bank 0 to store platform wakeup time"` + CMOSOff1 uint8 `flag:"" optional:"" name:"cmosoff1" help:"Second CMOS byte in bank 0 to store platform wakeup time"` } type kmPrintCmd struct { @@ -154,34 +150,34 @@ type generateACMCmdv0 struct { } type generateKMCmdv1 struct { - KM string `arg:"" required:"" name:"km" help:"Path to the newly generated Key Manifest binary file." type:"path"` - Key string `arg:"" required:"" name:"key" help:"Public signing key"` - Config string `flag:"" optional:"" name:"config" help:"Path to the JSON config file." type:"path"` - SVN bg.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` - ID uint8 `flag:"" optional:"" name:"id" help:"The key Manifest Identifier"` - PKHashAlg string `flag:"" optional:"" name:"pkhashalg" help:"Hash algorithm of OEM public key digest. E.g.: SHA256, SHA384, SM3"` + KM string `arg:"" required:"" name:"km" help:"Path to the newly generated Key Manifest binary file." type:"path"` + Key string `arg:"" required:"" name:"key" help:"Public signing key"` + Config string `flag:"" optional:"" name:"config" help:"Path to the JSON config file." type:"path"` + SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` + ID uint8 `flag:"" optional:"" name:"id" help:"The key Manifest Identifier"` + PKHashAlg string `flag:"" optional:"" name:"pkhashalg" help:"Hash algorithm of OEM public key digest. E.g.: SHA256, SHA384, SM3"` // KMHash bg.HashStructure `flag optional:"" name:"kmhash" help:"Key hash for BPM, ACM, uCode etc"` BpmPubkey string `flag:"" optional:"" name:"bpmpubkey" help:"Path to bpm public signing key"` - BpmHashAlg string `flag:"" optional:"" name:"bpmhashalgo" help:"Hash algorithm for bpm public signing cbntkey.. E.g.: SHA256, SHA384, SM3"` + BpmHashAlg string `flag:"" optional:"" name:"bpmhashalgo" help:"Hash algorithm for bpm public signing keymanifest.. E.g.: SHA256, SHA384, SM3"` Out string `flag:"" optional:"" name:"out" help:"Path to write applied config to"` Cut bool `flag:"" optional:"" name:"cut" help:"Cuts the signature before writing to binary."` PrintME bool `flag:"" optional:"" name:"printme" help:"Prints the hash of KM public signing key"` } type generateKMCmdv2 struct { - KM string `arg:"" required:"" name:"km" help:"Path to the newly generated Key Manifest binary file." type:"path"` - Key string `arg:"" required:"" name:"key" help:"Public signing key"` - Config string `flag:"" optional:"" name:"config" help:"Path to the JSON config file." type:"path"` - Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` - SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` - ID uint8 `flag:"" optional:"" name:"id" help:"The key Manifest Identifier"` - PKHashAlg string `flag:"" optional:"" name:"pkhashalg" help:"Hash algorithm of OEM public key digest. E.g.: SHA256, SHA384, SM3"` - KMHashes []cbntkey.Hash `flag:"" optional:"" name:"kmhashes" help:"Key hashes for BPM, ACM, uCode etc"` - BpmPubkey string `flag:"" optional:"" name:"bpmpubkey" help:"Path to bpm public signing key"` - BpmHashAlg string `flag:"" optional:"" name:"bpmhashalgo" help:"Hash algorithm for bpm public signing cbntkey.. E.g.: SHA256, SHA384, SM3"` - Out string `flag:"" optional:"" name:"out" help:"Path to write applied config to"` - Cut bool `flag:"" optional:"" name:"cut" help:"Cuts the signature before writing to binary."` - PrintME bool `flag:"" optional:"" name:"printme" help:"Prints the hash of KM public signing key"` + KM string `arg:"" required:"" name:"km" help:"Path to the newly generated Key Manifest binary file." type:"path"` + Key string `arg:"" required:"" name:"key" help:"Public signing key"` + Config string `flag:"" optional:"" name:"config" help:"Path to the JSON config file." type:"path"` + Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` + SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` + ID uint8 `flag:"" optional:"" name:"id" help:"The key Manifest Identifier"` + PKHashAlg string `flag:"" optional:"" name:"pkhashalg" help:"Hash algorithm of OEM public key digest. E.g.: SHA256, SHA384, SM3"` + KMHashes []keymanifest.Hash `flag:"" optional:"" name:"kmhashes" help:"Key hashes for BPM, ACM, uCode etc"` + BpmPubkey string `flag:"" optional:"" name:"bpmpubkey" help:"Path to bpm public signing key"` + BpmHashAlg string `flag:"" optional:"" name:"bpmhashalgo" help:"Hash algorithm for bpm public signing keymanifest.. E.g.: SHA256, SHA384, SM3"` + Out string `flag:"" optional:"" name:"out" help:"Path to write applied config to"` + Cut bool `flag:"" optional:"" name:"cut" help:"Cuts the signature before writing to binary."` + PrintME bool `flag:"" optional:"" name:"printme" help:"Prints the hash of KM public signing key"` } type generateBPMCmdv1 struct { @@ -189,20 +185,20 @@ type generateBPMCmdv1 struct { BIOS string `arg:"" required:"" name:"bios" help:"Path to the full BIOS binary file." type:"path"` Config string `flag:"" optional:"" name:"config" help:"Path to the JSON config file." type:"path"` // CBnT Manifest Header args - Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` - SVN bg.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` - ACMSVN bg.SVN `flag:"" optional:"" name:"acmsvn" help:"Authorized ACM Security Version Number"` - NEMS bgbootpolicy.Size4K `flag:"" optional:"" name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` + Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` + SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` + ACMSVN cbnt.SVN `flag:"" optional:"" name:"acmsvn" help:"Authorized ACM Security Version Number"` + NEMS bootpolicy.Size4K `flag:"" optional:"" name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` // IBB args - PBET bgbootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` - IBBSegFlags bgbootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` - MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` - VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` - PMRLBase uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` - PMRLLimit uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` - EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` - IbbHash string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithm. Valid options: SHA256, SHA384, SM3"` - IbbSegFlag uint16 `flag:"" optional:"" name:"ibbsegflag" help:"Reducted"` + PBET bootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` + IBBSegFlags bootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` + MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` + VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` + PMRLBase uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` + PMRLLimit uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` + EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` + IbbHash string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithm. Valid options: SHA256, SHA384, SM3"` + IbbSegFlag uint16 `flag:"" optional:"" name:"ibbsegflag" help:"Reducted"` Out string `flag:"" optional:"" name:"out" help:"Path to write applied config to"` Cut bool `flag:"" optional:"" name:"cut" help:"Cuts the signature before writing to binary."` @@ -213,30 +209,30 @@ type generateBPMCmdv2 struct { BIOS string `arg:"" required:"" name:"bios" help:"Path to the full BIOS binary file." type:"path"` Config string `flag:"" optional:"" name:"config" help:"Path to the JSON config file." type:"path"` // CBnT Manifest Header args - Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` - SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` - ACMSVN cbnt.SVN `flag:"" optional:"" name:"acmsvn" help:"Authorized ACM Security Version Number"` - NEMS cbntbootpolicy.Size4K `flag:"" optional:"" name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` + Revision uint8 `flag:"" optional:"" name:"revision" help:"Platform Manufacturer’s BPM revision number."` + SVN cbnt.SVN `flag:"" optional:"" name:"svn" help:"Boot Policy Manifest Security Version Number"` + ACMSVN cbnt.SVN `flag:"" optional:"" name:"acmsvn" help:"Authorized ACM Security Version Number"` + NEMS bootpolicy.Size4K `flag:"" optional:"" name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"` // IBB args - PBET cbntbootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` - IBBSegFlags cbntbootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` - MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` - VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` - DMABase0 uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` - DMASize0 uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` - DMABase1 uint64 `flag:"" optional:"" name:"dmabase1" help:"High DMA protected range base."` - DMASize1 uint64 `flag:"" optional:"" name:"dmasize1" help:"High DMA protected range limit."` - EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` - IbbHash []string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithm. Valid options: SHA256, SHA384, SM3"` - IbbSegFlag uint16 `flag:"" optional:"" name:"ibbsegflag" help:"Reducted"` + PBET bootpolicy.PBETValue `flag:"" optional:"" name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."` + IBBSegFlags bootpolicy.SEFlags `flag:"" optional:"" name:"ibbflags" help:"IBB Control flags"` + MCHBAR uint64 `flag:"" optional:"" name:"mchbar" help:"MCHBAR address"` + VDTBAR uint64 `flag:"" optional:"" name:"vdtbar" help:"VTDPVC0BAR address"` + DMABase0 uint32 `flag:"" optional:"" name:"dmabase0" help:"Low DMA protected range base"` + DMASize0 uint32 `flag:"" optional:"" name:"dmasize0" help:"Low DMA protected range limit"` + DMABase1 uint64 `flag:"" optional:"" name:"dmabase1" help:"High DMA protected range base."` + DMASize1 uint64 `flag:"" optional:"" name:"dmasize1" help:"High DMA protected range limit."` + EntryPoint uint32 `flag:"" optional:"" name:"entrypoint" help:"IBB (Startup BIOS) entry point"` + IbbHash []string `flag:"" optional:"" name:"ibbhash" help:"IBB Hash Algorithm. Valid options: SHA256, SHA384, SM3"` + IbbSegFlag uint16 `flag:"" optional:"" name:"ibbsegflag" help:"Reducted"` // TXT args - SinitMin uint8 `flag:"" optional:"" name:"sinitmin" help:"OEM authorized SinitMinSvn value"` - TXTFlags cbntbootpolicy.TXTControlFlags `flag:"" optional:"" name:"txtflags" help:"TXT Element control flags"` - PowerDownInterval cbntbootpolicy.Duration16In5Sec `flag:"" optional:"" name:"powerdowninterval" help:"Duration of Power Down in 5 sec increments"` - ACPIBaseOffset uint16 `flag:"" optional:"" name:"acpibaseoffset" help:"ACPI IO offset."` - PowermBaseOffset uint32 `flag:"" optional:"" name:"powermbaseoffset" help:"ACPI MMIO offset."` - CMOSOff0 uint8 `flag:"" optional:"" name:"cmosoff0" help:"CMOS byte in bank 0 to store platform wakeup time"` - CMOSOff1 uint8 `flag:"" optional:"" name:"cmosoff1" help:"Second CMOS byte in bank 0 to store platform wakeup time"` + SinitMin uint8 `flag:"" optional:"" name:"sinitmin" help:"OEM authorized SinitMinSvn value"` + TXTFlags bootpolicy.TXTControlFlags `flag:"" optional:"" name:"txtflags" help:"TXT Element control flags"` + PowerDownInterval bootpolicy.Duration16In5Sec `flag:"" optional:"" name:"powerdowninterval" help:"Duration of Power Down in 5 sec increments"` + ACPIBaseOffset uint16 `flag:"" optional:"" name:"acpibaseoffset" help:"ACPI IO offset."` + PowermBaseOffset uint32 `flag:"" optional:"" name:"powermbaseoffset" help:"ACPI MMIO offset."` + CMOSOff0 uint8 `flag:"" optional:"" name:"cmosoff0" help:"CMOS byte in bank 0 to store platform wakeup time"` + CMOSOff1 uint8 `flag:"" optional:"" name:"cmosoff1" help:"Second CMOS byte in bank 0 to store platform wakeup time"` Out string `flag:"" optional:"" name:"out" help:"Path to write applied config to"` Cut bool `flag:"" optional:"" name:"cut" help:"Cuts the signature before writing to binary."` @@ -429,20 +425,26 @@ func (bpme *bpmExportCmd) Run(ctx *context) error { func (g *generateKMCmdv1) Run(ctx *context) error { var b bootguard.BootGuard - b.Version = bgheader.Version10 + b.Version = cbnt.Version10 if g.Config != "" { err := b.ReadJSON(g.Config) if err != nil { return fmt.Errorf("unable to read JSON config file: %w", err) } } else { - var err error - b.VData.BGkm = bgkey.NewManifest() - b.VData.BGkm.KMSVN = g.SVN - b.VData.BGkm.KMID = g.ID + bgkm, err := keymanifest.NewManifest(cbnt.Version10) if err != nil { return err } + km, ok := bgkm.(*keymanifest.BGManifest) + if !ok { + return fmt.Errorf("could not assert the type for KM") + } + b.VData.BGkm = km + + b.VData.BGkm.KMSVN = g.SVN + b.VData.BGkm.KMID = g.ID + // b.VData.BGkm.BPKey = g.KMHash // Create KM_Hash for BPM pub signing key if g.BpmPubkey != "" { @@ -455,7 +457,7 @@ func (g *generateKMCmdv1) Run(ctx *context) error { return err } } else { - return fmt.Errorf("add --bpmpubkey= as argument") + return fmt.Errorf("add --bpmpubkey= as argument") } } key, err := bootguard.ReadPubKey(g.Key) @@ -466,7 +468,11 @@ func (g *generateKMCmdv1) Run(ctx *context) error { return err } if g.PrintME { - if b.VData.BGkm.KeyAndSignature.Signature.DataTotalSize() > 1 { + sigDataSize, err := b.VData.BGkm.KeyAndSignature.Signature.SizeOf(4) + if err != nil { + return err + } + if sigDataSize > 1 { if err := b.VData.BGkm.KeyAndSignature.Key.PrintKMPubKey(b.VData.BGkm.BPKey.HashAlg); err != nil { return err } @@ -487,7 +493,11 @@ func (g *generateKMCmdv1) Run(ctx *context) error { } if g.Cut { // Cut signature from binary - bKM = bKM[:int(b.VData.BGkm.KeyAndSignatureOffset())] + off, err := b.VData.BGkm.OffsetOf(5) + if err != nil { + return err + } + bKM = bKM[:int(off)] } if err = os.WriteFile(g.KM, bKM, 0o600); err != nil { return fmt.Errorf("unable to write KM to file: %w", err) @@ -497,15 +507,22 @@ func (g *generateKMCmdv1) Run(ctx *context) error { func (g *generateKMCmdv2) Run(ctx *context) error { var b bootguard.BootGuard - b.Version = bgheader.Version20 + b.Version = cbnt.Version20 if g.Config != "" { err := b.ReadJSON(g.Config) if err != nil { return err } } else { - var err error - b.VData.CBNTkm = cbntkey.NewManifest() + cbntkm, err := keymanifest.NewManifest(cbnt.Version20) + if err != nil { + return err + } + km, ok := cbntkm.(*keymanifest.CBnTManifest) + if !ok { + return fmt.Errorf("could not assert KM type") + } + b.VData.CBNTkm = km b.VData.CBNTkm.Revision = g.Revision b.VData.CBNTkm.KMSVN = g.SVN b.VData.CBNTkm.KMID = g.ID @@ -536,7 +553,12 @@ func (g *generateKMCmdv2) Run(ctx *context) error { return err } if g.PrintME { - if b.VData.CBNTkm.KeyAndSignature.Signature.DataTotalSize() > 1 { + sigDataSize, err := b.VData.CBNTkm.KeyAndSignature.Signature.SizeOf(4) + if err != nil { + return err + } + + if sigDataSize > 1 { if err := b.VData.CBNTkm.KeyAndSignature.Key.PrintKMPubKey(b.VData.CBNTkm.PubKeyHashAlg); err != nil { return err } @@ -567,20 +589,35 @@ func (g *generateKMCmdv2) Run(ctx *context) error { func (g *generateBPMCmdv1) Run(ctx *context) error { var b bootguard.BootGuard - b.Version = bgheader.Version10 + b.Version = cbnt.Version10 if g.Config != "" { err := b.ReadJSON(g.Config) if err != nil { return err } } else { - b.VData.BGbpm = bgbootpolicy.NewManifest() - b.VData.BGbpm.BPMH = *bgbootpolicy.NewBPMH() - b.VData.BGbpm.BPMH.BPMSVN = g.SVN - b.VData.BGbpm.BPMH.ACMSVNAuth = g.ACMSVN - b.VData.BGbpm.BPMH.NEMDataStack = g.NEMS + bgbpm, err := bootpolicy.NewManifest(cbnt.Version10) + if err != nil { + return err + } + bpmh, err := bootpolicy.NewBPMH(cbnt.Version10) + bp, ok := bpmh.(*bootpolicy.BPMHBG) + if !ok { + return fmt.Errorf("could not assert BPMH type") + } + + bpm, ok := bgbpm.(*bootpolicy.ManifestBG) + if !ok { + return fmt.Errorf("could not assert BPM type") + } - b.VData.BGbpm.SE = make([]bgbootpolicy.SE, 1) + b.VData.BGbpm = bpm + b.VData.BGbpm.BPMHBG = *bp + b.VData.BGbpm.BPMHBG.BPMSVN = g.SVN + b.VData.BGbpm.BPMHBG.ACMSVNAuth = g.ACMSVN + b.VData.BGbpm.BPMHBG.NEMDataStack = g.NEMS + + b.VData.BGbpm.SE = make([]bootpolicy.SEBG, 1) b.VData.BGbpm.SE[0].PBETValue = g.PBET b.VData.BGbpm.SE[0].Flags = g.IBBSegFlags b.VData.BGbpm.SE[0].IBBMCHBAR = g.MCHBAR @@ -588,7 +625,7 @@ func (g *generateBPMCmdv1) Run(ctx *context) error { b.VData.BGbpm.SE[0].PMRLBase = g.PMRLBase b.VData.BGbpm.SE[0].PMRLLimit = g.PMRLLimit b.VData.BGbpm.SE[0].IBBEntryPoint = g.EntryPoint - hashAlgo, err := bg.GetAlgFromString(g.IbbHash) + hashAlgo, err := cbnt.GetAlgFromString(g.IbbHash) if err != nil { return err } @@ -616,7 +653,11 @@ func (g *generateBPMCmdv1) Run(ctx *context) error { return err } if g.Cut { - bBPM = bBPM[:b.VData.BGbpm.PMSE.KeySignatureOffset()] + off, err := b.VData.BGbpm.PMSE.OffsetOf(1) + if err != nil { + return err + } + bBPM = bBPM[:int(off)] } if err = os.WriteFile(g.BPM, bBPM, 0o600); err != nil { return fmt.Errorf("unable to write BPM to file: %w", err) @@ -626,21 +667,36 @@ func (g *generateBPMCmdv1) Run(ctx *context) error { func (g *generateBPMCmdv2) Run(ctx *context) error { var b bootguard.BootGuard - b.Version = bgheader.Version20 + b.Version = cbnt.Version20 if g.Config != "" { err := b.ReadJSON(g.Config) if err != nil { return err } } else { - b.VData.CBNTbpm = cbntbootpolicy.NewManifest() - b.VData.CBNTbpm.BPMH = *cbntbootpolicy.NewBPMH() - b.VData.CBNTbpm.BPMH.BPMRevision = g.Revision - b.VData.CBNTbpm.BPMH.BPMSVN = g.SVN - b.VData.CBNTbpm.BPMH.ACMSVNAuth = g.ACMSVN - b.VData.CBNTbpm.BPMH.NEMDataStack = g.NEMS - - b.VData.CBNTbpm.SE = make([]cbntbootpolicy.SE, 1) + cbntbpm, err := bootpolicy.NewManifest(cbnt.Version20) + if err != nil { + return err + } + bpmh, err := bootpolicy.NewBPMH(cbnt.Version20) + bp, ok := bpmh.(*bootpolicy.BPMHCBnT) + if !ok { + return fmt.Errorf("could not assert BPMH type") + } + + bpm, ok := cbntbpm.(*bootpolicy.ManifestCBnT) + if !ok { + return fmt.Errorf("could not assert BPM type") + } + + b.VData.CBNTbpm = bpm + b.VData.CBNTbpm.BPMHCBnT = *bp + b.VData.CBNTbpm.BPMHCBnT.BPMRevision = g.Revision + b.VData.CBNTbpm.BPMHCBnT.BPMSVN = g.SVN + b.VData.CBNTbpm.BPMHCBnT.ACMSVNAuth = g.ACMSVN + b.VData.CBNTbpm.BPMHCBnT.NEMDataStack = g.NEMS + + b.VData.CBNTbpm.SE = make([]bootpolicy.SECBnT, 1) b.VData.CBNTbpm.SE[0].PBETValue = g.PBET b.VData.CBNTbpm.SE[0].Flags = g.IBBSegFlags b.VData.CBNTbpm.SE[0].IBBMCHBAR = g.MCHBAR @@ -663,11 +719,11 @@ func (g *generateBPMCmdv2) Run(ctx *context) error { for iterator := range b.VData.CBNTbpm.SE[0].DigestList.List { b.VData.CBNTbpm.SE[0].DigestList.List[iterator].HashAlg = ibbhashalgs[iterator] } - err := b.CreateIBBSegments(0, g.IbbSegFlag, g.BIOS) + err = b.CreateIBBSegments(0, g.IbbSegFlag, g.BIOS) if err != nil { return err } - txt := cbntbootpolicy.NewTXT() + txt := bootpolicy.NewTXT() txt.SInitMinSVNAuth = g.SinitMin txt.ControlFlags = g.TXTFlags txt.PwrDownInterval = g.PowerDownInterval @@ -695,7 +751,11 @@ func (g *generateBPMCmdv2) Run(ctx *context) error { return err } if g.Cut { - bBPM = bBPM[:b.VData.CBNTbpm.KeySignatureOffset] + off, err := b.VData.CBNTbpm.PMSE.OffsetOf(1) + if err != nil { + return err + } + bBPM = bBPM[:int(off)] } if err = os.WriteFile(g.BPM, bBPM, 0o600); err != nil { return fmt.Errorf("unable to write BPM to file: %w", err) @@ -919,15 +979,41 @@ func (s *signBPMCmd) Run(ctx *context) error { func (t *templateCmdv2) Run(ctx *context) error { var vdata bootguard.VersionedData - vdata.CBNTbpm = cbntbootpolicy.NewManifest() - vdata.CBNTkm = cbntkey.NewManifest() - vdata.CBNTbpm.BPMH.BPMRevision = t.Revision - vdata.CBNTbpm.BPMH.BPMSVN = t.SVN - vdata.CBNTbpm.BPMH.ACMSVNAuth = t.ACMSVN - vdata.CBNTbpm.BPMH.NEMDataStack = t.NEMS + cbntbpm, err := bootpolicy.NewManifest(cbnt.Version20) + if err != nil { + return err + } + bpm, ok := cbntbpm.(*bootpolicy.ManifestCBnT) + if !ok { + return fmt.Errorf("could not assert BPM type") + } + vdata.CBNTbpm = bpm + + cbntkm, err := keymanifest.NewManifest(cbnt.Version20) + if err != nil { + return err + } + km, ok := cbntkm.(*keymanifest.CBnTManifest) + if !ok { + return fmt.Errorf("could not assert KM type") + } + vdata.CBNTkm = km + + vdata.CBNTbpm.BPMHCBnT.BPMRevision = t.Revision + vdata.CBNTbpm.BPMHCBnT.BPMSVN = t.SVN + vdata.CBNTbpm.BPMHCBnT.ACMSVNAuth = t.ACMSVN + vdata.CBNTbpm.BPMHCBnT.NEMDataStack = t.NEMS + + seCommon, err := bootpolicy.NewSE(cbnt.Version20) + if err != nil { + return err + } - se := cbntbootpolicy.NewSE() + se, ok := seCommon.(*bootpolicy.SECBnT) + if !ok { + return err + } se.PBETValue = t.PBET se.Flags = t.IBBSegFlags se.IBBMCHBAR = t.MCHBAR @@ -954,7 +1040,7 @@ func (t *templateCmdv2) Run(ctx *context) error { vdata.CBNTbpm.SE = append(vdata.CBNTbpm.SE, *se) - txt := cbntbootpolicy.NewTXT() + txt := bootpolicy.NewTXT() txt.SInitMinSVNAuth = t.SintMin txt.ControlFlags = t.TXTFlags txt.PwrDownInterval = t.PowerDownInterval @@ -987,16 +1073,42 @@ func (t *templateCmdv2) Run(ctx *context) error { func (t *templateCmdv1) Run(ctx *context) error { var vdata bootguard.VersionedData - var err error + var ok bool - vdata.BGbpm = bgbootpolicy.NewManifest() - vdata.BGkm = bgkey.NewManifest() + bgbpm, err := bootpolicy.NewManifest(cbnt.Version10) + if err != nil { + return err + } + bpm, ok := bgbpm.(*bootpolicy.ManifestBG) + if !ok { + return fmt.Errorf("could not assert BPM type") + } + vdata.BGbpm = bpm - vdata.BGbpm.BPMH.BPMSVN = t.SVN - vdata.BGbpm.BPMH.ACMSVNAuth = t.ACMSVN - vdata.BGbpm.BPMH.NEMDataStack = t.NEMS + bgkm, err := keymanifest.NewManifest(cbnt.Version10) + if err != nil { + return err + } + km, ok := bgkm.(*keymanifest.BGManifest) + if !ok { + return fmt.Errorf("could not assert KM type") + } + vdata.BGkm = km + + vdata.BGbpm.BPMHBG.BPMSVN = t.SVN + vdata.BGbpm.BPMHBG.ACMSVNAuth = t.ACMSVN + vdata.BGbpm.BPMHBG.NEMDataStack = t.NEMS + + seCommon, err := bootpolicy.NewSE(cbnt.Version10) + if err != nil { + return err + } + + se, ok := seCommon.(*bootpolicy.SEBG) + if !ok { + return err + } - se := bgbootpolicy.NewSE() se.PBETValue = t.PBET se.Flags = t.IBBSegFlags se.IBBMCHBAR = t.MCHBAR @@ -1004,7 +1116,7 @@ func (t *templateCmdv1) Run(ctx *context) error { se.PMRLBase = t.PMRLBase se.PMRLLimit = t.PMRLLimit se.IBBEntryPoint = t.EntryPoint - se.Digest.HashAlg, err = bg.GetAlgFromString(t.IbbHash) + se.Digest.HashAlg, err = cbnt.GetAlgFromString(t.IbbHash) if err != nil { return err } diff --git a/cmd/core/bg-suite/README.md b/cmd/core/bg-suite/README.md index 9454fa83..fd905557 100644 --- a/cmd/core/bg-suite/README.md +++ b/cmd/core/bg-suite/README.md @@ -11,6 +11,11 @@ Prerequisites for Usage ----------------------- Supported OS: Any Linux distribution +It is recommended to run this utility on the Intel platform for both, static +and runtime tests. Otherwise, please ignore `BgVersion` field in the output `test_log.json` file. +Additionally, runtime tests are only giving reliable results when executed on the platform running the +firmware provided for the static tests. + **1. Load the MSR kernel module.** Load the *msr* kernel module: diff --git a/cmd/core/bg-suite/TESTPLAN.md b/cmd/core/bg-suite/TESTPLAN.md index 4f7baaf1..42639888 100644 --- a/cmd/core/bg-suite/TESTPLAN.md +++ b/cmd/core/bg-suite/TESTPLAN.md @@ -1,10 +1,56 @@ -Id | Test | Implemented | Document | Chapter -------------|------------|------------|------------|------------ -00 | FIT meets BootGuard requirements | :white_check_mark: | Document 599500 Revision 1.2 | -01 | SACM meets sane BootGuard requirements | :white_check_mark: | Document 315168-017 | Chapter A. Authenticated Code Modules -02 | Key Manifest meets sane BootGuard requirements | :white_check_mark: | Document 557867 / 575623 | -03 | Boot Policy Manifest meets sane BootGuard requirements | :white_check_mark: | Document 557867 / 575623 | -04 | Verifies BPM and IBBs match firmware image | :white_check_mark: | Document 557867 / 575623 | -05 | [RUNTIME] Validates Intel ME specific configuration against KM/BPM in firmware image | :white_check_mark: | Document 557867 / 575623 | -06 | [RUNTIME] Verifies Intel ME Boot Guard configuration is sane and safe | :white_check_mark: | Document 557867 / 575623 | -07 | [RUNTIME] BtG/TXT registers are sane | :white_check_mark: | Document 315168-017 | +Id | Test | Implemented | Document | Chapter | Supported BG/CBnt Version +------------|------------|------------|------------|------------|----------- +00 | FIT meets BootGuard requirements | :white_check_mark: | Document 599500 Revision 1.2 | | 1.0 / 2.0 / 2.1 +01 | SACM meets sane BootGuard requirements | :white_check_mark: | Document 315168-017 | Chapter A. Authenticated Code Module | 1.0 / 2.0 / 2.1 +02 | Key Manifest meets sane BootGuard requirements | :white_check_mark: | Document 557867 / 575623 | | 1.0 / 2.0 / 2.1 +03 | Boot Policy Manifest meets sane BootGuard requirements | :white_check_mark: | Document 557867 / 575623 | | 1.0 / 2.0 / 2.1 +04 | Verifies BPM and IBBs match firmware image | :white_check_mark: | Document 557867 / 575623 | | 1.0 / 2.0 / 2.1 +05 | [RUNTIME] Validates Intel ME specific configuration against KM/BPM in firmware image | :white_check_mark: | Document 557867 / 575623 | | 1.0 / 2.0 +06 | [RUNTIME] Verifies Intel ME Boot Guard status | :white_check_mark: | Document 729124 / 829718 | | 2.1 +07 | [RUNTIME] Verifies Intel ME Boot Guard configuration is sane and safe | :white_check_mark: | Document 557867 / 575623 / 829718 / 729124 | 1.0 / 2.0 / 2.1 +08 | [RUNTIME] Verifies post-boot ACM status | :white_check_mark: | Document 315168-017 / 575623 rev 1.5 | | 1.0 / 2.0 +09 | [RUNTIME] Verifies post-boot BtG/TXT registers | :white_check_mark: | Document 315168-017 / 575623 rev 1.9 | | 1.0 / 2.0 / 2.1 + +## Differences in Test Logic Between BG/CBnT 2.0 and CBnT 2.1 + +Platforms starting from MTL follow CBnT 2.1 specification. This brings additional logic changes in the tests +given above: + - Test 03: As per Document 575623 rev. 1.9, section 5.3.3.5, PCDE and its sub-structures (PDRS and CBNS) are mandatory +in CBnT 2.1. Therefore on "Validate BPM structure" step, they are also included. Additionally, extending PCR7 to the operating system was deprecated +with MTL, therefore with CBnT 2.1, this check is skipped. +> [!NOTE] +> While this extending PCR7 authority is recommended for older platforms, it breaks the BitLocker on Windows >=10. Thus, it is often not set by the OEM, +> and should be treated as information rather than hard error. + + - Test 05: Platforms that conform to CBnT 2.1 use different specification of Intel ME (18 or above). These do not expose neither +SVN for KM and BPM, nor KMID (see Documents 729124/829718). Therefore, Test 05 for CBnT 2.1 is skipped. Instead, Test 6 is available. +It checks BootGuard status exposed by Intel ME, which is equivalent to runtime validation of BootGuard startup process. + + - Test 07: Similarly as with Test 05, some information are not exposed in Intel ME 18 and above, see below: + - Bypass Boot Policy + - Boot Policy validity + - Error Enforcement Policy + - Protected BIOS environment status + - BootGuard disabled bit + Instead, the following is checked with Intel ME 18 and above: + - FPF lock + - Debug Mode status + - Validity of the bits that follow + - Whether RCS origin is ACM + - CPU Debug status + - ME working state correctness + - ME operating mode correctness + + - Test 08: The check for ACM status by reading `txtSpace >> 0x328` only gives a meaningful + results if TXT is disabled in BIOS by the user. Otherwise, the same address will be + used as `TXT.ERRORCODE` register, and filled with the TXT status. Now given that TXT started + successfully, bit 31 will change the meaning, i.e. if set, there is some error that we could + further evaluate, otherwise we shall ignore the rest. Therefore, Test 07 is limited to BG 1.0 + and CBnT 2.0. + +- Test 09: Available for all specifications, same as Test 08, but without the ACM status check if TXT is enabled, and +with the additional checks of BootStatus register, namely: + - Boot Guard startup status + - whether BIOS is considered trusted + - whether CPU error occurred + - SACM startup status. diff --git a/cmd/core/bg-suite/cmd.go b/cmd/core/bg-suite/cmd.go index fb25d37c..58b2495d 100644 --- a/cmd/core/bg-suite/cmd.go +++ b/cmd/core/bg-suite/cmd.go @@ -5,10 +5,12 @@ import ( "fmt" "os" "regexp" + "slices" "sort" "strconv" "strings" + "github.com/9elements/converged-security-suite/v2/pkg/intel" "github.com/9elements/converged-security-suite/v2/pkg/test" "github.com/9elements/converged-security-suite/v2/pkg/tools" log "github.com/sirupsen/logrus" @@ -48,6 +50,12 @@ var cli struct { func (e *execTestsCmd) Run(ctx *context) error { ret := false + bgver := intel.RuntimeBGVersion() + log.Infof("Runtime BG/CBnT version: %s", bgver) + if bgver == intel.Unknown { + log.Warn("Unable to map CPU model to Boot Guard/CBnT generation") + } + data, err := os.ReadFile(e.Firmware) if err != nil { return fmt.Errorf("can't read firmware file") @@ -59,7 +67,7 @@ func (e *execTestsCmd) Run(ctx *context) error { } switch e.Set { case "all": - log.Info("For more information about the documents and chapters, run: bg-suite -m") + log.Info("For more information about the documents and chapters, run: bg-suite markdown") ret = run("All", getTests(), &preset, e.Interactive) case "static": ret = run("Static", getStaticTest(), &preset, e.Interactive) @@ -97,6 +105,10 @@ func (e *execTestsCmd) Run(ctx *context) error { func (l *listCmd) Run(ctx *context) error { tests := getTests() for i := range tests { + if tests[i].Description != "" { + log.Infof("Test No: %v, %v - %v", i, tests[i].Name, tests[i].Description) + continue + } log.Infof("Test No: %v, %v", i, tests[i].Name) } return nil @@ -106,8 +118,8 @@ func (m *markdownCmd) Run(ctx *context) error { var teststate string tests := getTests() - log.Info("Id | Test | Implemented | Document | Chapter") - log.Info("------------|------------|------------|------------|------------") + log.Info("Id | Test | Description | Implemented | Document | Chapter") + log.Info("------------|------------|------------|------------|------------|------------") for i := range tests { if tests[i].Status == test.Implemented { @@ -121,7 +133,7 @@ func (m *markdownCmd) Run(ctx *context) error { if docID != "" { docID = "Document " + docID } - log.Infof("%02d | %-48s | %-22s | %-28s | %-56s", i, tests[i].Name, teststate, docID, tests[i].SpecificationChapter) + log.Infof("%02d | %-48s | %-52s | %-22s | %-28s | %-56s", i, tests[i].Name, tests[i].Description, teststate, docID, tests[i].SpecificationChapter) } return nil } @@ -133,7 +145,15 @@ func (v *versionCmd) Run(ctx *context) error { func getTests() []*test.Test { var tests []*test.Test + bgver := intel.RuntimeBGVersion() + for i := range test.TestsBootGuard { + if strings.HasPrefix(test.TestsBootGuard[i].Name, "[RUNTIME]") { + if slices.Contains(test.TestsBootGuard[i].SupportedVersion, bgver) { + tests = append(tests, test.TestsBootGuard[i]) + } + continue + } tests = append(tests, test.TestsBootGuard[i]) } return tests @@ -151,9 +171,13 @@ func getStaticTest() []*test.Test { func getRuntimeTest() []*test.Test { var tests []*test.Test + bgver := intel.RuntimeBGVersion() + for i := range test.TestsBootGuard { if strings.HasPrefix(test.TestsBootGuard[i].Name, "[RUNTIME]") { - tests = append(tests, test.TestsBootGuard[i]) + if slices.Contains(test.TestsBootGuard[i].SupportedVersion, bgver) { + tests = append(tests, test.TestsBootGuard[i]) + } } } return tests @@ -188,9 +212,18 @@ func run(testGroup string, tests []*test.Test, preset *test.PreSet, interactive if !interactive { var t []temptest + bgVersion := string(intel.RuntimeBGVersion()) for index := range tests { if tests[index].Status != test.NotImplemented { - ttemp := temptest{index, tests[index].Name, tests[index].Result.String(), tests[index].ErrorText, tests[index].Status.String()} + ttemp := temptest{ + Testnumber: index, + Testname: tests[index].Name, + Description: tests[index].Description, + BgVersion: bgVersion, + Result: tests[index].Result.String(), + Error: tests[index].ErrorText, + Status: tests[index].Status.String(), + } t = append(t, ttemp) } } diff --git a/cmd/core/bg-suite/main.go b/cmd/core/bg-suite/main.go index 482be1fd..45aed547 100644 --- a/cmd/core/bg-suite/main.go +++ b/cmd/core/bg-suite/main.go @@ -23,11 +23,13 @@ var ( ) type temptest struct { - Testnumber int - Testname string - Result string - Error string - Status string + Testnumber int + Testname string + Description string + BgVersion string + Result string + Error string + Status string } func main() { diff --git a/pkg/bootflow/conditions/biosconds/intelconds/valid_bpm.go b/pkg/bootflow/conditions/biosconds/intelconds/valid_bpm.go index 90632a4c..55497a73 100644 --- a/pkg/bootflow/conditions/biosconds/intelconds/valid_bpm.go +++ b/pkg/bootflow/conditions/biosconds/intelconds/valid_bpm.go @@ -6,8 +6,8 @@ import ( "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/types" - bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" - key "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" + key "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" ) @@ -31,25 +31,51 @@ func (ValidBPM) Check(ctx context.Context, s *types.State) bool { return false } - return validateBPM(km, bpm, bpmFIT) == nil + return validateBPM(*km, *bpm, bpmFIT) == nil } // TODO: move this to linuxboot/fiano func validateBPM( - km *key.Manifest, - bpm *bootpolicy.Manifest, + km key.Manifest, + bpm bootpolicy.Manifest, bpmFIT *fit.EntryBootPolicyManifestRecord, ) error { if err := bpm.Validate(); err != nil { return nil } - if err := bpm.PMSE.Verify(bpmFIT.DataSegmentBytes[:bpm.KeySignatureOffset]); err != nil { - return fmt.Errorf("unable to confirm KM signature: %w", err) + if bpmBg, ok := bpm.(*bootpolicy.ManifestBG); ok { + off, err := bpmBg.PMSE.OffsetOf(1) + if err != nil { + return err + } + + if err := bpmBg.PMSE.Verify(bpmFIT.DataSegmentBytes[:off]); err != nil { + return fmt.Errorf("unable to confirm KM signature: %w", err) + } + + // It is safe to assume that in the normal conditions there won't be + // mixed revisions of BPM and KM, otherwise we have bigger problems + // than non-matching BPM key in KM... + if err := km.(*key.BGManifest).ValidateBPMKey(bpmBg.PMSE.KeySignature); err != nil { + return fmt.Errorf("key chain is invalid: %w", err) + } } - if err := km.ValidateBPMKey(bpm.PMSE.KeySignature); err != nil { - return fmt.Errorf("key chain is invalid: %w", err) + if bpmCBnt, ok := bpm.(*bootpolicy.ManifestCBnT); ok { + off, err := bpmCBnt.PMSE.OffsetOf(1) + if err != nil { + return err + } + + if err := bpmCBnt.PMSE.KeySignature.Verify(bpmFIT.DataSegmentBytes[:off]); err != nil { + return fmt.Errorf("unable to confirm KM signature: %w", err) + } + + // Same assumption as above. + if err := km.(*key.BGManifest).ValidateBPMKey(bpmCBnt.PMSE.KeySignature); err != nil { + return fmt.Errorf("key chain is invalid: %w", err) + } } return nil diff --git a/pkg/bootflow/conditions/biosconds/intelconds/valid_ibb.go b/pkg/bootflow/conditions/biosconds/intelconds/valid_ibb.go index 5ee5b87d..1029b7d2 100644 --- a/pkg/bootflow/conditions/biosconds/intelconds/valid_ibb.go +++ b/pkg/bootflow/conditions/biosconds/intelconds/valid_ibb.go @@ -7,7 +7,7 @@ import ( "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/types" - bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" ) // ValidIBB checks if the Initial Boot Block is valid (including its signatures). @@ -25,11 +25,11 @@ func (ValidIBB) Check(ctx context.Context, s *types.State) bool { return false } - return validateIBB(bpm, intelFW.SystemArtifact()) == nil + return validateIBB(*bpm, intelFW.SystemArtifact()) == nil } func validateIBB( - bpm *bootpolicy.Manifest, + bpm bootpolicy.Manifest, img *biosimage.BIOSImage, ) error { uefi, err := img.Parse() @@ -37,8 +37,16 @@ func validateIBB( return fmt.Errorf("unable to parse the UEFI layout: %w", err) } - if err := bpm.ValidateIBB(uefi); err != nil { - return fmt.Errorf("IBB signature in BPM is not valid: %w", err) + if bpmBg, ok := bpm.(*bootpolicy.ManifestBG); ok { + if err := bpmBg.ValidateIBB(uefi); err != nil { + return fmt.Errorf("IBB signature in BPM is not valid: %w", err) + } + } + + if bpmCBnt, ok := bpm.(*bootpolicy.ManifestCBnT); ok { + if err := bpmCBnt.ValidateIBB(uefi); err != nil { + return fmt.Errorf("IBB signature in BPM is not valid: %w", err) + } } return nil diff --git a/pkg/bootflow/conditions/biosconds/intelconds/valid_km.go b/pkg/bootflow/conditions/biosconds/intelconds/valid_km.go index 60eb6f1e..aa69aef7 100644 --- a/pkg/bootflow/conditions/biosconds/intelconds/valid_km.go +++ b/pkg/bootflow/conditions/biosconds/intelconds/valid_km.go @@ -6,7 +6,7 @@ import ( "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/types" - key "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" + key "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" ) @@ -25,17 +25,32 @@ func (ValidKM) Check(ctx context.Context, s *types.State) bool { return false } - return validateKM(km, kmFIT) == nil + return validateKM(*km, kmFIT) == nil } // TODO: move this to linuxboot/fiano func validateKM( - km *key.Manifest, + km key.Manifest, kmFIT *fit.EntryKeyManifestRecord, ) error { - if err := km.KeyAndSignature.Verify(kmFIT.DataSegmentBytes[:km.KeyManifestSignatureOffset]); err != nil { - return fmt.Errorf("unable to confirm KM signature: %w", err) + if kmBg, ok := km.(*key.BGManifest); ok { + off, err := kmBg.OffsetOf(5) + if err != nil { + return err + } + if err := kmBg.KeyAndSignature.Verify(kmFIT.DataSegmentBytes[:off]); err != nil { + return fmt.Errorf("unable to confirm KM signature: %w", err) + } } + if kmCBnT, ok := km.(*key.CBnTManifest); ok { + off, err := kmCBnT.OffsetOf(8) + if err != nil { + return err + } + if err := kmCBnT.KeyAndSignature.Verify(kmFIT.DataSegmentBytes[:off]); err != nil { + return fmt.Errorf("unable to confirm KM signature: %w", err) + } + } return nil } diff --git a/pkg/bootflow/datasources/inteldata/ibb.go b/pkg/bootflow/datasources/inteldata/ibb.go index 2eeaefe3..3747218b 100644 --- a/pkg/bootflow/datasources/inteldata/ibb.go +++ b/pkg/bootflow/datasources/inteldata/ibb.go @@ -7,6 +7,8 @@ import ( "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/types" + pkgbytes "github.com/linuxboot/fiano/pkg/bytes" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" ) // IBB implements types.DataSource by referencing to @@ -27,7 +29,17 @@ func (IBB) Data(ctx context.Context, s *types.State) (*types.Data, error) { return nil, fmt.Errorf("unable to get BPM: %w", err) } - ranges := bpm.IBBDataRanges(intelFW.SystemArtifact().Size()) + var ranges pkgbytes.Ranges + bpmP := *bpm + + if bpmBg, ok := bpmP.(*bootpolicy.ManifestBG); ok { + ranges = bpmBg.IBBDataRanges(intelFW.SystemArtifact().Size()) + } + + if bpmCBnt, ok := bpmP.(*bootpolicy.ManifestCBnT); ok { + ranges = bpmCBnt.IBBDataRanges(intelFW.SystemArtifact().Size()) + } + addrMapper := biosimage.PhysMemMapper{} ranges = addrMapper.UnresolveFullImageOffset(intelFW.SystemArtifact(), ranges...) diff --git a/pkg/bootflow/steps/intelsteps/measure_pcr0_data.go b/pkg/bootflow/steps/intelsteps/measure_pcr0_data.go index 969e2785..7ee8dd1c 100644 --- a/pkg/bootflow/steps/intelsteps/measure_pcr0_data.go +++ b/pkg/bootflow/steps/intelsteps/measure_pcr0_data.go @@ -19,6 +19,8 @@ import ( "github.com/google/go-tpm/legacy/tpm2" pkgbytes "github.com/linuxboot/fiano/pkg/bytes" manifest "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" + key "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" ) // MeasurePCR0DATA is a types.Step to measure the PCR0_DATA structure. @@ -92,15 +94,55 @@ func (MeasurePCR0DATA) Actions(ctx context.Context, s *types.State) types.Action } } kmAddr := keyManifestFITEntry.Headers.Address.Pointer() - pcr0DATA.kmSignature = types.Reference{ - Artifact: intelFW.SystemArtifact(), - MappedRanges: types.MappedRanges{ - AddressMapper: biosimage.PhysMemMapper{}, - Ranges: []pkgbytes.Range{{ - Offset: kmAddr + keyManifest.KeyAndSignatureOffset() + keyManifest.KeyAndSignature.SignatureOffset() + keyManifest.KeyAndSignature.Signature.DataOffset(), - Length: uint64(len(keyManifest.KeyAndSignature.Signature.Data)), - }}, - }, + switch km := (*keyManifest).(type) { + case *key.BGManifest: + keyAndSignatureOffset, err := km.OffsetOf(5) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG key manifest key-and-signature offset: %w", err))} + } + signatureOffset, err := km.KeyAndSignature.OffsetOf(2) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG key manifest signature offset: %w", err))} + } + signatureDataOffset, err := km.KeyAndSignature.Signature.OffsetOf(4) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG key manifest signature data offset: %w", err))} + } + pcr0DATA.kmSignature = types.Reference{ + Artifact: intelFW.SystemArtifact(), + MappedRanges: types.MappedRanges{ + AddressMapper: biosimage.PhysMemMapper{}, + Ranges: []pkgbytes.Range{{ + Offset: kmAddr + keyAndSignatureOffset + signatureOffset + signatureDataOffset, + Length: uint64(len(km.KeyAndSignature.Signature.Data)), + }}, + }, + } + case *key.CBnTManifest: + keyAndSignatureOffset, err := km.OffsetOf(8) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT key manifest key-and-signature offset: %w", err))} + } + signatureOffset, err := km.KeyAndSignature.OffsetOf(2) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT key manifest signature offset: %w", err))} + } + signatureDataOffset, err := km.KeyAndSignature.Signature.OffsetOf(4) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT key manifest signature data offset: %w", err))} + } + pcr0DATA.kmSignature = types.Reference{ + Artifact: intelFW.SystemArtifact(), + MappedRanges: types.MappedRanges{ + AddressMapper: biosimage.PhysMemMapper{}, + Ranges: []pkgbytes.Range{{ + Offset: kmAddr + keyAndSignatureOffset + signatureOffset + signatureDataOffset, + Length: uint64(len(km.KeyAndSignature.Signature.Data)), + }}, + }, + } + default: + return types.Actions{commonactions.Panic(fmt.Errorf("unsupported key manifest type: %T", km))} } bpManifest, bpManifestFITEntry, err := intelFW.BootPolicyManifest() @@ -110,52 +152,134 @@ func (MeasurePCR0DATA) Actions(ctx context.Context, s *types.State) types.Action } } bpmAddr := bpManifestFITEntry.Headers.Address.Pointer() + var ( + ibbdigests []manifest.HashStructure + firstDigestOffset uint64 + bpmSignatureOffset uint64 + bpmSignatureLength uint64 + ) + switch bpm := (*bpManifest).(type) { + case *bootpolicy.ManifestBG: + pmseOffset, err := bpm.OffsetOf(3) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG boot policy manifest PMSE offset: %w", err))} + } + keySignatureOffset, err := bpm.PMSE.OffsetOf(1) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG boot policy manifest key signature offset: %w", err))} + } + signatureOffset, err := bpm.PMSE.KeySignature.OffsetOf(2) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG boot policy manifest signature offset: %w", err))} + } + signatureDataOffset, err := bpm.PMSE.Signature.OffsetOf(4) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG boot policy manifest signature data offset: %w", err))} + } + bpmSignatureOffset = pmseOffset + keySignatureOffset + signatureOffset + signatureDataOffset + bpmSignatureLength = uint64(len(bpm.PMSE.Signature.Data)) + + if len(bpm.SE) == 0 { + return types.Actions{commonactions.Panic(fmt.Errorf("IBBDigest list is empty"))} + } + seOffset, err := bpm.OffsetOf(1) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG boot policy manifest SE offset: %w", err))} + } + digestOffset, err := bpm.SE[0].OffsetOf(13) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get BG boot policy manifest digest offset: %w", err))} + } + ibbdigests = []manifest.HashStructure{bpm.SE[0].Digest} + firstDigestOffset = seOffset + digestOffset + case *bootpolicy.ManifestCBnT: + pmseOffset, err := bpm.OffsetOf(6) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT boot policy manifest PMSE offset: %w", err))} + } + keySignatureOffset, err := bpm.PMSE.OffsetOf(1) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT boot policy manifest key signature offset: %w", err))} + } + signatureOffset, err := bpm.PMSE.KeySignature.OffsetOf(2) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT boot policy manifest signature offset: %w", err))} + } + signatureDataOffset, err := bpm.PMSE.Signature.OffsetOf(4) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT boot policy manifest signature data offset: %w", err))} + } + bpmSignatureOffset = pmseOffset + keySignatureOffset + signatureOffset + signatureDataOffset + bpmSignatureLength = uint64(len(bpm.PMSE.Signature.Data)) + + if len(bpm.SE) == 0 { + return types.Actions{commonactions.Panic(fmt.Errorf("IBBDigest list is empty"))} + } + seOffset, err := bpm.OffsetOf(1) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT boot policy manifest SE offset: %w", err))} + } + digestListOffset, err := bpm.SE[0].OffsetOf(14) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT boot policy manifest digest-list offset: %w", err))} + } + digestArrayOffset, err := bpm.SE[0].DigestList.OffsetOf(1) + if err != nil { + return types.Actions{commonactions.Panic(fmt.Errorf("unable to get CBnT boot policy manifest digest-list array offset: %w", err))} + } + ibbdigests = bpm.SE[0].DigestList.List + // +2 skips the digest-list element count prefix (uint16) to point to the first digest. + firstDigestOffset = seOffset + digestListOffset + digestArrayOffset + 2 + default: + return types.Actions{commonactions.Panic(fmt.Errorf("unsupported boot policy manifest type: %T", bpm))} + } pcr0DATA.bpmSignature = types.Reference{ Artifact: intelFW.SystemArtifact(), MappedRanges: types.MappedRanges{ AddressMapper: biosimage.PhysMemMapper{}, Ranges: []pkgbytes.Range{{ - Offset: bpmAddr + uint64(bpManifest.KeySignatureOffset) + bpManifest.PMSE.SignatureOffset() + bpManifest.PMSE.Signature.DataOffset(), - Length: uint64(len(bpManifest.PMSE.Signature.Data)), + Offset: bpmAddr + bpmSignatureOffset, + Length: bpmSignatureLength, }}, }, } - - digests := bpManifest.SE[0].DigestList.List - if len(digests) == 0 { + if len(ibbdigests) == 0 { return types.Actions{ commonactions.Panic(fmt.Errorf("IBBDigest list is empty")), } } var actions types.Actions - offsetToTheFirstDigest := bpmAddr + bpManifest.SEOffset() + - bpManifest.SE[0].DigestListOffset() + (bpManifest.SE[0].DigestList.ListOffset() + 2) for _, hashAlgo := range []manifest.Algorithm{ manifest.AlgSHA1, manifest.AlgSHA256, } { pcr0DATA.hashAlgo = hashAlgo - // Note: +2 - skip array size field to get the first element // find ibbDigest with the required algorithm - offsetToCurrentDigest := offsetToTheFirstDigest + offsetToCurrentDigest := firstDigestOffset var found bool - for idx := range digests { - if digests[idx].HashAlg == hashAlgo { + for idx := range ibbdigests { + if ibbdigests[idx].HashAlg == hashAlgo { + hashBufferOffset, err := ibbdigests[idx].OffsetOf(1) + if err != nil { + actions = append(actions, commonactions.Panic(fmt.Errorf("unable to get IBB digest hash buffer offset: %w", err))) + break + } pcr0DATA.ibbDigest = types.Reference{ Artifact: intelFW.SystemArtifact(), MappedRanges: types.MappedRanges{ AddressMapper: biosimage.PhysMemMapper{}, Ranges: []pkgbytes.Range{{ - Offset: offsetToCurrentDigest + (digests[idx].HashBufferOffset() + 2), - Length: uint64(len(digests[idx].HashBuffer)), + // +2 skips the HashBuffer size prefix (uint16). + Offset: bpmAddr + offsetToCurrentDigest + hashBufferOffset + 2, + Length: uint64(len(ibbdigests[idx].HashBuffer)), }}, }, } found = true break } - offsetToCurrentDigest += digests[idx].TotalSize() + offsetToCurrentDigest += ibbdigests[idx].TotalSize() } if found { diff --git a/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/bpm.go b/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/bpm.go index 2e4608f2..acd61f75 100644 --- a/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/bpm.go +++ b/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/bpm.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage/accessor" - bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" ) diff --git a/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/km.go b/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/km.go index 324c1fb7..c3f25413 100644 --- a/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/km.go +++ b/pkg/bootflow/systemartifacts/biosimage/accessor/intelbiosimage/km.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/9elements/converged-security-suite/v2/pkg/bootflow/systemartifacts/biosimage/accessor" - key "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" + key "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" ) diff --git a/pkg/intel/soc.go b/pkg/intel/soc.go new file mode 100644 index 00000000..2e2e7039 --- /dev/null +++ b/pkg/intel/soc.go @@ -0,0 +1,188 @@ +package intel + +import ( + cpuid "github.com/u-root/cpuid" +) + +// Based on arch/x86/include/asm/intel-family.h +// from the kernel codebase +const ( + INTEL_PENTIUM_PRO = 0x01 + INTEL_PENTIUM_II_KLAMATH = 0x03 + INTEL_PENTIUM_III_DESCHUTES = 0x05 + INTEL_PENTIUM_III_TUALATIN = 0x0B + INTEL_PENTIUM_M_DOTHAN = 0x0D + + INTEL_CORE_YONAH = 0x0E + + INTEL_CORE2_MEROM = 0x0F + INTEL_CORE2_MEROM_L = 0x16 + INTEL_CORE2_PENRYN = 0x17 + INTEL_CORE2_DUNNINGTON = 0x1D + + INTEL_NEHALEM = 0x1E + INTEL_NEHALEM_G = 0x1F + INTEL_NEHALEM_EP = 0x1A + INTEL_NEHALEM_EX = 0x2E + + INTEL_WESTMERE = 0x25 + INTEL_WESTMERE_EP = 0x2C + INTEL_WESTMERE_EX = 0x2F + + INTEL_SANDYBRIDGE = 0x2A + INTEL_SANDYBRIDGE_X = 0x2D + INTEL_IVYBRIDGE = 0x3A + INTEL_IVYBRIDGE_X = 0x3E + + INTEL_HASWELL = 0x3C + INTEL_HASWELL_X = 0x3F + INTEL_HASWELL_L = 0x45 + INTEL_HASWELL_G = 0x46 + + INTEL_BROADWELL = 0x3D + INTEL_BROADWELL_G = 0x47 + INTEL_BROADWELL_X = 0x4F + INTEL_BROADWELL_D = 0x56 + + INTEL_SKYLAKE_L = 0x4E + INTEL_SKYLAKE = 0x5E + INTEL_SKYLAKE_X = 0x55 + + INTEL_KABYLAKE_L = 0x8E + INTEL_KABYLAKE = 0x9E + + INTEL_COMETLAKE = 0xA5 + INTEL_COMETLAKE_L = 0xA6 + + INTEL_CANNONLAKE_L = 0x66 + + INTEL_ICELAKE_X = 0x6A + INTEL_ICELAKE_D = 0x6C + INTEL_ICELAKE = 0x7D + INTEL_ICELAKE_L = 0x7E + INTEL_ICELAKE_NNPI = 0x9D + + INTEL_ROCKETLAKE = 0xA7 + + INTEL_TIGERLAKE_L = 0x8C + INTEL_TIGERLAKE = 0x8D + + INTEL_SAPPHIRERAPIDS_X = 0x8F + + INTEL_EMERALDRAPIDS_X = 0xCF + + INTEL_GRANITERAPIDS_X = 0xAD + INTEL_GRANITERAPIDS_D = 0xAE + + INTEL_BARTLETTLAKE = 0xD7 + + // Hybrid processors + + INTEL_LAKEFIELD = 0x8A + + INTEL_ALDERLAKE = 0x97 + INTEL_ALDERLAKE_L = 0x9A + + INTEL_RAPTORLAKE = 0xB7 + INTEL_RAPTORLAKE_P = 0xBA + INTEL_RAPTORLAKE_S = 0xBF + + INTEL_METEORLAKE = 0xAC + INTEL_METEORLAKE_L = 0xAA + + INTEL_ARROWLAKE_H = 0xC5 + INTEL_ARROWLAKE = 0xC6 + INTEL_ARROWLAKE_U = 0xB5 + + INTEL_LUNARLAKE_M = 0xBD + + INTEL_PANTHERLAKE_L = 0xCC +) + +type BgVersion string + +const ( + Unknown BgVersion = "Unknown" + Legacy BgVersion = "Legacy (pre-Boot Guard)" + BootGuard BgVersion = "Boot Guard (1.0)" + CBnT20 BgVersion = "CBnT (2.0)" + CBnT21 BgVersion = "CBnT (2.1)" +) + +func bgVersion(model uint32) BgVersion { + switch model { + case INTEL_PENTIUM_PRO, + INTEL_PENTIUM_II_KLAMATH, + INTEL_PENTIUM_III_DESCHUTES, + INTEL_PENTIUM_III_TUALATIN, + INTEL_PENTIUM_M_DOTHAN, + INTEL_CORE_YONAH, + INTEL_CORE2_MEROM, + INTEL_CORE2_MEROM_L, + INTEL_CORE2_PENRYN, + INTEL_CORE2_DUNNINGTON, + INTEL_NEHALEM, + INTEL_NEHALEM_G, + INTEL_NEHALEM_EP, + INTEL_NEHALEM_EX, + INTEL_WESTMERE, + INTEL_WESTMERE_EP, + INTEL_WESTMERE_EX, + INTEL_SANDYBRIDGE, + INTEL_SANDYBRIDGE_X, + INTEL_IVYBRIDGE, + INTEL_IVYBRIDGE_X: + return Legacy + case INTEL_HASWELL, + INTEL_HASWELL_X, + INTEL_HASWELL_L, + INTEL_HASWELL_G, + INTEL_BROADWELL, + INTEL_BROADWELL_G, + INTEL_BROADWELL_X, + INTEL_BROADWELL_D, + INTEL_SKYLAKE_L, + INTEL_SKYLAKE, + INTEL_SKYLAKE_X, + INTEL_KABYLAKE_L, + INTEL_KABYLAKE, + INTEL_COMETLAKE_L, + INTEL_CANNONLAKE_L, + INTEL_ICELAKE_X, + INTEL_ICELAKE_D, + INTEL_ICELAKE, + INTEL_ICELAKE_L, + INTEL_ICELAKE_NNPI, + INTEL_LAKEFIELD: + return BootGuard + case INTEL_TIGERLAKE_L, + INTEL_COMETLAKE, + INTEL_ROCKETLAKE, + INTEL_TIGERLAKE, + INTEL_SAPPHIRERAPIDS_X, + INTEL_EMERALDRAPIDS_X, + INTEL_GRANITERAPIDS_X, + INTEL_GRANITERAPIDS_D, + INTEL_BARTLETTLAKE, + INTEL_ALDERLAKE, + INTEL_ALDERLAKE_L, + INTEL_RAPTORLAKE, + INTEL_RAPTORLAKE_P, + INTEL_RAPTORLAKE_S: + return CBnT20 + case INTEL_METEORLAKE, + INTEL_METEORLAKE_L, + INTEL_ARROWLAKE_H, + INTEL_ARROWLAKE, + INTEL_ARROWLAKE_U, + INTEL_LUNARLAKE_M, + INTEL_PANTHERLAKE_L: + return CBnT21 + default: + return Unknown + } +} + +func RuntimeBGVersion() BgVersion { + return bgVersion(cpuid.DisplayModel) +} diff --git a/pkg/provisioning/bootguard/bootguard.go b/pkg/provisioning/bootguard/bootguard.go index fdbd4845..f86c61c8 100644 --- a/pkg/provisioning/bootguard/bootguard.go +++ b/pkg/provisioning/bootguard/bootguard.go @@ -12,13 +12,10 @@ import ( "github.com/9elements/converged-security-suite/v2/pkg/tools" "github.com/9elements/converged-security-suite/v2/pkg/uefi/consts" "github.com/linuxboot/fiano/pkg/cbfs" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgkey" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/bgheader" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" + keymanifest "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" "github.com/linuxboot/fiano/pkg/uefi" "github.com/tidwall/pretty" @@ -29,31 +26,7 @@ import ( // Everything more secure than SHA-1 const minHashTypeSize = 32 -func bgBPMReader(bpm *bgbootpolicy.Manifest) (*bytes.Reader, error) { - if bpm == nil { - return nil, fmt.Errorf("manifest is nil") - } - buf := new(bytes.Buffer) - _, err := bpm.WriteTo(buf) - if err != nil { - return nil, err - } - return bytes.NewReader(buf.Bytes()), nil -} - -func bgKMReader(km *bgkey.Manifest) (*bytes.Reader, error) { - if km == nil { - return nil, fmt.Errorf("manifest is nil") - } - buf := new(bytes.Buffer) - _, err := km.WriteTo(buf) - if err != nil { - return nil, err - } - return bytes.NewReader(buf.Bytes()), nil -} - -func cbntBPMReader(bpm *cbntbootpolicy.Manifest) (*bytes.Reader, error) { +func bpmReader(bpm bootpolicy.Manifest) (*bytes.Reader, error) { if bpm == nil { return nil, fmt.Errorf("manifest is nil") } @@ -65,7 +38,7 @@ func cbntBPMReader(bpm *cbntbootpolicy.Manifest) (*bytes.Reader, error) { return bytes.NewReader(buf.Bytes()), nil } -func cbntKMReader(km *cbntkey.Manifest) (*bytes.Reader, error) { +func kmReader(km keymanifest.Manifest) (*bytes.Reader, error) { if km == nil { return nil, fmt.Errorf("manifest is nil") } @@ -79,22 +52,33 @@ func cbntKMReader(km *cbntkey.Manifest) (*bytes.Reader, error) { func NewVData(vdata VersionedData) (*BootGuard, error) { var b BootGuard - var err error - manifest, err := bgBPMReader(vdata.BGbpm) - if err == nil { - b.Version, _ = bgheader.DetectBGV(manifest) - } - manifest, err = bgKMReader(vdata.BGkm) - if err == nil { - b.Version, _ = bgheader.DetectBGV(manifest) - } - manifest, err = cbntBPMReader(vdata.CBNTbpm) - if err == nil { - b.Version, _ = bgheader.DetectBGV(manifest) + + if vdata.BGbpm != nil && vdata.BGkm != nil { + // For BG 1.0 KM and BPM versions have to be the same. + // So we don't have to call DetectBGV twice. + manifest, err := bpmReader(vdata.BGbpm) + if err == nil { + b.Version, _ = cbnt.DetectBGV(manifest) + } + + _, err = kmReader(vdata.BGkm) + if err != nil { + return nil, fmt.Errorf("NewVData: %v", err) + } } - manifest, err = cbntKMReader(vdata.CBNTkm) - if err == nil { - b.Version, _ = bgheader.DetectBGV(manifest) + + if vdata.CBNTbpm != nil && vdata.CBNTkm != nil { + // for CBnT 2.0 KM and BPM will be the same, and for 2.1 + // we only care about version as reported by BPM. + manifest, err := bpmReader(vdata.CBNTbpm) + if err == nil { + b.Version, _ = cbnt.DetectBGV(manifest) + } + + _, err = kmReader(vdata.CBNTkm) + if err != nil { + return nil, fmt.Errorf("NewVData: %v", err) + } } if b.Version == 0 { return nil, fmt.Errorf("NewVData: can't identify bootguard header") @@ -109,19 +93,37 @@ func NewBPM(bpm io.ReadSeeker) (*BootGuard, error) { return nil, fmt.Errorf("manifest is nil") } var err error - b.Version, err = bgheader.DetectBGV(bpm) + b.Version, err = cbnt.DetectBGV(bpm) if err != nil { return nil, err } switch b.Version { - case bgheader.Version10: - b.VData.BGbpm = bgbootpolicy.NewManifest() + case cbnt.Version10: + bgbpm, err := bootpolicy.NewManifest(cbnt.Version10) + if err != nil { + return nil, err + } + ast, ok := bgbpm.(*bootpolicy.ManifestBG) + if !ok { + return nil, fmt.Errorf("could not assert BPM type") + } + b.VData.BGbpm = ast + _, err = b.VData.BGbpm.ReadFrom(bpm) if err != nil && !errors.Is(err, io.EOF) { return nil, err } - case bgheader.Version20: - b.VData.CBNTbpm = cbntbootpolicy.NewManifest() + case cbnt.Version20, cbnt.Version21: + cbntbpm, err := bootpolicy.NewManifest(cbnt.Version20) + if err != nil { + return nil, err + } + ast, ok := cbntbpm.(*bootpolicy.ManifestCBnT) + if !ok { + return nil, fmt.Errorf("could not assert BPM type") + } + b.VData.CBNTbpm = ast + _, err = b.VData.CBNTbpm.ReadFrom(bpm) if err != nil && !errors.Is(err, io.EOF) { return nil, err @@ -138,19 +140,37 @@ func NewKM(km io.ReadSeeker) (*BootGuard, error) { return nil, fmt.Errorf("manifest is nil") } var err error - b.Version, err = bgheader.DetectBGV(km) + b.Version, err = cbnt.DetectBGV(km) if err != nil { return nil, err } switch b.Version { - case bgheader.Version10: - b.VData.BGkm = bgkey.NewManifest() + case cbnt.Version10: + bgkm, err := keymanifest.NewManifest(cbnt.Version10) + if err != nil { + return nil, err + } + ast, ok := bgkm.(*keymanifest.BGManifest) + if !ok { + return nil, fmt.Errorf("could not assert KM type") + } + b.VData.BGkm = ast + _, err = b.VData.BGkm.ReadFrom(km) if err != nil && !errors.Is(err, io.EOF) { return nil, err } - case bgheader.Version20: - b.VData.CBNTkm = cbntkey.NewManifest() + case cbnt.Version20, cbnt.Version21: + cbntkm, err := keymanifest.NewManifest(b.Version) + if err != nil { + return nil, err + } + ast, ok := cbntkm.(*keymanifest.CBnTManifest) + if !ok { + return nil, fmt.Errorf("could not assert KM type") + } + b.VData.CBNTkm = ast + _, err = b.VData.CBNTkm.ReadFrom(km) if err != nil && !errors.Is(err, io.EOF) { return nil, err @@ -166,24 +186,44 @@ func NewBPMAndKM(bpm io.ReadSeeker, km io.ReadSeeker) (*BootGuard, error) { if bpm == nil || km == nil { return nil, fmt.Errorf("either both or one manifest is nil") } - var err error - bpmV, err := bgheader.DetectBGV(bpm) + bpmV, err := cbnt.DetectBGV(bpm) if err != nil { return nil, err } - kmV, err := bgheader.DetectBGV(km) + kmV, err := cbnt.DetectBGV(km) if err != nil { return nil, err } - if bpmV != kmV { + // This check is not valid for CBnT 2.1 since KM headers were + // not bumped at all. So the case where km header is 0x21 and + // bpm is 0x25 is fine. + if bpmV != kmV && bpmV <= cbnt.Version20 { return nil, fmt.Errorf("km and bpm version number differ") } b.Version = bpmV switch b.Version { - case bgheader.Version10: - b.VData.BGbpm = bgbootpolicy.NewManifest() - b.VData.BGkm = bgkey.NewManifest() - _, err := b.VData.BGbpm.ReadFrom(bpm) + case cbnt.Version10: + bgbpm, err := bootpolicy.NewManifest(cbnt.Version10) + if err != nil { + return nil, err + } + astbpm, ok := bgbpm.(*bootpolicy.ManifestBG) + if !ok { + return nil, fmt.Errorf("could not assert BPM type") + } + b.VData.BGbpm = astbpm + + bgkm, err := keymanifest.NewManifest(cbnt.Version10) + if err != nil { + return nil, err + } + astkm, ok := bgkm.(*keymanifest.BGManifest) + if !ok { + return nil, fmt.Errorf("could not assert KM type") + } + b.VData.BGkm = astkm + + _, err = b.VData.BGbpm.ReadFrom(bpm) if err != nil && !errors.Is(err, io.EOF) { return nil, err } @@ -191,10 +231,28 @@ func NewBPMAndKM(bpm io.ReadSeeker, km io.ReadSeeker) (*BootGuard, error) { if err != nil && !errors.Is(err, io.EOF) { return nil, err } - case bgheader.Version20: - b.VData.CBNTbpm = cbntbootpolicy.NewManifest() - b.VData.CBNTkm = cbntkey.NewManifest() - _, err := b.VData.CBNTbpm.ReadFrom(bpm) + case cbnt.Version20, cbnt.Version21: + cbntbpm, err := bootpolicy.NewManifest(b.Version) + if err != nil { + return nil, err + } + astbpm, ok := cbntbpm.(*bootpolicy.ManifestCBnT) + if !ok { + return nil, fmt.Errorf("could not assert BPM type") + } + b.VData.CBNTbpm = astbpm + + cbntkm, err := keymanifest.NewManifest(cbnt.Version20) + if err != nil { + return nil, err + } + astkm, ok := cbntkm.(*keymanifest.CBnTManifest) + if !ok { + return nil, fmt.Errorf("could not assert KM type") + } + b.VData.CBNTkm = astkm + + _, err = b.VData.CBNTbpm.ReadFrom(bpm) if err != nil && !errors.Is(err, io.EOF) { return nil, err } @@ -218,15 +276,33 @@ func NewBPMAndKMFromBIOS(biosFilepath string, jsonFilepath *os.File) (*BootGuard return nil, err } var b BootGuard - b.Version, err = bgheader.DetectBGV(bpmEntry.Reader()) + b.Version, err = cbnt.DetectBGV(bpmEntry.Reader()) if err != nil { return nil, err } switch b.Version { - case bgheader.Version10: - b.VData.BGbpm = bgbootpolicy.NewManifest() - b.VData.BGkm = bgkey.NewManifest() - _, err := b.VData.BGbpm.ReadFrom(bpmEntry.Reader()) + case cbnt.Version10: + bgbpm, err := bootpolicy.NewManifest(cbnt.Version10) + if err != nil { + return nil, err + } + bpm, ok := bgbpm.(*bootpolicy.ManifestBG) + if !ok { + return nil, fmt.Errorf("could not assert BPM type") + } + b.VData.BGbpm = bpm + + bgkm, err := keymanifest.NewManifest(cbnt.Version10) + if err != nil { + return nil, err + } + km, ok := bgkm.(*keymanifest.BGManifest) + if !ok { + return nil, fmt.Errorf("could not assert KM type") + } + b.VData.BGkm = km + + _, err = b.VData.BGbpm.ReadFrom(bpmEntry.Reader()) if err != nil && !errors.Is(err, io.EOF) { return nil, err } @@ -234,10 +310,28 @@ func NewBPMAndKMFromBIOS(biosFilepath string, jsonFilepath *os.File) (*BootGuard if err != nil && !errors.Is(err, io.EOF) { return nil, err } - case bgheader.Version20: - b.VData.CBNTbpm = cbntbootpolicy.NewManifest() - b.VData.CBNTkm = cbntkey.NewManifest() - _, err := b.VData.CBNTbpm.ReadFrom(bpmEntry.Reader()) + case cbnt.Version20, cbnt.Version21: + cbntbpm, err := bootpolicy.NewManifest(b.Version) + if err != nil { + return nil, err + } + bpm, ok := cbntbpm.(*bootpolicy.ManifestCBnT) + if !ok { + return nil, fmt.Errorf("could not assert BPM type") + } + b.VData.CBNTbpm = bpm + + cbntkm, err := keymanifest.NewManifest(b.Version) + if err != nil { + return nil, err + } + km, ok := cbntkm.(*keymanifest.CBnTManifest) + if !ok { + return nil, fmt.Errorf("could not assert KM type") + } + b.VData.CBNTkm = km + + _, err = b.VData.CBNTbpm.ReadFrom(bpmEntry.Reader()) if err != nil && !errors.Is(err, io.EOF) { return nil, err } @@ -263,9 +357,9 @@ func NewBPMAndKMFromBIOS(biosFilepath string, jsonFilepath *os.File) (*BootGuard // and validates the structure func (b *BootGuard) ValidateBPM() error { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: return b.VData.BGbpm.Validate() - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: return b.VData.CBNTbpm.Validate() default: return fmt.Errorf("ValidateBPM: can't identify bootguard header") @@ -276,9 +370,9 @@ func (b *BootGuard) ValidateBPM() error { // and validates the structure func (b *BootGuard) ValidateKM() error { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: return b.VData.BGkm.Validate() - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: return b.VData.CBNTkm.Validate() default: return fmt.Errorf("ValidateKM: can't identify bootguard header") @@ -288,9 +382,9 @@ func (b *BootGuard) ValidateKM() error { // PrintBPM prints the boot policy manifest in human readable func (b *BootGuard) PrintBPM() { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: b.VData.BGbpm.Print() - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: b.VData.CBNTbpm.Print() default: log.Error("PrintBPM: can't identify bootguard header") @@ -300,9 +394,9 @@ func (b *BootGuard) PrintBPM() { // PrintKM prints the key manifest in human readable func (b *BootGuard) PrintKM() { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: b.VData.BGkm.Print() - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: b.VData.CBNTkm.Print() default: log.Error("PrintKM: can't identify bootguard header") @@ -314,9 +408,9 @@ func (b *BootGuard) WriteKM() ([]byte, error) { var err error buf := new(bytes.Buffer) switch b.Version { - case bgheader.Version10: + case cbnt.Version10: _, err = b.VData.BGkm.WriteTo(buf) - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: _, err = b.VData.CBNTkm.WriteTo(buf) default: log.Error("WriteKM: can't identify bootguard header") @@ -329,9 +423,9 @@ func (b *BootGuard) WriteBPM() ([]byte, error) { var err error buf := new(bytes.Buffer) switch b.Version { - case bgheader.Version10: + case cbnt.Version10: _, err = b.VData.BGbpm.WriteTo(buf) - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: _, err = b.VData.CBNTbpm.WriteTo(buf) default: log.Error("WriteBPM: can't identify bootguard header") @@ -367,15 +461,14 @@ func (b *BootGuard) ReadJSON(filepath string) error { // StitchKM returns a key manifest manifest as byte slice func (b *BootGuard) StitchKM(pubKey crypto.PublicKey, signature []byte) ([]byte, error) { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: if err := b.VData.BGkm.KeyAndSignature.FillSignature(0, pubKey, signature, b.VData.BGkm.BPKey.HashAlg); err != nil { return nil, err } - b.VData.BGkm.RehashRecursive() if err := b.VData.BGkm.Validate(); err != nil { return nil, err } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: if err := b.VData.CBNTkm.KeyAndSignature.FillSignature(0, pubKey, signature, b.VData.CBNTkm.PubKeyHashAlg); err != nil { return nil, err } @@ -392,9 +485,14 @@ func (b *BootGuard) StitchKM(pubKey crypto.PublicKey, signature []byte) ([]byte, // StitchBPM returns a boot policy manifest as byte slice func (b *BootGuard) StitchBPM(pubKey crypto.PublicKey, signature []byte) ([]byte, error) { switch b.Version { - case bgheader.Version10: - b.VData.BGbpm.PMSE = *bgbootpolicy.NewSignature() - if err := b.VData.BGbpm.PMSE.FillSignature(0, pubKey, signature, bg.AlgNull); err != nil { + case cbnt.Version10: + sig, err := bootpolicy.NewSignature(cbnt.Version10) + if err != nil { + return nil, err + } + b.VData.BGbpm.PMSE = *sig + + if err := b.VData.BGbpm.PMSE.FillSignature(0, pubKey, signature, cbnt.AlgNull); err != nil { return nil, err } @@ -402,8 +500,12 @@ func (b *BootGuard) StitchBPM(pubKey crypto.PublicKey, signature []byte) ([]byte if err := b.VData.BGbpm.Validate(); err != nil { return nil, err } - case bgheader.Version20: - b.VData.CBNTbpm.PMSE = *cbntbootpolicy.NewSignature() + case cbnt.Version20, cbnt.Version21: + sig, err := bootpolicy.NewSignature(cbnt.Version20) + if err != nil { + return nil, err + } + b.VData.CBNTbpm.PMSE = *sig if err := b.VData.CBNTbpm.PMSE.FillSignature(0, pubKey, signature, cbnt.AlgNull); err != nil { return nil, err } @@ -422,21 +524,25 @@ func (b *BootGuard) StitchBPM(pubKey crypto.PublicKey, signature []byte) ([]byte func (b *BootGuard) SignKM(signAlgo string, signer crypto.Signer) ([]byte, error) { buf := new(bytes.Buffer) switch b.Version { - case bgheader.Version10: - signAlgo, err := bg.GetAlgFromString(signAlgo) + case cbnt.Version10: + signAlgo, err := cbnt.GetAlgFromString(signAlgo) if err != nil { return nil, err } - b.VData.BGkm.RehashRecursive() _, err = b.VData.BGkm.WriteTo(buf) if err != nil { return nil, err } - unsignedKM := buf.Bytes()[:b.VData.BGkm.KeyAndSignatureOffset()] - if err := b.VData.BGkm.SetSignature(signAlgo, signer, unsignedKM); err != nil { + off, err := b.VData.BGkm.OffsetOf(5) + if err != nil { return nil, err } - case bgheader.Version20: + unsignedKM := buf.Bytes()[:off] + // FIXME: second algo here is not needed in BG + if err := b.VData.BGkm.SetSignature(signAlgo, signAlgo, signer, unsignedKM); err != nil { + return nil, err + } + case cbnt.Version20, cbnt.Version21: signAlgo, err := cbnt.GetAlgFromString(signAlgo) if err != nil { return nil, err @@ -446,7 +552,11 @@ func (b *BootGuard) SignKM(signAlgo string, signer crypto.Signer) ([]byte, error if err != nil { return nil, err } - unsignedKM := buf.Bytes()[:b.VData.CBNTkm.KeyAndSignatureOffset()] + v, err := b.VData.CBNTkm.OffsetOf(8) + if err != nil { + return nil, err + } + unsignedKM := buf.Bytes()[:v] if err = b.VData.CBNTkm.SetSignature(signAlgo, b.VData.CBNTkm.PubKeyHashAlg, signer, unsignedKM); err != nil { return nil, err } @@ -460,22 +570,30 @@ func (b *BootGuard) SignKM(signAlgo string, signer crypto.Signer) ([]byte, error func (b *BootGuard) SignBPM(signAlgo, hashAlgo string, privkey crypto.PrivateKey) ([]byte, error) { buf := new(bytes.Buffer) switch b.Version { - case bgheader.Version10: - signAlgo, err := bg.GetAlgFromString(signAlgo) + case cbnt.Version10: + signAlgo, err := cbnt.GetAlgFromString(signAlgo) + if err != nil { + return nil, err + } + sig, err := bootpolicy.NewSignature(cbnt.Version10) if err != nil { return nil, err } - b.VData.BGbpm.PMSE = *bgbootpolicy.NewSignature() + b.VData.BGbpm.PMSE = *sig b.VData.BGbpm.RehashRecursive() _, err = b.VData.BGbpm.WriteTo(buf) if err != nil { return nil, err } - unsignedBPM := buf.Bytes()[:b.VData.BGbpm.PMSE.KeySignatureOffset()] - if err := b.VData.BGbpm.PMSE.SetSignature(signAlgo, privkey.(crypto.Signer), unsignedBPM); err != nil { + off, err := b.VData.BGbpm.PMSE.OffsetOf(1) + if err != nil { + return nil, err + } + unsignedBPM := buf.Bytes()[:off] + if err := b.VData.BGbpm.PMSE.SetSignature(signAlgo, signAlgo, privkey.(crypto.Signer), unsignedBPM); err != nil { return nil, err } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: signAlgo, err := cbnt.GetAlgFromString(signAlgo) if err != nil { return nil, err @@ -484,7 +602,11 @@ func (b *BootGuard) SignBPM(signAlgo, hashAlgo string, privkey crypto.PrivateKey if err != nil { return nil, err } - b.VData.CBNTbpm.PMSE = *cbntbootpolicy.NewSignature() + sig, err := bootpolicy.NewSignature(cbnt.Version20) + if err != nil { + return nil, err + } + b.VData.CBNTbpm.PMSE = *sig b.VData.CBNTbpm.RehashRecursive() _, err = b.VData.CBNTbpm.WriteTo(buf) if err != nil { @@ -504,20 +626,27 @@ func (b *BootGuard) SignBPM(signAlgo, hashAlgo string, privkey crypto.PrivateKey func (b *BootGuard) VerifyKM() error { buf := new(bytes.Buffer) switch b.Version { - case bgheader.Version10: + case cbnt.Version10: _, err := b.VData.BGkm.WriteTo(buf) if err != nil { return err } - if err := b.VData.BGkm.KeyAndSignature.Verify(buf.Bytes()[:b.VData.BGkm.KeyAndSignatureOffset()]); err != nil { + km := b.VData.BGkm + off, err := km.OffsetOf(5) + if err != nil { return err } - case bgheader.Version20: + if err := km.KeyAndSignature.Verify(buf.Bytes()[:off]); err != nil { + return err + } + case cbnt.Version20, cbnt.Version21: _, err := b.VData.CBNTkm.WriteTo(buf) if err != nil { return err } - if err := b.VData.CBNTkm.KeyAndSignature.Verify(buf.Bytes()[:b.VData.CBNTkm.KeyAndSignatureOffset()]); err != nil { + + v, _ := b.VData.CBNTkm.OffsetOf(8) + if err := b.VData.CBNTkm.KeyAndSignature.Verify(buf.Bytes()[:v]); err != nil { return err } default: @@ -530,20 +659,25 @@ func (b *BootGuard) VerifyKM() error { func (b *BootGuard) VerifyBPM() error { buf := new(bytes.Buffer) switch b.Version { - case bgheader.Version10: + case cbnt.Version10: _, err := b.VData.BGbpm.WriteTo(buf) if err != nil { return err } - if err := b.VData.BGbpm.PMSE.Verify(buf.Bytes()[:b.VData.BGbpm.PMSEOffset()]); err != nil { + off, err := b.VData.BGbpm.OffsetOf(3) + if err != nil { return err } - case bgheader.Version20: + if err := b.VData.BGbpm.PMSE.Verify(buf.Bytes()[:off]); err != nil { + return err + } + case cbnt.Version20, cbnt.Version21: _, err := b.VData.CBNTbpm.WriteTo(buf) if err != nil { return err } - if err := b.VData.CBNTbpm.PMSE.Verify(buf.Bytes()[:b.VData.CBNTbpm.KeySignatureOffset]); err != nil { + off := uint64(b.VData.CBNTbpm.BPMHCBnT.KeySignatureOffset) + if err := b.VData.CBNTbpm.PMSE.Verify(buf.Bytes()[:off]); err != nil { return err } default: @@ -577,8 +711,8 @@ func (b *BootGuard) CalculateNEMSize(image []byte, acm *tools.ACM) (uint16, erro totalSize += uint32(acm.Header.GetSize().Size()) totalSize += defaultStackAndDataSize switch b.Version { - case bgheader.Version10: - totalSize += uint32((&bgbootpolicy.BPMH{}).TotalSize()) + case cbnt.Version10: + totalSize += uint32((&bootpolicy.BPMHBG{}).TotalSize()) totalSize += uint32(b.VData.BGbpm.SE[0].TotalSize()) for _, ibb := range b.VData.BGbpm.SE[0].IBBSegments { totalSize += ibb.Size @@ -594,10 +728,10 @@ func (b *BootGuard) CalculateNEMSize(image []byte, acm *tools.ACM) (uint16, erro if (totalSize % 4096) != 0 { totalSize += 4096 - (totalSize % 4096) } - return uint16(bgbootpolicy.NewSize4K(totalSize)), nil - case bgheader.Version20: + return uint16(bootpolicy.NewSize4K(totalSize)), nil + case cbnt.Version20, cbnt.Version21: totalSize += uint32(b.VData.CBNTkm.KeyManifestSignatureOffset) - totalSize += uint32((&cbntbootpolicy.BPMH{}).TotalSize()) + totalSize += uint32((&bootpolicy.BPMHCBnT{}).TotalSize()) for _, se := range b.VData.CBNTbpm.SE { totalSize += uint32(se.ElementSize) for _, ibb := range se.IBBSegments { @@ -621,7 +755,7 @@ func (b *BootGuard) CalculateNEMSize(image []byte, acm *tools.ACM) (uint16, erro if (totalSize % 4096) != 0 { totalSize += 4096 - (totalSize % 4096) } - return uint16(cbntbootpolicy.NewSize4K(totalSize)), nil + return uint16(bootpolicy.NewSize4K(totalSize)), nil default: return 0, fmt.Errorf("CalculateNEMSize: can't identify bootguard header") } @@ -636,8 +770,8 @@ func (b *BootGuard) GetBPMPubHash(pubkey crypto.PublicKey, hashAlgo string) erro return err } switch b.Version { - case bgheader.Version10: - hashAlg, err := bg.GetAlgFromString(hashAlgo) + case cbnt.Version10: + hashAlg, err := cbnt.GetAlgFromString(hashAlgo) if err != nil { return err } @@ -650,12 +784,12 @@ func (b *BootGuard) GetBPMPubHash(pubkey crypto.PublicKey, hashAlgo string) erro return err } data = hash.Sum(nil) - hStruc := bg.HashStructure{ - HashAlg: bg.Algorithm(hashAlg), + hStruc := cbnt.HashStructure{ + HashAlg: cbnt.Algorithm(hashAlg), } hStruc.HashBuffer = data b.VData.BGkm.BPKey = hStruc - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: hashAlg, err := cbnt.GetAlgFromString(hashAlgo) if err != nil { return err @@ -669,14 +803,14 @@ func (b *BootGuard) GetBPMPubHash(pubkey crypto.PublicKey, hashAlgo string) erro return err } data = hash.Sum(nil) - var keyHashes []cbntkey.Hash + var keyHashes []keymanifest.Hash hStruc := &cbnt.HashStructure{ HashAlg: cbnt.Algorithm(hashAlg), } hStruc.HashBuffer = data - kH := cbntkey.Hash{ - Usage: cbntkey.UsageBPMSigningPKD, + kH := keymanifest.Hash{ + Usage: keymanifest.UsageBPMSigningPKD, Digest: *hStruc, } b.VData.CBNTkm.Hash = append(keyHashes, kH) @@ -688,8 +822,8 @@ func (b *BootGuard) GetBPMPubHash(pubkey crypto.PublicKey, hashAlgo string) erro func (b *BootGuard) GetIBBsDigest(image []byte, hashAlgo string) (digest []byte, err error) { switch b.Version { - case bgheader.Version10: - hashAlg, err := bg.GetAlgFromString(hashAlgo) + case cbnt.Version10: + hashAlg, err := cbnt.GetAlgFromString(hashAlgo) if err != nil { return nil, err } @@ -726,7 +860,7 @@ func (b *BootGuard) GetIBBsDigest(image []byte, hashAlgo string) (digest []byte, } } digest = hash.Sum(nil) - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: hashAlg, err := cbnt.GetAlgFromString(hashAlgo) if err != nil { return nil, err @@ -777,7 +911,7 @@ func (b *BootGuard) CreateIBBDigest(biosFilepath string) error { return fmt.Errorf("unable to read file '%s': %w", biosFilepath, err) } switch b.Version { - case bgheader.Version10: + case cbnt.Version10: hashAlgo := b.VData.BGbpm.SE[0].Digest.HashAlg.String() d, err := b.GetIBBsDigest(data, hashAlgo) if err != nil { @@ -785,7 +919,7 @@ func (b *BootGuard) CreateIBBDigest(biosFilepath string) error { } b.VData.BGbpm.SE[0].Digest.HashBuffer = make([]byte, len(d)) copy(b.VData.BGbpm.SE[0].Digest.HashBuffer, d) - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: for iterator, item := range b.VData.CBNTbpm.SE[0].DigestList.List { d, err := b.GetIBBsDigest(data, item.HashAlg.String()) if err != nil { @@ -803,16 +937,16 @@ func (b *BootGuard) CreateIBBDigest(biosFilepath string) error { // BPMCryptoSecure verifies that BPM uses sane crypto algorithms func (b *BootGuard) BPMCryptoSecure() (bool, error) { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: hash := b.VData.BGbpm.SE[0].Digest.HashAlg - if hash == bg.AlgSHA1 || hash.IsNull() { + if hash == cbnt.AlgSHA1 || hash.IsNull() { return false, fmt.Errorf("signed IBB hash in BPM uses insecure hash algorithm SHA1/Null") } hash = b.VData.BGbpm.PMSE.Signature.HashAlg - if hash == bg.AlgSHA1 || hash.IsNull() { + if hash == cbnt.AlgSHA1 || hash.IsNull() { return false, fmt.Errorf("BPM signature uses insecure hash algorithm SHA1/Null") } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: for _, hash := range b.VData.CBNTbpm.SE[0].DigestList.List { if hash.HashAlg == cbnt.AlgSHA1 || hash.HashAlg.IsNull() { if b.VData.CBNTbpm.SE[0].DigestList.Size < 2 { @@ -831,16 +965,16 @@ func (b *BootGuard) BPMCryptoSecure() (bool, error) { // KMCryptoSecure verifies that KM uses sane crypto algorithms func (b *BootGuard) KMCryptoSecure() (bool, error) { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: hash := b.VData.BGkm.KeyAndSignature.Signature.HashAlg - if hash == bg.AlgSHA1 || hash.IsNull() { + if hash == cbnt.AlgSHA1 || hash.IsNull() { return false, fmt.Errorf("KM signature uses insecure hash algorithm SHA1/Null") } hash = b.VData.BGkm.BPKey.HashAlg - if hash == bg.AlgSHA1 || hash.IsNull() { + if hash == cbnt.AlgSHA1 || hash.IsNull() { return false, fmt.Errorf("signed BPM hash in KM uses insecure hash algorithm SHA1/Null") } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: hash := b.VData.CBNTkm.PubKeyHashAlg if hash == cbnt.AlgSHA1 || hash.IsNull() { return false, fmt.Errorf("KM signature uses insecure hash algorithm SHA1/Null") @@ -858,13 +992,17 @@ func (b *BootGuard) KMCryptoSecure() (bool, error) { func (b *BootGuard) KMHasBPMHash() (bool, error) { var bpmHashFound bool switch b.Version { - case bgheader.Version10: - if b.VData.BGkm.BPKey.HashBufferTotalSize() > minHashTypeSize { + case cbnt.Version10: + size, err := b.VData.BGkm.BPKey.SizeOf(1) + if err != nil { + return false, err + } + if size > minHashTypeSize { bpmHashFound = true } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: for _, hash := range b.VData.CBNTkm.Hash { - if hash.Usage == cbntkey.UsageBPMSigningPKD { + if hash.Usage == keymanifest.UsageBPMSigningPKD { bpmHashFound = true } } @@ -878,15 +1016,19 @@ func (b *BootGuard) KMHasBPMHash() (bool, error) { // BPMKeyMatchKMHash verifies that BPM pubkey hash matches KM hash of Boot Policy func (b *BootGuard) BPMKeyMatchKMHash() (bool, error) { switch b.Version { - case bgheader.Version10: - if b.VData.BGkm.BPKey.HashBufferTotalSize() > minHashTypeSize { + case cbnt.Version10: + size, err := b.VData.BGkm.BPKey.SizeOf(1) + if err != nil { + return false, err + } + if size > minHashTypeSize { if err := b.VData.BGkm.ValidateBPMKey(b.VData.BGbpm.PMSE.KeySignature); err != nil { return false, fmt.Errorf("couldn't verify bpm hash in km") } } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: for _, hash := range b.VData.CBNTkm.Hash { - if hash.Usage == cbntkey.UsageBPMSigningPKD { + if hash.Usage == keymanifest.UsageBPMSigningPKD { if err := b.VData.CBNTkm.ValidateBPMKey(b.VData.CBNTbpm.PMSE.KeySignature); err != nil { return false, fmt.Errorf("couldn't verify bpm hash in km") } @@ -899,7 +1041,7 @@ func (b *BootGuard) BPMKeyMatchKMHash() (bool, error) { // StrictSaneBPMSecurityProps verifies that BPM contains security properties more strictly func (b *BootGuard) StrictSaneBPMSecurityProps() (bool, error) { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: flags := b.VData.BGbpm.SE[0].Flags if !flags.AuthorityMeasure() { return false, fmt.Errorf("pcr-7 data should extended for OS security") @@ -907,16 +1049,16 @@ func (b *BootGuard) StrictSaneBPMSecurityProps() (bool, error) { if !flags.TPMFailureLeavesHierarchiesEnabled() { return false, fmt.Errorf("tpm failure should lead to default measurements from PCR0 to PCR7") } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: bgFlags := b.VData.CBNTbpm.SE[0].Flags - if !bgFlags.AuthorityMeasure() { + if !bgFlags.AuthorityMeasure() && b.Version != cbnt.Version21 { return false, fmt.Errorf("pcr-7 data should extended for OS security") } if !bgFlags.TPMFailureLeavesHierarchiesEnabled() { return false, fmt.Errorf("tpm failure should lead to default measurements from PCR0 to PCR7") } txtFlags := b.VData.CBNTbpm.TXTE.ControlFlags - if txtFlags.MemoryScrubbingPolicy() != cbntbootpolicy.MemoryScrubbingPolicySACM { + if txtFlags.MemoryScrubbingPolicy() != bootpolicy.MemoryScrubbingPolicySACM { return false, fmt.Errorf("S-ACM memory scrubbing should be used over the BIOS") } } @@ -927,7 +1069,7 @@ func (b *BootGuard) StrictSaneBPMSecurityProps() (bool, error) { // SaneBPMSecurityProps verifies that BPM contains security properties set accordingly to spec func (b *BootGuard) SaneBPMSecurityProps() (bool, error) { switch b.Version { - case bgheader.Version10: + case cbnt.Version10: flags := b.VData.BGbpm.SE[0].Flags if !flags.DMAProtection() { return false, fmt.Errorf("dma protection should be enabled for bootguard") @@ -941,14 +1083,15 @@ func (b *BootGuard) SaneBPMSecurityProps() (bool, error) { if len(b.VData.BGbpm.SE[0].IBBSegments) < 1 { return false, fmt.Errorf("no ibb segments measured") } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: bgFlags := b.VData.CBNTbpm.SE[0].Flags if !bgFlags.DMAProtection() { if b.VData.CBNTbpm.SE[0].DMAProtBase0 == 0 && b.VData.CBNTbpm.SE[0].VTdBAR == 0 { return false, fmt.Errorf("dma protection should be enabled for bootguard") } } - if !bgFlags.AuthorityMeasure() { + // PCR7 is not available since MTL + if b.VData.CBNTbpm.BPMHCBnT.StructInfoCBNT.Version < 0x25 && !bgFlags.AuthorityMeasure() { return false, fmt.Errorf("pcr-7 data should extended for OS security") } if b.VData.CBNTbpm.SE[0].PBETValue.PBETValue() == 0 { @@ -972,39 +1115,39 @@ func (b *BootGuard) IBBsMatchBPMDigest(image []byte) (bool, error) { return false, fmt.Errorf("can't parse firmware image") } switch b.Version { - case bgheader.Version10: + case cbnt.Version10: if err := b.VData.BGbpm.ValidateIBB(firmware); err != nil { - return false, fmt.Errorf("bpm final ibb hash doesn't match selected measurements in image") + return false, fmt.Errorf("bpm final ibb hash doesn't match selected measurements in image: %w", err) } - case bgheader.Version20: + case cbnt.Version20, cbnt.Version21: if err := b.VData.CBNTbpm.ValidateIBB(firmware); err != nil { - return false, fmt.Errorf("bpm final ibb hash doesn't match selected measurements in image") + return false, fmt.Errorf("bpm final ibb hash doesn't match selected measurements in image: %w", err) } } return true, nil } // ValidateMEAgainstManifests validates during runtime ME configuation with BootGuard KM & BPM manifests -func (b *BootGuard) ValidateMEAgainstManifests(fws *FirmwareStatus6) (bool, error) { +func (b *BootGuard) ValidateMEAgainstManifests(fws *FirmwareStatus) (bool, error) { switch b.Version { - case bgheader.Version10: - if fws.BPMSVN != uint32(b.VData.BGbpm.BPMSVN) { + case cbnt.Version10: + if fws.Status6.BPMSVN != uint32(b.VData.BGbpm.BPMSVN) { return false, fmt.Errorf("bpm svn doesn't match me configuration") } - if fws.KMSVN != uint32(b.VData.BGkm.KMSVN) { + if fws.Status6.KMSVN != uint32(b.VData.BGkm.KMSVN) { return false, fmt.Errorf("km svn doesn't match me configuration") } - if fws.KMID != uint32(b.VData.BGkm.KMID) { + if fws.Status6.KMID != uint32(b.VData.BGkm.KMID) { return false, fmt.Errorf("km KMID doesn't match me configuration") } - case bgheader.Version20: - if fws.BPMSVN > uint32(b.VData.CBNTbpm.BPMSVN) { + case cbnt.Version20: + if fws.Status6.BPMSVN > uint32(b.VData.CBNTbpm.BPMSVN) { return false, fmt.Errorf("bpm svn doesn't match me configuration") } - if fws.KMSVN != uint32(b.VData.CBNTkm.KMSVN) { + if fws.Status6.KMSVN != uint32(b.VData.CBNTkm.KMSVN) { return false, fmt.Errorf("km svn doesn't match me configuration") } - if fws.KMID != uint32(b.VData.CBNTkm.KMID) { + if fws.Status6.KMID != uint32(b.VData.CBNTkm.KMID) { return false, fmt.Errorf("km KMID doesn't match me configuration") } } @@ -1092,15 +1235,15 @@ func (b *BootGuard) CreateIBBSegments(seElement uint8, flags uint16, imagepath s } } switch b.Version { - case bgheader.Version10: - b.VData.BGbpm.SE[seElement].IBBSegments = make([]bgbootpolicy.IBBSegment, len(ibbElements)) + case cbnt.Version10: + b.VData.BGbpm.SE[seElement].IBBSegments = make([]bootpolicy.IBBSegment, len(ibbElements)) for idx, ibb := range ibbElements { b.VData.BGbpm.SE[seElement].IBBSegments[idx].Base = ibb.Base b.VData.BGbpm.SE[seElement].IBBSegments[idx].Size = ibb.Size b.VData.BGbpm.SE[seElement].IBBSegments[idx].Flags = ibb.Flags } - case bgheader.Version20: - b.VData.CBNTbpm.SE[seElement].IBBSegments = make([]cbntbootpolicy.IBBSegment, len(ibbElements)) + case cbnt.Version20, cbnt.Version21: + b.VData.CBNTbpm.SE[seElement].IBBSegments = make([]bootpolicy.IBBSegment, len(ibbElements)) for idx, ibb := range ibbElements { b.VData.CBNTbpm.SE[seElement].IBBSegments[idx].Base = ibb.Base b.VData.CBNTbpm.SE[seElement].IBBSegments[idx].Size = ibb.Size diff --git a/pkg/provisioning/bootguard/hfsts.go b/pkg/provisioning/bootguard/hfsts.go index 3c2f0a13..544c55cd 100644 --- a/pkg/provisioning/bootguard/hfsts.go +++ b/pkg/provisioning/bootguard/hfsts.go @@ -10,6 +10,40 @@ import ( // Const Array with HFSTS Offsets var hfstsOffset = []int{0x40, 0x48, 0x60, 0x64, 0x68, 0x6c} +type FirmwareStatus struct { + // ME 16 + Status1 *FirmwareStatus1 + Status6 *FirmwareStatus6 + // ME 18/21 + Status5 *FirmwareStatus5 +} + +func NewFirmwareStatus(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus, error) { + // ME 16 + hwsts1, err := getHFSTS1(hw) + if err != nil { + return nil, err + } + + hwsts6, err := getHFSTS6(hw) + if err != nil { + return nil, err + } + + hwsts5, err := getHFSTS5(hw) + if err != nil { + return nil, err + } + + return &FirmwareStatus{ + // ME 16 + Status1: hwsts1, + Status6: hwsts6, + // ME 18/21 + Status5: hwsts5, + }, nil +} + type FirmwareStatus1 struct { WorkingState uint32 MfgMode bool @@ -49,7 +83,7 @@ type FirmwareStatus6 struct { TXTSupported bool } -func GetHFSTS1(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus1, error) { +func getHFSTS1(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus1, error) { hfsts1, err := readHFSTSFromPCIConfigSpace(hw, 1) if err != nil { return nil, fmt.Errorf("couldn't read HFSTS6 from PCI config space: %v", err) @@ -77,7 +111,7 @@ func GetHFSTS1(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus1, error) { return &firmwareStatus, nil } -func GetHFSTS6(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus6, error) { +func getHFSTS6(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus6, error) { hfsts6, err := readHFSTSFromPCIConfigSpace(hw, 6) if err != nil { return nil, fmt.Errorf("couldn't read HFSTS6 from PCI config space: %v", err) @@ -109,6 +143,41 @@ func GetHFSTS6(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus6, error) { return &firmwareStatus, nil } +type FirmwareStatus5 struct { + BgACMStatus bool + VLD bool + RCS bool + ErrorCode uint32 + TXTSupported bool + CPUDebugDisabled bool + BSPInitDisabled bool + BPMExecStatus bool + BgStatus uint32 +} + +// ME 18/21 +func getHFSTS5(hw hwapi.LowLevelHardwareInterfaces) (*FirmwareStatus5, error) { + hfsts5, err := readHFSTSFromPCIConfigSpace(hw, 5) + if err != nil { + return nil, fmt.Errorf("couldn't read HFSTS5 from PCI config space: %v", err) + } + + firmwareStatus := FirmwareStatus5{} + + configSpace := binary.LittleEndian.Uint32(hfsts5) + firmwareStatus.BgACMStatus = (configSpace>>0)&1 != 0 + firmwareStatus.VLD = (configSpace>>1)&1 != 0 + firmwareStatus.RCS = (configSpace>>2)&1 != 0 + firmwareStatus.ErrorCode = (configSpace >> 3) & 31 + firmwareStatus.TXTSupported = (configSpace>>17)&1 != 0 + firmwareStatus.CPUDebugDisabled = (configSpace>>21)&1 != 0 + firmwareStatus.BSPInitDisabled = (configSpace>>22)&1 != 0 + firmwareStatus.BPMExecStatus = (configSpace>>23)&1 != 0 + firmwareStatus.BgStatus = (configSpace >> 25) & 15 + + return &firmwareStatus, nil +} + func readHFSTSFromPCIConfigSpace(hw hwapi.LowLevelHardwareInterfaces, offset int) ([]byte, error) { if offset < 1 || offset > 6 { return nil, fmt.Errorf("invalid HFSTS offset") diff --git a/pkg/provisioning/bootguard/me.go b/pkg/provisioning/bootguard/me.go index d2cc25d7..032dad0b 100644 --- a/pkg/provisioning/bootguard/me.go +++ b/pkg/provisioning/bootguard/me.go @@ -5,7 +5,7 @@ import ( "github.com/9elements/converged-security-suite/v2/pkg/tools" "github.com/9elements/go-linux-lowlevel-hw/pkg/hwapi" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/bgheader" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" ) const ( @@ -55,8 +55,8 @@ func GetBGInfo(hw hwapi.LowLevelHardwareInterfaces) (*BGInfo, error) { return &bgi, nil } -func StrictSaneBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareStatus6, bgi *BGInfo) (bool, error) { - if fws.ErrorEnforcementPolicy != EnforcementPolicyShutdownImmediately { +func StrictSaneBootGuardProvisioning(v cbnt.BootGuardVersion, fws *FirmwareStatus, bgi *BGInfo) (bool, error) { + if fws.Status6.ErrorEnforcementPolicy != EnforcementPolicyShutdownImmediately { return false, fmt.Errorf("enforcement policy isn't set to immediate shutdown") } @@ -64,42 +64,90 @@ func StrictSaneBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareS } // SaneMEBootGuardProvisioning validates during runtime ME bootguard provisioning -func SaneMEBootGuardProvisioning(v bgheader.BootGuardVersion, fws *FirmwareStatus6, bgi *BGInfo) (bool, error) { - if fws.BypassBootPolicy { - return false, fmt.Errorf("bypass boot policy is active") - } - if fws.BootPolicyInvalid { - return false, fmt.Errorf("boot policy is invalid") - } - if !fws.FPFLock { - return false, fmt.Errorf("FPF isn't locked") - } - if fws.ErrorEnforcementPolicy == EnforcementPolicyDoNothing || - fws.ErrorEnforcementPolicy == EnforcementPolicyShutdownSomehow { - return false, fmt.Errorf("enforcement policy is lazy and doesn't stop boot process") - } - if !fws.ProtectBIOSEnvironment { - return false, fmt.Errorf("protected bios enviroment is disabled") - } - if v == bgheader.Version20 && !bgi.ForceAnchorBoot { - return false, fmt.Errorf("force anchor boot is disabled") - } - if !bgi.Verified { - return false, fmt.Errorf("verified boot is disabled, measured boot only may be possible but isn't supported by Intel officially") - } - if bgi.ModuleRevoked { - return false, fmt.Errorf("one of the the ACM, BPM and KM may be revoked") - } - if fws.BootGuardDisable { - return false, fmt.Errorf("boot guard is disabled") +func SaneMEBootGuardProvisioning(v cbnt.BootGuardVersion, fws *FirmwareStatus, bgi *BGInfo) (bool, error) { + ver, err := tools.GetMEVersion() + if err != nil { + return false, err } if !bgi.BootGuardCapability { return false, fmt.Errorf("missing boot guard microcode updates in FIT") } + + switch ver { + case tools.Version16: + if fws.Status6.BypassBootPolicy { + return false, fmt.Errorf("bypass boot policy is active") + } + if fws.Status6.BootPolicyInvalid { + return false, fmt.Errorf("boot policy is invalid") + } + if !fws.Status6.FPFLock { + return false, fmt.Errorf("FPF isn't locked") + } + if fws.Status6.ErrorEnforcementPolicy == EnforcementPolicyDoNothing || + fws.Status6.ErrorEnforcementPolicy == EnforcementPolicyShutdownSomehow { + return false, fmt.Errorf("enforcement policy is lazy and doesn't stop boot process") + } + if !fws.Status6.ProtectBIOSEnvironment { + return false, fmt.Errorf("protected bios enviroment is disabled") + } + if v == cbnt.Version20 && !bgi.ForceAnchorBoot { + return false, fmt.Errorf("force anchor boot is disabled") + } + if !bgi.Verified { + return false, fmt.Errorf("verified boot is disabled, measured boot only may be possible but isn't supported by Intel officially") + } + if bgi.ModuleRevoked { + return false, fmt.Errorf("one of the the ACM, BPM and KM may be revoked") + } + if fws.Status6.BootGuardDisable { + return false, fmt.Errorf("boot guard is disabled") + } + if !bgi.BootGuardCapability { + return false, fmt.Errorf("missing boot guard microcode updates in FIT") + } + case tools.Version18, tools.Version21: + if !fws.Status6.FPFLock { + return false, fmt.Errorf("FPF is not locked") + } + if fws.Status1.MfgMode { + return false, fmt.Errorf("debug mode is enabled") + } + if !fws.Status5.VLD { + return false, fmt.Errorf("bits that follow are invalid") + } + if fws.Status5.RCS { + return false, fmt.Errorf("RCS does not come from ACM") + } + if !fws.Status5.CPUDebugDisabled { + return false, fmt.Errorf("cpu debug is enabled") + } + if fws.Status1.WorkingState != 0x05 { + return false, fmt.Errorf("invalid working state") + } + if fws.Status1.OperatingMode != 0 { + return false, fmt.Errorf("invalid operating mode") + } + if (v == cbnt.Version20 || v == cbnt.Version21) && !bgi.ForceAnchorBoot { + return false, fmt.Errorf("force anchor boot is disabled") + } + if !bgi.Verified { + return false, fmt.Errorf("verified boot is disabled, measured boot only may be possible but isn't supported by Intel officially") + } + if bgi.ModuleRevoked { + return false, fmt.Errorf("one of the the ACM, BPM and KM may be revoked") + } + if fws.Status6.BootGuardDisable { + return false, fmt.Errorf("boot guard is disabled") + } + if !bgi.BootGuardCapability { + return false, fmt.Errorf("missing boot guard microcode updates in FIT") + } + } return true, nil } -func ValidTXTRegister(hw hwapi.LowLevelHardwareInterfaces) (bool, error) { +func ValidACMStatus(hw hwapi.LowLevelHardwareInterfaces) (bool, error) { txtSpace, err := tools.FetchTXTRegs(hw) if err != nil { return false, fmt.Errorf("couldn't fetch TXT regs: %v", err) @@ -132,9 +180,84 @@ func ValidTXTRegister(hw hwapi.LowLevelHardwareInterfaces) (bool, error) { return false, fmt.Errorf("couldn't read bootstatus: %v", err) } - if ((Bootstatus >> 31) & 0x1) != 1 { + if !Bootstatus.BgSuccess { + return false, fmt.Errorf("BootGuard did not startup successfully") + } + + return true, nil +} + +func ValidTXTRegisters(hw hwapi.LowLevelHardwareInterfaces) (bool, error) { + txtSpace, err := tools.FetchTXTRegs(hw) + if err != nil { + return false, fmt.Errorf("couldn't fetch TXT regs: %v", err) + } + + // The check for ACM status by reading txtSpace >> 0x328 only gives a meaningful + // results if TXT is disabled in BIOS by the user. Otherwise the same address will be + // used as TXT.ERRORCODE register, and filled with the TXT status. Now given that TXT started + // successfully, bit 31 will change the meaning, i.e. if set, there is some error that we could + // further evaluate, otherwise we shall ignore the rest. Thus, let's keep the 'old' logic iff TXT + // is disabled. The check can be done by reading 15th bit of IA32_FEATURE_CONTROL MSR. + txtEnabled := hw.ReadMSR(0x3a) + + Bootstatus, err := tools.ReadBootStatusRaw(txtSpace) + if err != nil { + return false, fmt.Errorf("couldn't read bootstatus: %v", err) + } + + if (txtEnabled >> 15) == 0 { + ACMStatus, err := tools.ReadACMStatus(txtSpace) + if err != nil { + return false, fmt.Errorf("couldn't read ACM status: %v", err) + } + + if !ACMStatus.Valid { + return false, fmt.Errorf("ACM status is invalid") + } + + if !ACMStatus.ACMStarted { + return false, fmt.Errorf("ACM isn't started") + } + + } else { + if !Bootstatus.TxtSuccess { + return false, fmt.Errorf("TXT did not startup successfully") + } + } + + ACMStatusPolicy, err := tools.ReadACMPolicyStatusRaw(txtSpace) + if err != nil { + return false, fmt.Errorf("couldn't read ACM policy status: %v", err) + } + + if ((ACMStatusPolicy >> 6) & 0x1) != 0 { + return false, fmt.Errorf("HAP Bit is set") + } + + if ((ACMStatusPolicy >> 4) & 0x1) != 1 { + return false, fmt.Errorf("Measured boot is disabled") + } + + if ((ACMStatusPolicy >> 5) & 0x1) != 1 { + return false, fmt.Errorf("verified boot is disabled") + } + + if !Bootstatus.BgSuccess { return false, fmt.Errorf("BootGuard did not startup successfully") } + if !Bootstatus.BIOSTrusted { + return false, fmt.Errorf("bios is not trusted") + } + + if !Bootstatus.SACMSuccess { + return false, fmt.Errorf("SACM status invalid") + } + + if Bootstatus.CPUError { + return false, fmt.Errorf("CPU error") + } + return true, nil } diff --git a/pkg/provisioning/bootguard/structures.go b/pkg/provisioning/bootguard/structures.go index 4c1d1b60..58b90b0e 100644 --- a/pkg/provisioning/bootguard/structures.go +++ b/pkg/provisioning/bootguard/structures.go @@ -1,12 +1,9 @@ package bootguard import ( - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/bg/bgkey" "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntkey" - "github.com/linuxboot/fiano/pkg/intel/metadata/common/bgheader" + bootpolicy "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/bootpolicy" + keymanifest "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/keymanifest" ) // CMOSIoAddress holds information about the location of on-demand power down requests in CMOS. @@ -75,14 +72,14 @@ type KeyHash struct { // Options contains all version bootguard options type VersionedData struct { - BGbpm *bgbootpolicy.Manifest `json:"v1-bootpolicy,omitempty"` - BGkm *bgkey.Manifest `json:"v1-keymanifest,omitempty"` - CBNTbpm *cbntbootpolicy.Manifest `json:"v2-bootpolicy,omitempty"` - CBNTkm *cbntkey.Manifest `json:"v2-keymanifest,omitempty"` + BGbpm *bootpolicy.ManifestBG `json:"v1-bootpolicy,omitempty"` + BGkm *keymanifest.BGManifest `json:"v1-keymanifest,omitempty"` + CBNTbpm *bootpolicy.ManifestCBnT `json:"v2-bootpolicy,omitempty"` + CBNTkm *keymanifest.CBnTManifest `json:"v2-keymanifest,omitempty"` } // BootGuard unification structure, operates on manifests and reader type BootGuard struct { VData VersionedData `json:"bootguard"` - Version bgheader.BootGuardVersion + Version cbnt.BootGuardVersion } diff --git a/pkg/test/bootguard_tests.go b/pkg/test/bootguard_tests.go index d57411e2..ad2f2118 100644 --- a/pkg/test/bootguard_tests.go +++ b/pkg/test/bootguard_tests.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" + "github.com/9elements/converged-security-suite/v2/pkg/intel" "github.com/9elements/converged-security-suite/v2/pkg/provisioning/bootguard" "github.com/9elements/converged-security-suite/v2/pkg/tools" "github.com/9elements/go-linux-lowlevel-hw/pkg/hwapi" @@ -16,8 +17,13 @@ const ( ) var ( + legacy = []intel.BgVersion{intel.BootGuard, intel.CBnT20} + cbnt21 = []intel.BgVersion{intel.CBnT21} + all = []intel.BgVersion{intel.BootGuard, intel.CBnT20, intel.CBnT21} + testbootguardfit = Test{ Name: "FIT meets BootGuard requirements", + Description: "Checks FIT has all required Boot Guard records (ACM, BPM, and KM).", Required: true, function: BootGuardFIT, Status: Implemented, @@ -27,6 +33,7 @@ var ( } testbootguardacm = Test{ Name: "SACM meets sane BootGuard requirements", + Description: "Parses SACM and validates production mode, ACM type, and optional chipset match.", Required: true, function: BootGuardACM, dependencies: []*Test{&testbootguardfit}, @@ -37,6 +44,7 @@ var ( } testbootguardkm = Test{ Name: "Key Manifest meets sane BootGuard requirements", + Description: "Parses Key Manifest and validates signature, crypto safety, and BPM hash presence.", Required: true, function: BootGuardKM, dependencies: []*Test{&testbootguardfit}, @@ -47,6 +55,7 @@ var ( } testbootguardbpm = Test{ Name: "Boot Policy Manifest meets sane BootGuard requirements", + Description: "Parses BPM/KM and validates BPM structure, signature, security properties, and KM binding.", Required: true, function: BootGuardBPM, dependencies: []*Test{&testbootguardfit}, @@ -57,6 +66,7 @@ var ( } testbootguardibb = Test{ Name: "Verifies BPM and IBBs match firmware image", + Description: "Verifies measured IBBs from firmware match the BPM final IBB digest.", Required: true, function: BootGuardIBB, dependencies: []*Test{&testbootguardfit}, @@ -67,6 +77,7 @@ var ( } testbootguardvalidateme = Test{ Name: "[RUNTIME] Validates Intel ME specific configuration against KM/BPM in firmware image", + Description: "Compares runtime ME Boot Guard status against KM/BPM policy requirements.", Required: true, function: BootGuardValidateME, dependencies: []*Test{&testbootguardfit}, @@ -74,26 +85,53 @@ var ( SpecificationChapter: "", SpecificiationTitle: IntelBootGuardSpecificationTitle, SpecificationDocumentID: IntelBootGuardSpecificationDocumentID, + SupportedVersion: legacy, + } + testbootguardmebootguardsts = Test{ + Name: "[RUNTIME] Verifies Intel ME Boot Guard status", + Description: "Reads Boot Guard related information from ME and checks if they are sane (requires ME 18/21)", + Required: true, + function: BootGuardMESts, + dependencies: []*Test{&testbootguardfit}, + Status: Implemented, + SpecificationChapter: "", + SpecificiationTitle: "Intel Converged Security and Management Engine 18.x/19.x BIOS Specification / Intel Converged Security and Management Engine 21.0", + SpecificationDocumentID: "729124 / 829718", + SupportedVersion: cbnt21, } testbootguardsanemeconfig = Test{ Name: "[RUNTIME] Verifies Intel ME Boot Guard configuration is sane and safe", + Description: "Checks runtime ME Boot Guard provisioning state is sane (strict or relaxed profile).", Required: true, function: BootGuardSaneMEConfig, Status: Implemented, SpecificationChapter: "", SpecificiationTitle: IntelBootGuardSpecificationTitle, SpecificationDocumentID: IntelBootGuardSpecificationDocumentID, + SupportedVersion: all, } - testbootguardtxt = Test{ - Name: "[RUNTIME] BtG/TXT registers are sane", + testbootguardbgacmsts = Test{ + Name: "[RUNTIME] Verifies post-boot ACM status", + Description: "Validates runtime TXT registers for a secure post-boot ACM status.", Required: true, - function: BootGuardTXT, + function: BootGuardTXTACMSts, Status: Implemented, SpecificationChapter: "", SpecificiationTitle: IntelTXTSpecificationTitle, SpecificationDocumentID: IntelTXTSpecificationDocumentID, + SupportedVersion: legacy, + } + testbootguardtxtsts = Test{ + Name: "[RUNTIME] Verifies post-boot BtG/TXT registers", + Description: "Validates runtime TXT/Boot Guard registers for a secure post boot status.", + Required: true, + function: BootGuardTXTRegisters, + Status: Implemented, + SpecificationChapter: "", + SpecificiationTitle: IntelTXTSpecificationTitle, + SpecificationDocumentID: IntelTXTSpecificationDocumentID, + SupportedVersion: all, } - // TestsMemory exposes the slice for memory related txt tests TestsBootGuard = [...]*Test{ &testbootguardfit, @@ -102,8 +140,10 @@ var ( &testbootguardbpm, &testbootguardibb, &testbootguardvalidateme, + &testbootguardmebootguardsts, &testbootguardsanemeconfig, - &testbootguardtxt, + &testbootguardbgacmsts, + &testbootguardtxtsts, } ) @@ -299,17 +339,38 @@ func BootGuardValidateME(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, if b == nil || err != nil { return false, fmt.Errorf("couldn't parse KM and BPM"), err } - hfsts6, err := bootguard.GetHFSTS6(hw) + hfsts, err := bootguard.NewFirmwareStatus(hw) if err != nil { - return false, err, nil + return false, fmt.Errorf("couldn't read Intel ME firmware status"), err } - valid, err := b.ValidateMEAgainstManifests(hfsts6) + valid, err := b.ValidateMEAgainstManifests(hfsts) if !valid || err != nil { return false, fmt.Errorf("bootguard km/bpm doesn't match ME BootGuard configuration"), err } return true, nil, nil } +// BootGuardMESts +func BootGuardMESts(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { + hfsts, err := bootguard.NewFirmwareStatus(hw) + if err != nil { + return false, fmt.Errorf("couldn't read Intel ME firmware status"), err + } + if !hfsts.Status5.BgACMStatus { + return false, fmt.Errorf("acm is not active"), err + } + if hfsts.Status5.ErrorCode != 0 { + return false, fmt.Errorf("bg startup failed"), err + } + if !hfsts.Status5.BPMExecStatus { + return false, fmt.Errorf("bpm not executed"), err + } + if hfsts.Status5.BgStatus != 0x01 { + return false, fmt.Errorf("bg status is invalid"), err + } + return true, nil, nil +} + // BootGuardSaneMEConfig func BootGuardSaneMEConfig(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { entries, err := fit.GetEntries(p.Firmware) @@ -328,23 +389,23 @@ func BootGuardSaneMEConfig(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool return false, fmt.Errorf("couldn't parse KM"), err } - hfsts6, err := bootguard.GetHFSTS6(hw) + hfsts, err := bootguard.NewFirmwareStatus(hw) if err != nil { - return false, fmt.Errorf("couldn't read HFSTS6: %v", err), nil + return false, fmt.Errorf("couldn't read HFSTS6"), err } bgi, err := bootguard.GetBGInfo(hw) if err != nil { - return false, err, nil + return false, fmt.Errorf("couldn't read Boot Guard runtime info"), err } if p.Strict { - valid, err := bootguard.StrictSaneBootGuardProvisioning(b.Version, hfsts6, bgi) + valid, err := bootguard.StrictSaneBootGuardProvisioning(b.Version, hfsts, bgi) if !valid || err != nil { return false, fmt.Errorf("provisiong boot guard configuraton in me isn't safe"), err } } else { - valid, err := bootguard.SaneMEBootGuardProvisioning(b.Version, hfsts6, bgi) + valid, err := bootguard.SaneMEBootGuardProvisioning(b.Version, hfsts, bgi) if !valid || err != nil { return false, fmt.Errorf("provisiong boot guard configuraton in me isn't safe"), err } @@ -352,9 +413,19 @@ func BootGuardSaneMEConfig(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool return true, nil, nil } -// BootGuardTXT checks TXT requirements for safe BootGuard configuration -func BootGuardTXT(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { - valid, err := bootguard.ValidTXTRegister(hw) +// BootGuardTXTACMSts checks TXT requirements for safe BootGuard configuration +func BootGuardTXTACMSts(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { + valid, err := bootguard.ValidACMStatus(hw) + if !valid || err != nil { + return false, fmt.Errorf("txt regs aren't valid"), err + } + + return true, nil, nil +} + +// BootGuardTXTRegisters checks TXT requirements for safe BootGuard configuration +func BootGuardTXTRegisters(hw hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { + valid, err := bootguard.ValidTXTRegisters(hw) if !valid || err != nil { return false, fmt.Errorf("txt regs aren't valid"), err } diff --git a/pkg/test/fit.go b/pkg/test/fit.go index fc1afc4f..5dfcc5a1 100644 --- a/pkg/test/fit.go +++ b/pkg/test/fit.go @@ -371,7 +371,11 @@ func HasBIOSPolicy(txtAPI hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, er func getFITDataSize(hdr fit.EntryHeaders, txtAPI hwapi.LowLevelHardwareInterfaces) uint64 { firmware := newTXTAPIFirmwareReadSeeker(txtAPI) - result, err := fit.EntryDataSegmentSize(fit.NewEntry(&hdr, firmware), firmware) + fwSize, err := fit.FirmwareSizeUsedFromSeeker(firmware) + if err != nil { + panic(err) + } + result, err := fit.EntryDataSegmentSize(fit.NewEntry(&hdr, firmware, fwSize), firmware, fwSize) if err != nil { panic(err) } diff --git a/pkg/test/test.go b/pkg/test/test.go index 0cf54ff9..cd8f438b 100644 --- a/pkg/test/test.go +++ b/pkg/test/test.go @@ -3,6 +3,7 @@ package test import ( "fmt" + "github.com/9elements/converged-security-suite/v2/pkg/intel" "github.com/9elements/go-linux-lowlevel-hw/pkg/hwapi" ) @@ -50,8 +51,9 @@ func (t Status) String() string { // Test exposes the structure in which information about TXT tests are held type Test struct { - Name string - Required bool + Name string + Required bool + Description string //testerror: If test fails and returns an testerror -> test failure //internalerror: If test fails and returns an internalerror //-> mostly api errors, but not directly testrelated problem. @@ -68,6 +70,8 @@ type Test struct { // The specification used in this test SpecificiationTitle string SpecificationDocumentID string + // Only relevant for the runtime tests + SupportedVersion []intel.BgVersion } // Run implements the genereal test function and exposes it. diff --git a/pkg/test/test_test.go b/pkg/test/test_test.go index faea8af7..e9c3c1da 100644 --- a/pkg/test/test_test.go +++ b/pkg/test/test_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/9elements/converged-security-suite/v2/pkg/intel" "github.com/9elements/converged-security-suite/v2/pkg/tools" "github.com/9elements/go-linux-lowlevel-hw/pkg/hwapi" "github.com/google/go-tpm/legacy/tpm2" @@ -13,16 +14,19 @@ func TestTest_Run(t *testing.T) { type fields struct { Name string Required bool + Description string function func(hwapi.LowLevelHardwareInterfaces, *PreSet) (bool, error, error) Result Result dependencies []*Test ErrorText string Status Status + SuppVersion []intel.BgVersion } BNotImplemented := Test{ "Test B", true, + "This is Test B", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return true, nil, nil }, @@ -34,11 +38,13 @@ func TestTest_Run(t *testing.T) { "", "", "", + []intel.BgVersion{intel.BootGuard}, } BFailed := Test{ "Test B", true, + "This is Test B", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return true, nil, nil }, @@ -50,10 +56,12 @@ func TestTest_Run(t *testing.T) { "", "", "", + []intel.BgVersion{intel.BootGuard}, } BNotRun := Test{ "Test B", true, + "This is Test B", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return true, nil, nil }, @@ -65,6 +73,7 @@ func TestTest_Run(t *testing.T) { "", "", "", + []intel.BgVersion{intel.CBnT20}, } tests := []struct { @@ -78,6 +87,7 @@ func TestTest_Run(t *testing.T) { fields{ "Test A, ignores unimplemented Test B", true, + "This is Test A", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return true, nil, nil }, @@ -85,6 +95,7 @@ func TestTest_Run(t *testing.T) { []*Test{&BNotImplemented}, "", Implemented, + []intel.BgVersion{intel.BootGuard}, }, true, ResultPass, @@ -94,6 +105,7 @@ func TestTest_Run(t *testing.T) { fields{ "Test A, fails on failed dependency Test B", true, + "This is Test A", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return true, nil, nil }, @@ -101,6 +113,7 @@ func TestTest_Run(t *testing.T) { []*Test{&BFailed}, "", Implemented, + []intel.BgVersion{intel.BootGuard}, }, false, ResultDependencyFailed, @@ -110,6 +123,7 @@ func TestTest_Run(t *testing.T) { fields{ "Test A, runs dependency Test B", true, + "This is Test A", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return BNotRun.Result == ResultPass, nil, nil }, @@ -117,6 +131,7 @@ func TestTest_Run(t *testing.T) { []*Test{&BNotRun}, "", Implemented, + []intel.BgVersion{intel.BootGuard}, }, true, ResultPass, @@ -126,6 +141,7 @@ func TestTest_Run(t *testing.T) { fields{ "Test A, multiple dependencies", true, + "This is Test A", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return BNotRun.Result == ResultPass, nil, nil }, @@ -133,6 +149,7 @@ func TestTest_Run(t *testing.T) { []*Test{&BNotRun, &BNotImplemented}, "", Implemented, + []intel.BgVersion{intel.BootGuard}, }, true, ResultPass, @@ -142,6 +159,7 @@ func TestTest_Run(t *testing.T) { fields{ "Test A, returns internal error", true, + "This is Test A", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return true, nil, fmt.Errorf("Internal error 24") }, @@ -149,6 +167,7 @@ func TestTest_Run(t *testing.T) { []*Test{}, "", Implemented, + []intel.BgVersion{intel.BootGuard}, }, false, ResultInternalError, @@ -158,6 +177,7 @@ func TestTest_Run(t *testing.T) { fields{ "Test A, returns error", true, + "This is Test A", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return true, fmt.Errorf("error 1"), nil }, @@ -165,6 +185,7 @@ func TestTest_Run(t *testing.T) { []*Test{}, "", Implemented, + []intel.BgVersion{intel.BootGuard}, }, false, ResultFail, @@ -174,6 +195,7 @@ func TestTest_Run(t *testing.T) { fields{ "Test A, returns error, but is critical", true, + "This is Test A", func(a hwapi.LowLevelHardwareInterfaces, p *PreSet) (bool, error, error) { return false, fmt.Errorf("error 1"), nil }, @@ -181,6 +203,7 @@ func TestTest_Run(t *testing.T) { []*Test{}, "", Implemented, + []intel.BgVersion{intel.BootGuard}, }, false, ResultFail, diff --git a/pkg/tools/me.go b/pkg/tools/me.go new file mode 100644 index 00000000..5acd3b19 --- /dev/null +++ b/pkg/tools/me.go @@ -0,0 +1,52 @@ +package tools + +import ( + "fmt" + "strconv" + "strings" + + "github.com/prometheus/procfs/sysfs" +) + +type MEVersion uint8 + +const ( + Version16 MEVersion = 16 + Version18 MEVersion = 18 + Version21 MEVersion = 21 +) + +func GetMEVersion() (MEVersion, error) { + fs, err := sysfs.NewFS("/sys") + if err != nil { + return 0, err + } + + mei, err := fs.MEIClass() + if err != nil { + return 0, err + } + + // There are always 4 lines in fw_version (no clue why) exposed in sysfs. + // So lets take the first line and then look for what is interesting for us, + // i.e. 0:N where N is one of the defined MEVersion's + fline := strings.Split(*mei.FWVersion, "\n") + felem := strings.Split(fline[0], ".") + pref := strings.Split(felem[0], ":") + + ver, err := strconv.Atoi(pref[1]) + if err != nil { + return 0, err + } + + switch ver { + case 16: + return Version16, nil + case 18: + return Version18, nil + case 21: + return Version21, nil + } + + return 0, fmt.Errorf("unknown ME version") +} diff --git a/pkg/tools/txt.go b/pkg/tools/txt.go index 0fa6957c..27286613 100644 --- a/pkg/tools/txt.go +++ b/pkg/tools/txt.go @@ -126,6 +126,18 @@ type TXTBiosMLEFlags struct { IsClientState bool } +// TXTBootStatus holds the results of SACM read from TXT config space +type TXTBootStatus struct { + TxtSuccess bool + BgSuccess bool + Bboot bool + PFRSuccess bool + BgFail bool + BIOSTrusted bool + CPUError bool + SACMSuccess bool +} + // FetchTXTRegs returns a raw copy of the TXT config space func FetchTXTRegs(txtAPI hwapi.LowLevelHardwareInterfaces) ([]byte, error) { data := make([]byte, 0x1000) @@ -448,16 +460,27 @@ func ReadACMPolicyStatusRaw(data []byte) (uint64, error) { } // ReadBootStatusRaw decodes the raw boot status register bits -func ReadBootStatusRaw(data []byte) (uint64, error) { +func ReadBootStatusRaw(data []byte) (TXTBootStatus, error) { + var ret TXTBootStatus var u64 uint64 buf := bytes.NewReader(data) _, err := buf.Seek(txtBootStatus, io.SeekStart) if err != nil { - return 0, err + return ret, err } err = binary.Read(buf, binary.LittleEndian, &u64) if err != nil { - return 0, err + return ret, err } - return u64, nil + + ret.TxtSuccess = (u64>>30)&1 != 0 + ret.BgSuccess = (u64>>31)&1 != 0 + ret.Bboot = (u64>>32)&1 != 0 + ret.PFRSuccess = (u64>>33)&1 != 0 + ret.BgFail = (u64>>48)&1 != 0 + ret.BIOSTrusted = (u64>>59)&1 != 0 + ret.CPUError = (u64>>62)&1 != 0 + ret.SACMSuccess = (u64>>63)&1 != 0 + + return ret, nil }