9esec-security-tooling/cmd/cbnt-prov/cmd.go

815 lines
27 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/linuxboot/fiano/pkg/uefi"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/key"
"github.com/9elements/converged-security-suite/v2/pkg/provisioning/cbnt"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
)
type context struct {
Debug bool
}
type versionCmd struct {
}
type templateCmd 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 Manufacturers BPM revision number."`
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
ACMSVN manifest.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 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 []manifest.Algorithm `flag optional name:"ibbhash" help:"IBB Hash Algorithm"`
IbbSegbase uint32 `flag optional name:"ibbsegbase" help:"Value for IbbSegment structure"`
IbbSegsize uint32 `flag optional name:"ibbsegsize" help:"Value for IBB segment structure"`
IbbSegFlag uint16 `flag optional name:"ibbsegflag" help:"Reducted"`
// TXT args
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 {
Path string `arg required name:"path" help:"Path to the Key Manifest binary file." type:"path"`
}
type bpmPrintCmd struct {
Path string `arg required name:"path" help:"Path to the Boot Policy Manifest binary file." type:"path"`
}
type acmPrintCmd struct {
Path string `arg required name:"path" help:"Path to the ACM binary file." type:"path"`
}
type biosPrintCmd struct {
Path string `arg required name:"path" help:"Path to the full BIOS binary file." type:"path"`
}
type acmExportCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly generated ACM binary file." type:"path"`
}
type kmExportCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly generated KM binary file." type:"path"`
}
type bpmExportCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly generated BPM binary file." type:"path"`
}
type generateKMCmd 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 Manufacturers BPM revision number."`
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
ID uint8 `flag optional name:"id" help:"The key Manifest Identifier"`
PKHashAlg manifest.Algorithm `flag optional name:"pkhashalg" help:"Hash algorithm of OEM public key digest"`
KMHashes []key.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 manifest.Algorithm `flag optional name:"bpmhashalgo" help:"Hash algorithm for bpm public signing key"`
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 generateBPMCmd struct {
BPM string `arg required name:"bpm" help:"Path to the newly generated Boot Policy Manifest binary file." type:"path"`
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 Manufacturers BPM revision number."`
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
ACMSVN manifest.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 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 []manifest.Algorithm `flag optional name:"ibbhash" help:"IBB Hash Algorithm"`
IbbSegbase uint32 `flag optional name:"ibbsegbase" help:"Value for IbbSegment structure"`
IbbSegsize uint32 `flag optional name:"ibbsegsize" help:"Value for IBB segment structure"`
IbbSegFlag uint16 `flag optional name:"ibbsegflag" help:"Reducted"`
Coreboot bool `flag optional name:"coreboot" help:"Required when BIOS binary file is a coreboot image"`
// TXT args
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."`
}
type signKMCmd struct {
KmIn string `arg required name:"kmin" help:"Path to the generated Key Manifest binary file." type:"path"`
KmOut string `arg required name:"kmout" help:"Path to write the signed KM to"`
Key string `arg required name:"km-keyfile" help:"Path to the encrypted PKCS8 private key file." type:"path"`
Password string `arg required name:"password" help:"Password to decrypted PKCS8 private key file"`
}
type signBPMCmd struct {
BpmIn string `arg required name:"bpmin" help:"Path to the newly generated Boot Policy Manifest binary file." type:"path"`
BpmOut string `arg required name."bpmout" help:"Path to write the signed BPM to"`
Key string `arg required name:"bpm-keyfile" help:"Path to the encrypted PKCS8 private key file." type:"path"`
Password string `arg required name:"password" help:"Password to decrypt PKCS8 private key file"`
}
type readConfigCmd struct {
Config string `arg required name:"config" help:"Path to the JSON config file." type:"path"`
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
}
type stitchingKMCmd struct {
KM string `arg required name:"km" help:"Path to the Key Manifest binary file." type:"path"`
Signature string `arg required name:"signature" help:"Path to the Key Manifest signature file." type:"path"`
PubKey string `arg required name:"pubkey" help:"Path to the Key Manifest public key file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly stitched KM binary file." type:"path"`
}
type stitchingBPMCmd struct {
BPM string `arg required name:"bpm" help:"Path to the Boot Policy Manifest binary file." type:"path"`
Signature string `arg required name:"signature" help:"Path to the Boot Policy Manifest signature file." type:"path"`
PubKey string `arg required name:"pubkey" help:"Path to the Boot Policy Manifest public key file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly stitched BPM binary file." type:"path"`
}
type stitchingCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
ACM string `flag optional name:"acm" help:"Path to the ACM binary file." type:"path"`
KM string `flag optional name:"km" help:"Path to the Key Manifest binary file." type:"path"`
BPM string `flag optional name:"bpm" help:"Path to the Boot Policy Manifest binary file." type:"path"`
ME string `flag optional name:"me" help:"Path to the Management Engine binary file." type:"path"`
}
type keygenCmd struct {
Algo string `arg require name:"algo" help:"Select crypto algorithm for key generation. Options: RSA2048. RSA3072, ECC224, ECC256"`
Password string `arg required name:"password" help:"Password for AES256 encryption of private keys"`
Path string `flag optional name:"path" help:"Path to store keys. File names are 'yourname_bpm/yourname_bpm.pub' and 'yourname_km/yourname_km.pub' respectivly"`
}
func (v *versionCmd) Run(ctx *context) error {
tools.ShowVersion(programName, gittag, gitcommit)
return nil
}
func (kmp *kmPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(kmp.Path)
if err != nil {
return err
}
reader := bytes.NewReader(data)
km, err := cbnt.ParseKM(reader)
if err != nil {
return err
}
km.Print()
if km.KeyAndSignature.Signature.DataTotalSize() > 1 {
if err := km.KeyAndSignature.Key.PrintKMPubKey(km.PubKeyHashAlg); err != nil {
return err
}
}
return nil
}
func (bpmp *bpmPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(bpmp.Path)
if err != nil {
return err
}
reader := bytes.NewReader(data)
bpm, err := cbnt.ParseBPM(reader)
if err != nil {
return err
}
bpm.Print()
if bpm.PMSE.Signature.DataTotalSize() > 1 {
if err := bpm.PMSE.KeySignature.Key.PrintBPMPubKey(bpm.PMSE.Signature.HashAlg); err != nil {
return err
}
}
return nil
}
func (acmp *acmPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(acmp.Path)
if err != nil {
return err
}
acm, chipsets, processors, tpms, err, err2 := tools.ParseACM(data)
if err != nil {
return err
}
if err2 != nil {
return err2
}
acm.PrettyPrint()
chipsets.PrettyPrint()
processors.PrettyPrint()
tpms.PrettyPrint()
return nil
}
func (biosp *biosPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(biosp.Path)
if err != nil {
return err
}
err = cbnt.PrintFIT(data)
if err != nil {
return err
}
err = cbnt.PrintCBnTStructures(data)
if err != nil {
return err
}
return nil
}
func (acme *acmExportCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(acme.BIOS)
if err != nil {
return err
}
acmfile, err := os.Create(acme.Out)
if err != nil {
return err
}
err = cbnt.WriteCBnTStructures(data, nil, nil, acmfile)
if err != nil {
return err
}
return nil
}
func (kme *kmExportCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(kme.BIOS)
if err != nil {
return err
}
kmfile, err := os.Create(kme.Out)
if err != nil {
return err
}
err = cbnt.WriteCBnTStructures(data, nil, kmfile, nil)
if err != nil {
return err
}
return nil
}
func (bpme *bpmExportCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(bpme.BIOS)
if err != nil {
return err
}
bpmfile, err := os.Create(bpme.Out)
if err != nil {
return err
}
err = cbnt.WriteCBnTStructures(data, bpmfile, nil, nil)
if err != nil {
return err
}
return nil
}
func (g *generateKMCmd) Run(ctx *context) error {
var options *cbnt.Options
if g.Config != "" {
cbnto, err := cbnt.ParseConfig(g.Config)
if err != nil {
return err
}
options = cbnto
} else {
var cbnto cbnt.Options
tmpKM := key.NewManifest()
tmpKM.Revision = g.Revision
tmpKM.KMSVN = g.SVN
tmpKM.KMID = g.ID
tmpKM.PubKeyHashAlg = g.PKHashAlg
tmpKM.Hash = g.KMHashes
// Create KM_Hash for BPM pub signing key
if g.BpmPubkey != "" {
kh, err := cbnt.GetBPMPubHash(g.BpmPubkey, g.BpmHashAlg)
if err != nil {
return err
}
tmpKM.Hash = kh
}
cbnto.KeyManifest = tmpKM
options = &cbnto
}
key, err := cbnt.ReadPubKey(g.Key)
if err != nil {
return err
}
if err := options.KeyManifest.KeyAndSignature.Key.SetPubKey(key); err != nil {
return err
}
if g.PrintME {
if options.KeyManifest.KeyAndSignature.Signature.DataTotalSize() > 1 {
if err := options.KeyManifest.KeyAndSignature.Key.PrintKMPubKey(options.KeyManifest.PubKeyHashAlg); err != nil {
return err
}
}
}
bKM, err := cbnt.WriteKM(options.KeyManifest)
if err != nil {
return err
}
if g.Out != "" {
out, err := os.Create(g.Out)
if err != nil {
return err
}
if err := cbnt.WriteConfig(out, options); err != nil {
return err
}
}
if g.Cut == true {
//Cut signature from binary
bKM = bKM[:int(options.KeyManifest.KeyManifestSignatureOffset)]
}
if err = ioutil.WriteFile(g.KM, bKM, 0600); err != nil {
return fmt.Errorf("unable to write KM to file: %w", err)
}
return nil
}
func (g *generateBPMCmd) Run(ctx *context) error {
var options *cbnt.Options
if g.Config != "" {
cbnto, err := cbnt.ParseConfig(g.Config)
if err != nil {
return err
}
options = cbnto
} else {
var cbnto cbnt.Options
cbnto.BootPolicyManifest = bootpolicy.NewManifest()
cbnto.KeyManifest = key.NewManifest()
cbnto.BootPolicyManifest.BPMH.BPMRevision = g.Revision
cbnto.BootPolicyManifest.BPMH.BPMSVN = g.SVN
cbnto.BootPolicyManifest.BPMH.ACMSVNAuth = g.ACMSVN
cbnto.BootPolicyManifest.BPMH.NEMDataStack = g.NEMS
se := bootpolicy.NewSE()
se.PBETValue = g.PBET
se.Flags = g.IBBSegFlags
se.IBBMCHBAR = g.MCHBAR
se.VTdBAR = g.VDTBAR
se.DMAProtBase0 = g.DMABase0
se.DMAProtLimit0 = g.DMASize0
se.DMAProtBase1 = g.DMABase1
se.DMAProtLimit1 = g.DMASize1
se.IBBEntryPoint = g.EntryPoint
se.DigestList.List = make([]manifest.HashStructure, len(g.IbbHash))
se.DigestList.Size = uint16(len(g.IbbHash))
for iterator := range se.DigestList.List {
se.DigestList.List[iterator].HashAlg = g.IbbHash[iterator]
}
if g.IbbSegbase != 0 {
seg := *bootpolicy.NewIBBSegment()
seg.Base = g.IbbSegbase
seg.Size = g.IbbSegsize
seg.Flags = g.IbbSegFlag
se.IBBSegments = append(se.IBBSegments, seg)
}
if g.Coreboot {
ibbs, err := cbnt.FindAdditionalIBBs(g.BIOS)
if err != nil {
return err
}
se.IBBSegments = append(se.IBBSegments, ibbs...)
}
cbnto.BootPolicyManifest.SE = append(cbnto.BootPolicyManifest.SE, *se)
txt := bootpolicy.NewTXT()
txt.SInitMinSVNAuth = g.SinitMin
txt.ControlFlags = g.TXTFlags
txt.PwrDownInterval = g.PowerDownInterval
txt.ACPIBaseOffset = g.ACPIBaseOffset
txt.PwrMBaseOffset = g.PowermBaseOffset
txt.PTTCMOSOffset0 = g.CMOSOff0
txt.PTTCMOSOffset1 = g.CMOSOff1
cbnto.BootPolicyManifest.TXTE = txt
options = &cbnto
}
bpm, err := cbnt.GenerateBPM(options, g.BIOS)
if err != nil {
return err
}
// This section is hacky, just to make the parsing work
bpm.PMSE.Key.KeyAlg = 0x01
bpm.PMSE.Signature.HashAlg = 0x01
// End of hacky section
if g.Out != "" {
out, err := os.Create(g.Out)
if err != nil {
return err
}
if err := cbnt.WriteConfig(out, options); err != nil {
return err
}
}
bBPM, err := cbnt.WriteBPM(bpm)
if err != nil {
return err
}
if g.Cut {
bBPM = bBPM[:bpm.KeySignatureOffset]
}
if err = ioutil.WriteFile(g.BPM, bBPM, 0600); err != nil {
return fmt.Errorf("unable to write BPM to file: %w", err)
}
return nil
}
func (s *signKMCmd) Run(ctx *context) error {
encKey, err := ioutil.ReadFile(s.Key)
if err != nil {
return err
}
privkey, err := cbnt.DecryptPrivKey(encKey, s.Password)
if err != nil {
return err
}
kmRaw, err := ioutil.ReadFile(s.KmIn)
if err != nil {
return err
}
var km key.Manifest
r := bytes.NewReader(kmRaw)
_, err = km.ReadFrom(r)
if err != nil {
return err
}
km.RehashRecursive()
unsignedKM := kmRaw[:km.KeyAndSignatureOffset()]
if err = km.SetSignature(0, privkey.(crypto.Signer), unsignedKM); err != nil {
return err
}
bKMSigned, err := cbnt.WriteKM(&km)
if err != nil {
return err
}
if err := ioutil.WriteFile(s.KmOut, bKMSigned, 0600); err != nil {
return err
}
return nil
}
func (s *signBPMCmd) Run(ctx *context) error {
encKey, err := ioutil.ReadFile(s.Key)
if err != nil {
return err
}
key, err := cbnt.DecryptPrivKey(encKey, s.Password)
if err != nil {
return err
}
bpmRaw, err := ioutil.ReadFile(s.BpmIn)
if err != nil {
return err
}
var bpm bootpolicy.Manifest
r := bytes.NewReader(bpmRaw)
if _, err = bpm.ReadFrom(r); err != nil && !errors.Is(err, io.EOF) {
return err
}
kAs := bootpolicy.NewSignature()
switch key := key.(type) {
case *rsa.PrivateKey:
kAs.Key.SetPubKey(key.Public())
case *ecdsa.PrivateKey:
kAs.Key.SetPubKey(key.Public())
default:
return fmt.Errorf("Invalid key type")
}
bpm.PMSE = *kAs
bpmRaw, err = cbnt.WriteBPM(&bpm)
if err != nil {
return err
}
bpm.RehashRecursive()
unsignedBPM := bpmRaw[:bpm.KeySignatureOffset]
//err = bpm.PMSE.SetSignature(0, key.(crypto.Signer), unsignedBPM)
err = bpm.PMSE.Signature.SetSignature(0, key.(crypto.Signer), unsignedBPM)
if err != nil {
return fmt.Errorf("unable to make a signature: %w", err)
}
bBPMSigned, err := cbnt.WriteBPM(&bpm)
if err != nil {
return err
}
if err = ioutil.WriteFile(s.BpmOut, bBPMSigned, 0600); err != nil {
return fmt.Errorf("unable to write BPM to file: %w", err)
}
return nil
}
func (t *templateCmd) Run(ctx *context) error {
var cbnto cbnt.Options
cbnto.BootPolicyManifest = bootpolicy.NewManifest()
cbnto.KeyManifest = key.NewManifest()
cbnto.BootPolicyManifest.BPMH.BPMRevision = t.Revision
cbnto.BootPolicyManifest.BPMH.BPMSVN = t.SVN
cbnto.BootPolicyManifest.BPMH.ACMSVNAuth = t.ACMSVN
cbnto.BootPolicyManifest.BPMH.NEMDataStack = t.NEMS
se := bootpolicy.NewSE()
se.PBETValue = t.PBET
se.Flags = t.IBBSegFlags
se.IBBMCHBAR = t.MCHBAR
se.VTdBAR = t.VDTBAR
se.DMAProtBase0 = t.DMABase0
se.DMAProtLimit0 = t.DMASize0
se.DMAProtBase1 = t.DMABase1
se.DMAProtLimit1 = t.DMASize1
se.IBBEntryPoint = t.EntryPoint
seg := *bootpolicy.NewIBBSegment()
seg.Base = t.IbbSegbase
seg.Size = t.IbbSegsize
seg.Flags = t.IbbSegFlag
se.IBBSegments = append(se.IBBSegments, seg)
cbnto.BootPolicyManifest.SE = append(cbnto.BootPolicyManifest.SE, *se)
txt := bootpolicy.NewTXT()
txt.SInitMinSVNAuth = t.SintMin
txt.ControlFlags = t.TXTFlags
txt.PwrDownInterval = t.PowerDownInterval
txt.ACPIBaseOffset = t.ACPIBaseOffset
txt.PwrMBaseOffset = t.PowermBaseOffset
txt.PTTCMOSOffset0 = t.CMOSOff0
txt.PTTCMOSOffset1 = t.CMOSOff1
cbnto.BootPolicyManifest.TXTE = txt
out, err := os.Create(t.Path)
if err != nil {
return err
}
if err := cbnt.WriteConfig(out, &cbnto); err != nil {
return err
}
return nil
}
func (rc *readConfigCmd) Run(ctx *context) error {
f, err := os.Create(rc.Config)
if err != nil {
return err
}
_, err = cbnt.ReadConfigFromBIOSImage(rc.BIOS, f)
if err != nil {
return err
}
return nil
}
func (s *stitchingKMCmd) Run(ctx *context) error {
kmData, err := ioutil.ReadFile(s.KM)
if err != nil {
return err
}
sig, err := ioutil.ReadFile(s.Signature)
if err != nil {
return err
}
pub, err := cbnt.ReadPubKey(s.PubKey)
if err != nil {
return err
}
if len(kmData) < 1 || len(sig) < 1 {
return fmt.Errorf("loaded files are empty")
}
reader := bytes.NewReader(kmData)
km, err := cbnt.ParseKM(reader)
if err != nil {
return err
}
kmRaw, err := cbnt.StitchKM(km, pub, sig)
if err != nil {
return err
}
if err := ioutil.WriteFile(s.Out, kmRaw, 0644); err != nil {
return err
}
return nil
}
func (s *stitchingBPMCmd) Run(ctx *context) error {
bpmData, err := ioutil.ReadFile(s.BPM)
if err != nil {
return err
}
sig, err := ioutil.ReadFile(s.Signature)
if err != nil {
return err
}
pub, err := cbnt.ReadPubKey(s.PubKey)
if err != nil {
return err
}
if len(bpmData) < 1 || len(sig) < 1 {
return fmt.Errorf("loaded files are empty")
}
reader := bytes.NewReader(bpmData)
bpm, err := cbnt.ParseBPM(reader)
if err != nil {
return err
}
bpmRaw, err := cbnt.StitchBPM(bpm, pub, sig)
if err != nil {
return err
}
if err := ioutil.WriteFile(s.Out, bpmRaw, 0644); err != nil {
return err
}
return nil
}
func (s *stitchingCmd) Run(ctx *context) error {
var err error
var bpm, km, acm, me []byte
if s.BPM != "" {
if bpm, err = ioutil.ReadFile(s.BPM); err != nil {
return err
}
}
if s.KM != "" {
if km, err = ioutil.ReadFile(s.KM); err != nil {
return err
}
}
if s.ACM != "" {
if acm, err = ioutil.ReadFile(s.ACM); err != nil {
return err
}
}
if s.ME != "" {
if me, err = ioutil.ReadFile(s.ME); err != nil {
return err
}
}
if len(acm) == 0 && len(km) == 0 && len(bpm) == 0 && len(me) == 0 {
return fmt.Errorf("at least one optional parameter required")
}
if err := cbnt.StitchFITEntries(s.BIOS, acm, bpm, km); err != nil {
return err
}
if len(me) != 0 {
image, err := ioutil.ReadFile(s.BIOS)
if err != nil {
return err
}
meRegionOffset, meRegionSize, err := tools.GetRegion(image, uefi.RegionTypeME)
if len(me) > int(meRegionSize) {
return fmt.Errorf("ME size exceeds region size! (%d > %d)", len(me), meRegionSize)
}
file, err := os.OpenFile(s.BIOS, os.O_RDWR, 0600)
if err != nil {
return err
}
defer file.Close()
size, err := file.WriteAt(me, int64(meRegionOffset))
if err != nil {
return err
}
if size != len(me) {
return fmt.Errorf("couldn't write new ME")
}
}
return nil
}
func (k *keygenCmd) Run(ctx *context) error {
kmPubFile, err := os.Create(k.Path + "km_pub.pem")
if err != nil {
return err
}
kmPrivFile, err := os.Create(k.Path + "km_priv.pem")
if err != nil {
return err
}
bpmPubFile, err := os.Create(k.Path + "bpm_pub.pem")
if err != nil {
return err
}
bpmPrivFile, err := os.Create(k.Path + "bpm_priv.pem")
if err != nil {
return err
}
switch k.Algo {
case "RSA2048":
err := cbnt.GenRSAKey(2048, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
case "RSA3072":
err := cbnt.GenRSAKey(3072, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
case "ECC224":
err := cbnt.GenECCKey(224, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
case "ECC256":
err := cbnt.GenECCKey(256, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
default:
return fmt.Errorf("Chosen algorithm invlid. Options are: RSA2048, RSA3072, ECC224, ECC256")
}
return nil
}
var cli struct {
Debug bool `help:"Enable debug mode."`
ManifestStrictOrderCheck bool `help:"Enable checking of manifest elements order"`
KMShow kmPrintCmd `cmd help:"Prints Key Manifest binary in human-readable format"`
KMGen generateKMCmd `cmd help:"Generate KM file based von json configuration"`
KMSign signKMCmd `cmd help:"Sign key manifest with given key"`
KMStitch stitchingKMCmd `cmd help:"Stitches KM Signatue into unsigned KM"`
KMExport kmExportCmd `cmd help:"Exports KM structures from BIOS image into file"`
BPMShow bpmPrintCmd `cmd help:"Prints Boot Policy Manifest binary in human-readable format"`
BPMGen generateBPMCmd `cmd help:"Generate BPM file based von json configuration"`
BPMSign signBPMCmd `cmd help:"Sign Boot Policy Manifest with given key"`
BPMStitch stitchingBPMCmd `cmd help:"Stitches BPM Signatue into unsigned BPM"`
BPMExport bpmExportCmd `cmd help:"Exports BPM structures from BIOS image into file"`
ACMExport acmExportCmd `cmd help:"Exports ACM structures from BIOS image into file"`
ACMShow acmPrintCmd `cmd help:"Prints ACM binary in human-readable format"`
ShowAll biosPrintCmd `cmd help:"Prints BPM, KM, FIT and ACM from BIOS binary in human-readable format"`
Stitch stitchingCmd `cmd help:"Stitches BPM, KM and ACM into given BIOS image file"`
KeyGen keygenCmd `cmd help:"Generates key for KM and BPM signing"`
Template templateCmd `cmd help:"Writes template JSON configuration into file"`
ReadConfig readConfigCmd `cmd help:"Reads config from existing BIOS file and translates it to a JSON configuration"`
Version versionCmd `cmd help:"Prints the version of the program"`
}