diff --git a/.circleci/config.yml b/.circleci/config.yml index 5457daa2..4735d6a2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,12 +16,12 @@ jobs: - run: if [ "$(gofmt -l .)" != "" ]; then exit 1; fi - run: golint -set_exit_status ./cmd/txt-suite - run: golint -set_exit_status ./cmd/txt-prov - - run: golint -set_exit_status ./cmd/cbnt-prov + - run: golint -set_exit_status ./cmd/bg-prov - run: go mod download - run: go mod verify - run: CGO_ENABLED=0 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-suite cmd/txt-suite/*.go - run: CGO_ENABLED=0 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-prov cmd/txt-prov/*.go - - run: CGO_ENABLED=0 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o cbnt-prov cmd/cbnt-prov/*.go + - run: CGO_ENABLED=0 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o bg-prov cmd/bg-prov/*.go - run: go test ./pkg/check/ - run: apt install -y libtspi-dev && go test ./pkg/diff/ - run: go test ./pkg/errors/ @@ -31,7 +31,7 @@ jobs: - run: go test ./pkg/pcd/ - run: go test ./pkg/pcr/ - run: go test ./pkg/provisioning/txt - - run: go test ./pkg/provisioning/cbnt + - run: go test ./pkg/provisioning/bootguard - run: go test ./pkg/registers - run: go test ./pkg/test/ - run: go test ./pkg/tools/ @@ -44,14 +44,14 @@ jobs: - run: ./txt-suite markdown > ./cmd/txt-suite/TESTPLAN.md - run: git add ./cmd/txt-suite/TESTPLAN.md - run: (git commit -m "Update testplan file" && git push --set-upstream origin ${CIRCLE_BRANCH}) || true - - run: cp txt-suite txt-prov cbnt-prov out/ + - run: cp txt-suite txt-prov bg-prov out/ - persist_to_workspace: root: out paths: - txt-suite - txt-prov - - cbnt-prov + - bg-prov create_deb_rpm: docker: @@ -67,7 +67,7 @@ jobs: - run: go build github.com/goreleaser/nfpm/cmd/nfpm - run: cp /tmp/out/txt-suite . - run: cp /tmp/out/txt-prov . - - run: cp /tmp/out/cbnt-prov . + - run: cp /tmp/out/bg-prov . - run: if [ -z "$CIRCLE_TAG" ]; then echo "export CIRCLE_TAG=$(git describe --tags|cut -d'-' -f1);" >> $BASH_ENV; fi - run: if [ -z "$CIRCLE_BUILD_NUM" ]; then echo "export CIRCLE_BUILD_NUM=$(git describe --tags|cut -d'-' -f2);" >> $BASH_ENV; fi - run: MY_APP_VERSION=${CIRCLE_TAG} MY_APP_BUILDNUMBER=${CIRCLE_BUILD_NUM} go run github.com/goreleaser/nfpm/cmd/nfpm pkg --config ./build/package/nfpm_rpm.yaml --target golang-css-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}.x86_64.rpm @@ -75,7 +75,7 @@ jobs: - run: mkdir -p out && cp *.rpm ./out/ && cp *.deb ./out/ - run: cp txt-suite ./out/ - run: cp txt-prov ./out/ - - run: cp cbnt-prov ./out/ + - run: cp bg-prov ./out/ - run: cp golang-css-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}.x86_64.rpm artifact.rpm - run: cp go_css-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}_amd64.deb artifact.deb - persist_to_workspace: @@ -85,7 +85,7 @@ jobs: - go_css-suite*.deb - txt-suite - txt-prov - - cbnt-prov + - bg-prov - store_artifacts: path: artifact.rpm destination: golang-css-suite.rpm @@ -105,7 +105,7 @@ jobs: - run: go mod verify - run: CGO_ENABLED=0 GOARCH=arm go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-suite cmd/txt-suite/*.go - run: CGO_ENABLED=0 GOARCH=arm go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-prov cmd/txt-prov/*.go - - run: CGO_ENABLED=0 GOARCH=arm go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o cbnt-prov cmd/cbnt-prov/*.go + - run: CGO_ENABLED=0 GOARCH=arm go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o bg-prov cmd/bg-prov/*.go build_arm64: docker: @@ -119,7 +119,7 @@ jobs: - run: go mod verify - run: CGO_ENABLED=0 GOARCH=arm64 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-suite cmd/txt-suite/*.go - run: CGO_ENABLED=0 GOARCH=arm64 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-prov cmd/txt-prov/*.go - - run: CGO_ENABLED=0 GOARCH=arm64 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o cbnt-prov cmd/cbnt-prov/*.go + - run: CGO_ENABLED=0 GOARCH=arm64 go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o bg-prov cmd/bg-prov/*.go build_ppc64le: docker: @@ -133,7 +133,7 @@ jobs: - run: go mod verify - run: CGO_ENABLED=0 GOARCH=ppc64le go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-suite cmd/txt-suite/*.go - run: CGO_ENABLED=0 GOARCH=ppc64le go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o txt-prov cmd/txt-prov/*.go - - run: CGO_ENABLED=0 GOARCH=ppc64le go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o cbnt-prov cmd/cbnt-prov/*.go + - run: CGO_ENABLED=0 GOARCH=ppc64le go build -ldflags '-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG} -w -extldflags "-static"' -o bg-prov cmd/bg-prov/*.go publish-github-release: diff --git a/.gitignore b/.gitignore index 56feb459..2900a781 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ /txt-suite /txt-prov -/cbnt-prov +/bg-prov /vendor cmd/txt-prov/txt-prov cmd/txt-suite/test_log.json cmd/txt-suite/txt-suite -cmd/cbnt-prov/cbnt-prov +cmd bg-prov/bg-prov diff --git a/README.md b/README.md index 64efb929..03ba6cab 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ The Converged Security Suite implements all necessary tools for Intel platform s | Technology | Testsuite | Provisioning | | --- | --- | --- | -| Intel Trusted Execution Technology Legacy | Supported | Supported | -| Intel Trusted Execution Technology CBnT | WIP | Supported | -| Intel Boot Guard | WIP | WIP | -| Intel Platform Firmware Resilience | On Hold | Partly Supported | +| Intel Trusted Execution Technology Legacy/CBnT | Supported | Supported | +| Intel Boot Guard 1.0 | WIP | Supported | +| Intel Boot Guard 2.0 | WIP | Supported | +| Intel Platform Firmware Resilience | N/A | Partly Supported | Build Status ------------ @@ -19,7 +19,7 @@ Tooling & API * [Intel TXT Test Suite](cmd/txt-suite) - Test Suite for Intel Trusted Execution Technology validation. * [Intel TXT Provisioning](cmd/txt-prov) - Provisioning of Trusted Platform Module for Intel Trusted Execution Technology usage. -* [Intel CBnT Provisioning](cmd/cbnt-prov) - Provisioning of Converged BootGuard and Trusted Execution Technology (CBnT) usage. +* [Intel CBnT Provisioning](cmd/bg-prov) - Provisioning of all BootGuard versions and Trusted Execution Technology (CBnT) usage. * [Intel/AMD pcr0tool](cmd/pcr0tool) - [PCR0](https://security.stackexchange.com/questions/127224/what-does-crtm-refer-to) diagnostics tool. Developer notes diff --git a/cmd/bg-prov/README.md b/cmd/bg-prov/README.md new file mode 100644 index 00000000..349bd4a5 --- /dev/null +++ b/cmd/bg-prov/README.md @@ -0,0 +1,177 @@ +Intel CBnT Provisioning +=============================== + +This Golang utility supports the artifact generation to support Intel Converged BootGuard and Trustes Execution Technology (CBnT) + +Prerequisites for Usage +----------------------- +Supported OS: Any Linux distribution + +How to compile +----------------------- + +Get Golang >= 1.11 and export: +``` +export GO111MODULE=on +``` +or set it in front of every command. +This environment variable actives moduled for GO 1.11 + +To download all dependencies run: +``` + go mod download +``` + +Verify all downloaded dependencies run: +``` + go mod verify +``` + +To build the test suite run: + +``` + go build -o bg-prov cmd/bg-prov/*.go +``` + +Commandline subcommands: +-------------- +```bash +Usage: bg-prov + +Intel BtG/CBnT provisioning tooling + +Flags: + -h, --help Show context-sensitive help. + --debug Enable debug mode. + --manifest-strict-order-check Enable checking of manifest elements order + +Commands: + km-show Prints Key Manifest binary in human-readable format + km-gen-v-1 Generate v1 KM file based von json configuration + km-gen-v-2 Generate v2 KM file based von json configuration + km-sign Sign key manifest with given key + km-verify Verify the signature of a given KM + km-stitch Stitches KM Signatue into unsigned KM + km-export Exports KM structures from BIOS image into file + bpm-show Prints Boot Policy Manifest binary in human-readable format + bpm-gen-v-1 Generate v1 BPM file based von json configuration + bpm-gen-v-2 Generate v2 BPM file based von json configuration + bpm-sign Sign Boot Policy Manifest with given key + bpm-verify Verify the signature of a given KM + bpm-stitch Stitches BPM Signatue into unsigned BPM + bpm-export Exports BPM structures from BIOS image into file + acm-gen-v-0 Generate an ACM v0 module (usable only for unit-tests) + acm-gen-v-3 Generate an ACM v3 module (usable only for unit-tests) + acm-export Exports ACM structures from BIOS image into file + acm-show Prints ACM binary in human-readable format + fit-show Prints the FIT Table of given BIOS image file + show-all Prints BPM, KM, FIT and ACM from BIOS binary in human-readable format + stitch Stitches BPM, KM and ACM into given BIOS image file + key-gen Generates key for KM and BPM signing + template-v-1 Writes template v1 JSON configuration into file + template-v-2 Writes template v2 JSON configuration into file + read-config Reads config from existing BIOS file and translates it to a JSON configuration + version Prints the version of the program + +Run "bg-prov --help" for more information on a command. + +bg-prov: error: expected one of "km-show", "km-gen-v-1", "km-gen-v-2", "km-sign", "km-verify", ... +``` + +Workflows +========== + +I. Boot Policy / Key Manifest Generation/Signing/Stitching +------------------------------- + +1. Create a template config file +```bash +./bg-prov template ./config.json +``` + +2. Create keys for signing of Key Manifest (KM) and Boot Policy Manifest (BPM) +Algorithm: RSA, BitSize: 2048, no password for enryption of private key files +```bash +./bg-prov key-gen RSA2048 "" --path=./Keys/mykey +``` + +3. Generate Key Manifest (KM) +```bash +./bg-prov km-gen-v-2 ./KM/km_unsigned.bin ./Keys/mykey_km_pub.pem \ + --config=./config.json \ + --pkhashalg=12 \ + --bpmpubkey=./Keys/mykey_bpmpub.pem \ + --bpmhashalgo=12 +``` + +4. Generation of Boot Policy Manifest (BPM) +```bash +./bg-prov bpm-gen-v-2 ./BPM/bpm_unsigned.bin ./firmware.rom --config=./config.json +``` + +5. Sign Key Manifest (KM) +```bash +./bg-prov km-sign ./KM/km_unsigned.bin ./KM/km_signed.bin ./Keys/myKey_km_priv.pem "" +``` + +6. Sign Boot Policy Manifest (BPM) +```bash +./bg-prov bpm-sign ./BPM/bpm_unsigned.bin ./BPM/bpm_signed.bin ./Keys/myKey_bpm_priv.pem "" + +``` + +7. Export ACM for stitching (Firmware image must contain an ACM) +Skip this if you already have an ACM for stitching +```bash +./bg-prov export-acm ./firmware.rom ./ACM/acm_export.bin +``` + +8. Stitch BPM, KM and ACM into firmware image +```bash +./bg-prov stitch ./firmware.rom ./ACM/acm.bin ./KM/km_signed.bin ./BPM/bpm_signed.bin +``` + +II. Read config from a CBnT enabled firmware image +------------------------------------------- +```bash +./bg-prov read-config ./config.json ./firmware.rom +``` + +III Export KM, BPM and ACM from CBnT enabled firmware image +------------------------------------------------ +1. Export of KM +```bash +./bg-prov export-km ./firmware.rom ./KM/km_export.bin +``` + +2. Export BPM +```bash +./bg-prov export-km ./firmware.rom ./BPM/bpm_export.bin +``` + +3. Export ACM +```bash +./bg-prov export-acm ./firmware.rom ./ACM/acm_export.bin +``` + +IV. Show details of exported KM, BPM, ACM +-------------------------------------- +1. Show details of KM +```bash +./bg-prov show-km ./KM/km_signed.bin +``` + +2. Show details of BPM +```bash +./bg-prov show-bpm ./BPM/bpm_signed.bin +``` + +3. Show details of ACM +```bash +./bg-prov show-acm ./ACM/acm_signed.bin +``` + +4. Show all +```bash +./bg-prov show-all ./firmware.rom +``` diff --git a/cmd/bg-prov/cmd.go b/cmd/bg-prov/cmd.go new file mode 100644 index 00000000..a63391dc --- /dev/null +++ b/cmd/bg-prov/cmd.go @@ -0,0 +1,1201 @@ +package main + +import ( + "bytes" + "encoding/binary" + "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" + "github.com/linuxboot/fiano/pkg/intel/metadata/fit" + + "github.com/linuxboot/fiano/pkg/uefi" + + "github.com/9elements/converged-security-suite/v2/pkg/provisioning/bootguard" + "github.com/9elements/converged-security-suite/v2/pkg/tools" +) + +type context struct { + Debug bool +} + +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 + 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"` + // 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"` +} + +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"` + // 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"` + // 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"` +} + +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 generateACMCmdv3 struct { + ACMOut string `arg required name:"acm" help:"Path to the newly generated ACM headers binary file." type:"path"` + ConfigIn string `flag optional name:"config" help:"Path to the JSON config file." type:"path"` + ConfigOut string `flag optional name:"out" help:"Path to write applied config to" type:"path"` + BodyPath string `flag optional name:"bodypath" help:"Path to the ACM body" type:"path"` + RSAPrivateKeyPEM string `flag optional name:"rsaprivkeypem" help:"RSA key used to sign the ACM" type:"path"` + + ModuleType fit.ACModuleType `flag optional name:"moduletype"` + ModuleSubType fit.ACModuleSubType `flag optional name:"modulesubtype"` + ChipsetID fit.ACChipsetID `flag optional name:"chipsetid"` + Flags fit.ACFlags `flag optional name:"flags"` + ModuleVendor fit.ACModuleVendor `flag optional name:"modulevendor"` + Date fit.BCDDate `flag optional name:"date"` + Size uint64 `flag optional name:"size"` + TXTSVN fit.TXTSVN `flag optional name:"txtsvn"` + SESVN fit.SESVN `flag optional name:"sesvn"` + CodeControl fit.CodeControl `flag optional name:"codecontrol"` + ErrorEntryPoint fit.ErrorEntryPoint `flag optional name:"errorentrypoint"` + GDTLimit fit.GDTLimit `flag optional name:"gdtlimit"` + GDTBasePtr fit.GDTBasePtr `flag optional name:"gdtbaseptr"` + SegSel fit.SegSel `flag optional name:"segsel"` + EntryPoint fit.EntryPoint `flag optional name:"entrypoint"` +} + +type generateACMCmdv0 struct { + ACMOut string `arg required name:"acm" help:"Path to the newly generated ACM headers binary file." type:"path"` + ConfigIn string `flag optional name:"config" help:"Path to the JSON config file." type:"path"` + ConfigOut string `flag optional name:"out" help:"Path to write applied config to" type:"path"` + BodyPath string `flag optional name:"bodypath" help:"Path to the ACM body" type:"path"` + RSAPrivateKeyPEM string `flag optional name:"rsaprivkeypem" help:"RSA key used to sign the ACM" type:"path"` + + ModuleType fit.ACModuleType `flag optional name:"moduletype"` + ModuleSubType fit.ACModuleSubType `flag optional name:"modulesubtype"` + ChipsetID fit.ACChipsetID `flag optional name:"chipsetid"` + Flags fit.ACFlags `flag optional name:"flags"` + ModuleVendor fit.ACModuleVendor `flag optional name:"modulevendor"` + Date fit.BCDDate `flag optional name:"date"` + Size uint64 `flag optional name:"size"` + TXTSVN fit.TXTSVN `flag optional name:"txtsvn"` + SESVN fit.SESVN `flag optional name:"sesvn"` + CodeControl fit.CodeControl `flag optional name:"codecontrol"` + ErrorEntryPoint fit.ErrorEntryPoint `flag optional name:"errorentrypoint"` + GDTLimit fit.GDTLimit `flag optional name:"gdtlimit"` + GDTBasePtr fit.GDTBasePtr `flag optional name:"gdtbaseptr"` + SegSel fit.SegSel `flag optional name:"segsel"` + EntryPoint fit.EntryPoint `flag optional name:"entrypoint"` +} + +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"` + //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"` + 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"` +} + +type generateBPMCmdv1 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 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"` + // 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"` + + 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 generateBPMCmdv2 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 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"` + // 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"` + // 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"` + + 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"` + SignAlgo string `arg required name:"signalgo" help:"Signing algorithm for KM. E.g.: RSASSA, RSAPSS, SM2"` + 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"` + SignAlgo string `arg required name:"signalgo" help:"Signing algorithm for KM. E.g.: RSASSA, RSAPSS, SM2"` + 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 `arg required name:"acm" help:"Path to the ACM binary file." type:"path"` + KM string `arg required name:"km" help:"Path to the Key Manifest binary file." type:"path"` + BPM string `arg required 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"` +} + +type printFITCmd struct { + BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"` +} + +type verifyKMSigCmd struct { + KM string `arg required name:"km" help:"Path to the Key Manifest binary file." type:"path"` +} +type verifyBPMSigCmd struct { + BPM string `arg required name:"bpm" help:"Path to the Boot Policy Manifest binary file." type:"path"` +} + +func (v *versionCmd) Run(ctx *context) error { + tools.ShowVersion(programName, gittag, gitcommit) + return nil +} + +func (kmp *kmPrintCmd) Run(ctx *context) error { + file, err := os.Open(kmp.Path) + if err != nil { + return err + } + defer file.Close() + bg, err := bootguard.NewKM(file) + if err != nil { + return err + + } + bg.PrintKM() + return nil +} + +func (bpmp *bpmPrintCmd) Run(ctx *context) error { + file, err := os.Open(bpmp.Path) + if err != nil { + return err + } + defer file.Close() + bg, err := bootguard.NewBPM(file) + if err != nil { + return err + } + bg.PrintBPM() + return nil +} + +func (acmp *acmPrintCmd) Run(ctx *context) error { + file, err := os.Open(acmp.Path) + if err != nil { + return err + } + defer file.Close() + acm, err := tools.ParseACM(file) + if err != nil { + return err + } + acm.PrettyPrint() + return nil +} + +func (biosp *biosPrintCmd) Run(ctx *context) error { + data, err := os.ReadFile(biosp.Path) + if err != nil { + return err + } + table, err := fit.GetTable(data) + if err != nil { + return err + } + fmt.Printf("%s", table.String()) + err = bootguard.PrintStructures(data) + if err != nil { + return err + } + return nil +} + +func (acme *acmExportCmd) Run(ctx *context) error { + data, err := os.ReadFile(acme.BIOS) + if err != nil { + return err + } + acmfile, err := os.Create(acme.Out) + if err != nil { + return err + } + err = bootguard.WriteCBnTStructures(data, nil, nil, acmfile) + if err != nil { + return err + } + return nil +} + +func (kme *kmExportCmd) Run(ctx *context) error { + data, err := os.ReadFile(kme.BIOS) + if err != nil { + return err + } + kmfile, err := os.Create(kme.Out) + if err != nil { + return err + } + err = bootguard.WriteCBnTStructures(data, nil, kmfile, nil) + if err != nil { + return err + } + return nil +} + +func (bpme *bpmExportCmd) Run(ctx *context) error { + data, err := os.ReadFile(bpme.BIOS) + if err != nil { + return err + } + bpmfile, err := os.Create(bpme.Out) + if err != nil { + return err + } + err = bootguard.WriteCBnTStructures(data, bpmfile, nil, nil) + if err != nil { + return err + } + return nil +} + +func (g *generateKMCmdv1) Run(ctx *context) error { + var b bootguard.BootGuard + b.Version = bgheader.Version10 + if g.Config != "" { + err := b.ReadJSON(g.Config) + if err != nil { + return err + } + } else { + var err error + b.VData.BGkm = bgkey.NewManifest() + b.VData.BGkm.KMSVN = g.SVN + b.VData.BGkm.KMID = g.ID + if err != nil { + return err + } + //b.VData.BGkm.BPKey = g.KMHash + // Create KM_Hash for BPM pub signing key + if g.BpmPubkey != "" { + err := b.GetBPMPubHash(g.BpmPubkey, g.BpmHashAlg) + if err != nil { + return err + } + } else { + return fmt.Errorf("add --bpmpubkey= as argument") + } + } + key, err := bootguard.ReadPubKey(g.Key) + if err != nil { + return err + } + if err := b.VData.BGkm.KeyAndSignature.Key.SetPubKey(key); err != nil { + return err + } + if g.PrintME { + if b.VData.BGkm.KeyAndSignature.Signature.DataTotalSize() > 1 { + if err := b.VData.BGkm.KeyAndSignature.Key.PrintKMPubKey(b.VData.BGkm.BPKey.HashAlg); err != nil { + return err + } + } + } + bKM, err := b.WriteKM() + if err != nil { + return err + } + if g.Out != "" { + out, err := os.Create(g.Out) + if err != nil { + return err + } + if err := b.WriteJSON(out); err != nil { + return err + } + } + if g.Cut { + //Cut signature from binary + bKM = bKM[:int(b.VData.BGkm.KeyAndSignatureOffset())] + } + if err = os.WriteFile(g.KM, bKM, 0600); err != nil { + return fmt.Errorf("unable to write KM to file: %w", err) + } + return nil +} + +func (g *generateKMCmdv2) Run(ctx *context) error { + var b bootguard.BootGuard + b.Version = bgheader.Version20 + if g.Config != "" { + err := b.ReadJSON(g.Config) + if err != nil { + return err + } + } else { + var err error + b.VData.CBNTkm = cbntkey.NewManifest() + b.VData.CBNTkm.Revision = g.Revision + b.VData.CBNTkm.KMSVN = g.SVN + b.VData.CBNTkm.KMID = g.ID + b.VData.CBNTkm.PubKeyHashAlg, err = cbnt.GetAlgFromString(g.PKHashAlg) + if err != nil { + return err + } + b.VData.CBNTkm.Hash = g.KMHashes + // Create KM_Hash for BPM pub signing key + if g.BpmPubkey != "" { + err := b.GetBPMPubHash(g.BpmPubkey, g.BpmHashAlg) + if err != nil { + return err + } + } else { + return fmt.Errorf("add --bpmpubkey= as argument") + } + } + key, err := bootguard.ReadPubKey(g.Key) + if err != nil { + return err + } + if err := b.VData.CBNTkm.KeyAndSignature.Key.SetPubKey(key); err != nil { + return err + } + if g.PrintME { + if b.VData.CBNTkm.KeyAndSignature.Signature.DataTotalSize() > 1 { + if err := b.VData.CBNTkm.KeyAndSignature.Key.PrintKMPubKey(b.VData.CBNTkm.PubKeyHashAlg); err != nil { + return err + } + } + } + bKM, err := b.WriteKM() + if err != nil { + return err + } + if g.Out != "" { + out, err := os.Create(g.Out) + if err != nil { + return err + } + if err := b.WriteJSON(out); err != nil { + return err + } + } + if g.Cut { + //Cut signature from binary + bKM = bKM[:int(b.VData.CBNTkm.KeyManifestSignatureOffset)] + } + if err = os.WriteFile(g.KM, bKM, 0600); err != nil { + return fmt.Errorf("unable to write KM to file: %w", err) + } + return nil +} + +func (g *generateBPMCmdv2) Run(ctx *context) error { + var b bootguard.BootGuard + if g.Config != "" { + err := b.ReadJSON(g.Config) + if err != nil { + return err + } + } else { + b.VData.CBNTbpm = cbntbootpolicy.NewManifest() + b.VData.CBNTkm = cbntkey.NewManifest() + + se := cbntbootpolicy.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([]cbnt.HashStructure, len(g.IbbHash)) + se.DigestList.Size = uint16(len(g.IbbHash)) + + ibbhashalgs := make([]cbnt.Algorithm, 0) + for _, item := range g.IbbHash { + hash, err := cbnt.GetAlgFromString(item) + if err != nil { + return err + } + ibbhashalgs = append(ibbhashalgs, hash) + } + + for iterator := range se.DigestList.List { + se.DigestList.List[iterator].HashAlg = ibbhashalgs[iterator] + } + + ibbs, err := bootguard.FindAdditionalIBBs(g.BIOS) + if err != nil { + return fmt.Errorf("FindAdditionalIBBs: %w", err) + } + for counter := range ibbs { + ibbs[counter].Flags = g.IbbSegFlag + } + se.IBBSegments = append(se.IBBSegments, ibbs...) + + b.VData.CBNTbpm.SE = append(b.VData.CBNTbpm.SE, *se) + + txt := cbntbootpolicy.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 + + b.VData.CBNTbpm.TXTE = txt + + b.VData.CBNTbpm.PCDE = cbntbootpolicy.NewPCD() + b.VData.CBNTbpm.PME = cbntbootpolicy.NewPM() + b.VData.CBNTbpm.PMSE = *cbntbootpolicy.NewSignature() + 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 + } + bpm, err := b.GenerateBPMFromImage(g.BIOS) + if err != nil { + return fmt.Errorf("GenerateBPM: %w", err) + } + if g.Out != "" { + out, err := os.Create(g.Out) + if err != nil { + return err + } + if err := b.WriteJSON(out); err != nil { + return err + } + } + bBPM, err := b.WriteBPM() + if err != nil { + return err + } + if g.Cut { + bBPM = bBPM[:bpm.VData.CBNTbpm.KeySignatureOffset] + } + if err = os.WriteFile(g.BPM, bBPM, 0600); err != nil { + return fmt.Errorf("unable to write BPM to file: %w", err) + } + return nil +} + +func (g *generateACMCmdv0) Run(ctx *context) error { + var sACM fit.EntrySACM + var sACMData *fit.EntrySACMData + if g.ConfigIn != "" { + data, err := os.ReadFile(g.ConfigIn) + if err != nil { + return err + } + if err := sACM.UnmarshalJSON(data); err != nil { + return err + } + sACMData, err = sACM.ParseData() + if err != nil { + return err + } + } else { + var acmHeaders fit.EntrySACMData0 + acmHeaders.HeaderVersion = fit.ACHeaderVersion0 + acmHeaders.HeaderLen.SetSize(uint64(binary.Size(acmHeaders))) + acmHeaders.ModuleType = g.ModuleType + acmHeaders.ModuleSubType = g.ModuleSubType + acmHeaders.ChipsetID = g.ChipsetID + acmHeaders.Flags = g.Flags + acmHeaders.ModuleVendor = g.ModuleVendor + acmHeaders.Date = g.Date + acmHeaders.Size.SetSize(g.Size) + acmHeaders.TXTSVN = g.TXTSVN + acmHeaders.SESVN = g.SESVN + acmHeaders.CodeControl = g.CodeControl + acmHeaders.ErrorEntryPoint = g.ErrorEntryPoint + acmHeaders.GDTLimit = g.GDTLimit + acmHeaders.GDTBasePtr = g.GDTBasePtr + acmHeaders.SegSel = g.SegSel + acmHeaders.EntryPoint = g.EntryPoint + acmHeaders.KeySize.SetSize(256) + sACMData.EntrySACMDataInterface = &acmHeaders + } + if g.ConfigOut != "" { + buf := new(bytes.Buffer) + _, err := sACMData.Write(buf.Bytes()) + if err != nil { + return err + } + json, err := sACM.MarshalJSON() + if err != nil { + return err + } + if err := os.WriteFile(g.ConfigOut, json, 0700); err != nil { + return err + } + } + if g.BodyPath != "" { + bodyData, err := os.ReadFile(g.BodyPath) + if err != nil { + return fmt.Errorf("unable to read the ACM body file '%s': %w", g.BodyPath, err) + } + + sACMData.UserArea = bodyData + } + + if g.RSAPrivateKeyPEM != "" { + return fmt.Errorf("signing is not implemented, yet") + } + + var acmBytes bytes.Buffer + if _, err := sACMData.WriteTo(&acmBytes); err != nil { + return fmt.Errorf("unable to compile the ACM module: %w", err) + } + + if err := os.WriteFile(g.ACMOut, acmBytes.Bytes(), 0600); err != nil { + return fmt.Errorf("unable to write KM to file: %w", err) + } + return nil +} + +func (g *generateACMCmdv3) Run(ctx *context) error { + var sACM fit.EntrySACM + var sACMData *fit.EntrySACMData + if g.ConfigIn != "" { + data, err := os.ReadFile(g.ConfigIn) + if err != nil { + return err + } + if err := sACM.UnmarshalJSON(data); err != nil { + return err + } + sACMData, err = sACM.ParseData() + if err != nil { + return err + } + } else { + var acmHeaders fit.EntrySACMData3 + acmHeaders.HeaderVersion = fit.ACHeaderVersion3 + acmHeaders.HeaderLen.SetSize(uint64(binary.Size(acmHeaders))) + acmHeaders.ModuleType = g.ModuleType + acmHeaders.ModuleSubType = g.ModuleSubType + acmHeaders.ChipsetID = g.ChipsetID + acmHeaders.Flags = g.Flags + acmHeaders.ModuleVendor = g.ModuleVendor + acmHeaders.Date = g.Date + acmHeaders.Size.SetSize(g.Size) + acmHeaders.TXTSVN = g.TXTSVN + acmHeaders.SESVN = g.SESVN + acmHeaders.CodeControl = g.CodeControl + acmHeaders.ErrorEntryPoint = g.ErrorEntryPoint + acmHeaders.GDTLimit = g.GDTLimit + acmHeaders.GDTBasePtr = g.GDTBasePtr + acmHeaders.SegSel = g.SegSel + acmHeaders.EntryPoint = g.EntryPoint + acmHeaders.KeySize.SetSize(384) + sACMData.EntrySACMDataInterface = &acmHeaders + } + if g.ConfigOut != "" { + buf := new(bytes.Buffer) + _, err := sACMData.Write(buf.Bytes()) + if err != nil { + return err + } + json, err := sACM.MarshalJSON() + if err != nil { + return err + } + if err := os.WriteFile(g.ConfigOut, json, 0700); err != nil { + return err + } + } + if g.BodyPath != "" { + bodyData, err := os.ReadFile(g.BodyPath) + if err != nil { + return fmt.Errorf("unable to read the ACM body file '%s': %w", g.BodyPath, err) + } + + sACMData.UserArea = bodyData + } + + if g.RSAPrivateKeyPEM != "" { + return fmt.Errorf("signing is not implemented, yet") + } + + var acmBytes bytes.Buffer + if _, err := sACMData.WriteTo(&acmBytes); err != nil { + return fmt.Errorf("unable to compile the ACM module: %w", err) + } + + if err := os.WriteFile(g.ACMOut, acmBytes.Bytes(), 0600); err != nil { + return fmt.Errorf("unable to write KM to file: %w", err) + } + return nil +} + +func (s *signKMCmd) Run(ctx *context) error { + encKey, err := os.ReadFile(s.Key) + if err != nil { + return err + } + privkey, err := bootguard.DecryptPrivKey(encKey, s.Password) + if err != nil { + return err + } + file, err := os.Open(s.KmIn) + if err != nil { + return err + } + defer file.Close() + bg, err := bootguard.NewKM(file) + if err != nil { + return err + } + bKMSigned, err := bg.SignKM(s.SignAlgo, privkey) + if err != nil { + return err + } + if err := os.WriteFile(s.KmOut, bKMSigned, 0600); err != nil { + return err + } + return nil +} + +func (s *signBPMCmd) Run(ctx *context) error { + encKey, err := os.ReadFile(s.Key) + if err != nil { + return err + } + key, err := bootguard.DecryptPrivKey(encKey, s.Password) + if err != nil { + return err + } + file, err := os.Open(s.BpmIn) + if err != nil { + return err + } + defer file.Close() + bg, err := bootguard.NewBPM(file) + if err != nil { + return err + } + bBPMSigned, err := bg.SignBPM(s.SignAlgo, key) + if err != nil { + return err + } + if err = os.WriteFile(s.BpmOut, bBPMSigned, 0600); err != nil { + return fmt.Errorf("unable to write BPM to file: %w", err) + } + return nil +} + +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 + + se := cbntbootpolicy.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 + se.DigestList.List = make([]cbnt.HashStructure, len(t.IbbHash)) + + ibbhashalgs := make([]cbnt.Algorithm, 0) + for _, item := range t.IbbHash { + hash, err := cbnt.GetAlgFromString(item) + if err != nil { + return err + } + ibbhashalgs = append(ibbhashalgs, hash) + } + + for iterator := range se.DigestList.List { + se.DigestList.List[iterator].HashAlg = ibbhashalgs[iterator] + } + + vdata.CBNTbpm.SE = append(vdata.CBNTbpm.SE, *se) + + txt := cbntbootpolicy.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 + + vdata.CBNTbpm.TXTE = txt + + bootguard, err := bootguard.NewVData(vdata) + if err != nil { + return err + } + bBPM, err := bootguard.WriteBPM() + if err != nil { + return err + } + if err = os.WriteFile(t.Path, bBPM, 0600); err != nil { + return fmt.Errorf("unable to write BPM to file: %w", err) + } + return nil +} + +func (t *templateCmdv1) Run(ctx *context) error { + var vdata bootguard.VersionedData + var err error + + vdata.BGbpm = bgbootpolicy.NewManifest() + vdata.BGkm = bgkey.NewManifest() + + vdata.BGbpm.BPMH.BPMSVN = t.SVN + vdata.BGbpm.BPMH.ACMSVNAuth = t.ACMSVN + vdata.BGbpm.BPMH.NEMDataStack = t.NEMS + + se := bgbootpolicy.NewSE() + se.PBETValue = t.PBET + se.Flags = t.IBBSegFlags + se.IBBMCHBAR = t.MCHBAR + se.VTdBAR = t.VDTBAR + se.PMRLBase = t.PMRLBase + se.PMRLLimit = t.PMRLLimit + se.IBBEntryPoint = t.EntryPoint + se.Digest.HashAlg, err = bg.GetAlgFromString(t.IbbHash) + if err != nil { + return err + } + vdata.BGbpm.SE = append(vdata.BGbpm.SE, *se) + + bootguard, err := bootguard.NewVData(vdata) + if err != nil { + return err + } + bBPM, err := bootguard.WriteBPM() + if err != nil { + return err + } + if err = os.WriteFile(t.Path, bBPM, 0600); err != nil { + return fmt.Errorf("unable to write BPM to file: %w", err) + } + return nil +} + +func (rc *readConfigCmd) Run(ctx *context) error { + f, err := os.Create(rc.Config) + if err != nil { + return err + } + _, err = bootguard.NewBPMAndKMFromBIOS(rc.BIOS, f) + if err != nil { + return err + } + return nil +} + +func (s *stitchingKMCmd) Run(ctx *context) error { + file, err := os.Open(s.KM) + if err != nil { + return err + } + defer file.Close() + sig, err := os.ReadFile(s.Signature) + if err != nil { + return err + } + pub, err := bootguard.ReadPubKey(s.PubKey) + if err != nil { + return err + } + if len(sig) < 1 { + return fmt.Errorf("loaded files are empty") + } + bg, err := bootguard.NewKM(file) + if err != nil { + return err + } + km, err := bg.StitchKM(pub, sig) + if err != nil { + return err + } + if err := os.WriteFile(s.Out, km, 0644); err != nil { + return err + } + return nil +} + +func (s *stitchingBPMCmd) Run(ctx *context) error { + file, err := os.Open(s.BPM) + if err != nil { + return err + } + defer file.Close() + sig, err := os.ReadFile(s.Signature) + if err != nil { + return err + } + pub, err := bootguard.ReadPubKey(s.PubKey) + if err != nil { + return err + } + if len(sig) < 1 { + return fmt.Errorf("loaded files are empty") + } + bg, err := bootguard.NewBPM(file) + if err != nil { + return err + } + bpm, err := bg.StitchBPM(pub, sig) + if err != nil { + return err + } + if err := os.WriteFile(s.Out, bpm, 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 = os.ReadFile(s.BPM); err != nil { + return err + } + } + if s.KM != "" { + if km, err = os.ReadFile(s.KM); err != nil { + return err + } + } + if s.ACM != "" { + if acm, err = os.ReadFile(s.ACM); err != nil { + return err + } + } + if s.ME != "" { + if me, err = os.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 := bootguard.StitchFITEntries(s.BIOS, acm, bpm, km); err != nil { + return err + } + if len(me) != 0 { + image, err := os.ReadFile(s.BIOS) + if err != nil { + return err + } + meRegionOffset, meRegionSize, err := tools.GetRegion(image, uefi.RegionTypeME) + if err != nil { + return err + } + 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 := bootguard.GenRSAKey(2048, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile) + if err != nil { + return err + } + case "RSA3072": + err := bootguard.GenRSAKey(3072, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile) + if err != nil { + return err + } + case "ECC224": + err := bootguard.GenECCKey(224, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile) + if err != nil { + return err + } + case "ECC256": + err := bootguard.GenECCKey(256, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile) + if err != nil { + return err + } + default: + return fmt.Errorf("chosen algorithm invalid. Options are: RSA2048, RSA3072, ECC224, ECC256") + } + + return nil +} + +func (p printFITCmd) Run(ctx *context) error { + img, err := os.ReadFile(p.BIOS) + if err != nil { + return err + } + table, err := fit.GetTable(img) + if err != nil { + return err + } + fmt.Printf("%s", table.String()) + return nil +} + +func (v *verifyKMSigCmd) Run(ctx *context) error { + file, err := os.Open(v.KM) + if err != nil { + return err + } + defer file.Close() + bg, err := bootguard.NewKM(file) + if err != nil { + return err + } + return bg.VerifyKM() +} + +func (b *verifyBPMSigCmd) Run(ctx *context) error { + file, err := os.Open(b.BPM) + if err != nil { + return err + } + defer file.Close() + bg, err := bootguard.NewBPM(file) + if err != nil { + return err + } + return bg.VerifyBPM() +} + +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"` + KMGenV1 generateKMCmdv1 `cmd help:"Generate v1 KM file based von json configuration"` + KMGenV2 generateKMCmdv2 `cmd help:"Generate v2 KM file based von json configuration"` + KMSign signKMCmd `cmd help:"Sign key manifest with given key"` + KMVerify verifyKMSigCmd `cmd help:"Verify the signature of a given KM"` + 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"` + BPMGenV1 generateBPMCmdv1 `cmd help:"Generate v1 BPM file based von json configuration"` + BPMGenV2 generateBPMCmdv2 `cmd help:"Generate v2 BPM file based von json configuration"` + BPMSign signBPMCmd `cmd help:"Sign Boot Policy Manifest with given key"` + BPMVerify verifyBPMSigCmd `cmd help:"Verify the signature of a given KM"` + BPMStitch stitchingBPMCmd `cmd help:"Stitches BPM Signatue into unsigned BPM"` + BPMExport bpmExportCmd `cmd help:"Exports BPM structures from BIOS image into file"` + + ACMGenV0 generateACMCmdv0 `cmd help:"Generate an ACM v0 module (usable only for unit-tests)"` + ACMGenV3 generateACMCmdv3 `cmd help:"Generate an ACM v3 module (usable only for unit-tests)"` + ACMExport acmExportCmd `cmd help:"Exports ACM structures from BIOS image into file"` + ACMShow acmPrintCmd `cmd help:"Prints ACM binary in human-readable format"` + + FITShow printFITCmd `cmd help:"Prints the FIT Table of given BIOS image file"` + + 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"` + TemplateV1 templateCmdv1 `cmd help:"Writes template v1 JSON configuration into file"` + TemplateV2 templateCmdv2 `cmd help:"Writes template v2 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"` +} diff --git a/cmd/cbnt-prov/main.go b/cmd/bg-prov/main.go similarity index 84% rename from cmd/cbnt-prov/main.go rename to cmd/bg-prov/main.go index aec7f265..2e5c870c 100644 --- a/cmd/cbnt-prov/main.go +++ b/cmd/bg-prov/main.go @@ -3,7 +3,7 @@ package main import ( "github.com/9elements/converged-security-suite/v2/pkg/log" "github.com/alecthomas/kong" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" fianoLog "github.com/linuxboot/fiano/pkg/log" ) @@ -26,7 +26,7 @@ func main() { Compact: true, Summary: true, })) - manifest.StrictOrderCheck = cli.ManifestStrictOrderCheck + cbnt.StrictOrderCheck = cli.ManifestStrictOrderCheck fianoLog.DefaultLogger = log.DummyLogger{} err := ctx.Run(&context{Debug: cli.Debug}) ctx.FatalIfErrorf(err) diff --git a/cmd/cbnt-prov/README.md b/cmd/cbnt-prov/README.md deleted file mode 100644 index 187e5b66..00000000 --- a/cmd/cbnt-prov/README.md +++ /dev/null @@ -1,334 +0,0 @@ -Intel CBnT Provisioning -=============================== - -This Golang utility supports the artifact generation to support Intel Converged BootGuard and Trustes Execution Technology (CBnT) - -Prerequisites for Usage ------------------------ -Supported OS: Any Linux distribution - -How to compile ------------------------ - -Get Golang >= 1.11 and export: -``` -export GO111MODULE=on -``` -or set it in front of every command. -This environment variable actives moduled for GO 1.11 - -To download all dependencies run: -``` - go mod download -``` - -Verify all downloaded dependencies run: -``` - go mod verify -``` - -To build the test suite run: - -``` - go build -o cbnt-prov cmd/cbnt-prov/*.go -``` - -Commandline subcommands: --------------- -```bash -Usage of ./cbnt-prov: - version - Prints the version of the program - show-km - Prints Key Manifest binary in human-readable format - show-bpm - Prints Boot Policy Manifest binary in human-readable format - show-acm - Prints ACM binary in human-readable format - show-all - Prints BPM, KM, FIT and ACM from BIOS binary in human-readable format - export-acm - Exports ACM structures from BIOS image into file - export-km - Exports KM structures from BIOS image into file - export-bpm - Exports BPM structures from BIOS image into file - template - Writes template JSON configuration into file - read-config - Reads config from existing BIOS file and translates it to a JSON configuration - km-gen - Generate KM file based on json configuration - bpm-gen - Generate BPM file based on json configuration - km-sign - Sign key manifest with given key - bpm-sign - Sign Boot Policy Manifest with given key - stitch - Stitches BPM, KM and ACM into given BIOS image file - key-gen - Generates key for KM and BPM signing - -Flags: - --help (-h) - Prints more information about ./cbnt-prov -``` -Every subcommand has several required or optional arguments and flags. To learn more about them: -```bash -./cbnt-prov -h -``` - -Extended documentation about subcommands: --------------- - -```bash -./cbnt-prov show-km Prints Key Manifest binary in human-readable format - Path to binary file containing Key Manifest -``` - -```bash -./cbnt-prov show-bpm Prints Boot Policy Manifest binary in human-readable format - Path to binary file containing Boot Policy Manifest -``` - -```bash -./cbnt-prov show-acm Prints ACM binary in human-readable format - Path to binary file containing Authenticated Code Module (ACM) -``` - -```bash -./cbnt-prov show-all Prints BPM, KM, FIT and ACM from Firmware image binary in human-readable format - Path to full Firmaware image binary file containing Key Manifest, Boot Policy Manifest and ACM -``` - -```bash -./cbnt-prov export-acm Exports ACM binary from Firmware image into file - Path to the full Firmware image binary file. - Path to the newly generated ACM binary file. -``` - -```bash -./cbnt-prov export-km Exports KM structures from Firmware image image into file - Path to the full Firmware image binary file. - Path to the newly generated Key Manifest binary file. -``` - -```bash -./cbnt-prov export-bpm Exports BPM structures from Firmware image image into file - Path to the full Firmware image binary file. - Path to the newly generated Boot Policy Manifest binary file. -``` - -```bash -./cbnt-prov read-config Reads config from existing BIOS file and translates it to a JSON configuration - Path to the JSON config file. - Path to the full Firmware image binary file. -``` - - -```bash -./cbnt-prov km-gen Generate KM file based of json configuration - Path to the newly generated Key Manifest binary file. - Public Boot Policy signing key - - --config=STRING Path to the JSON config file. - --revision=UINT-8 Platform Manufacturer’s BPM revision number. - --svn=UINT-8 Boot Policy Manifest Security Version Number - --id=UINT-8 The key Manifest Identifier - --pkhashalg=UINT-16 Hash algorithm of OEM public key digest - --bpmpubkey=STRING Path to bpm public signing key - --bpmhashalgo=ALGORITHM Hash algorithm for bpm public signing key - --out=STRING Path to write applied config to - --cut Cuts the signature before writing to binary (Facebook requirement) -``` - -```bash -./cbnt-prov bpm-gen Generate BPM file based of json configuration and complete firmware image - Path to the newly generated Boot Policy Manifest binary file. - Path to the firmware image binary file. - - --config Path to the JSON config file. - - --revision Platform Manufacturer’s BPM revision number. - --svn Boot Policy Manifest Security Version Number - --acmsvn Authorized ACM Security Version Number - --nems 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 - --pbet Protect BIOS Environment Timer (PBET) value. - --ibbflags IBB Control flags - --mchbar MCHBAR address - --vdtbar VTDPVC0BAR address - --dmabase0 Low DMA protected range base - --dmasize0 Low DMA protected range limit - --dmabase1 High DMA protected range base. - --dmasize1 High DMA protected range limit. - --entrypoint IBB (Startup BIOS) entry point - --sintmin OEM authorized SinitMinSvn value - --txtflags TXT Element control flags - --powerdowninterval Duration of Power Down in 5 sec increments - --acpibaseoffset ACPI IO offset. - --powermbaseoffset ACPI MMIO offset. - --cmosoff0 CMOS byte in bank 0 to store platform wakeup time - --cmosoff1 Second CMOS byte in bank 0 to store platform wakeup time - - --out Path to write applied config to -``` - -```bash -./cbnt-prov km-sign Sign key manifest with given key - Path to the generated Key Manifest binary file. - Path to write the signed KM to - Path to the encrypted PKCS8 private key file. - Password to decrypted PKCS8 private key file -``` - -```bash -./cbnt-prov bpm-sign Sign Boot Policy Manifest with given key - Path to the newly generated Boot Policy Manifest binary file. - Path to write the signed BPM to - Path to the encrypted PKCS8 private key file. - Password to decrypt PKCS8 private key file -``` - -```bash -./cbnt-prov stitch Stitches BPM, KM and ACM into given BIOS image file - Path to the full BIOS binary file. - [] Path to the ACM binary file. - [] Path to the Key Manifest binary file. - [] Path to the Boot Policy Manifest binary file. -``` - -```bash -./cbnt-prov key-gen Generates key for KM and BPM signing - Select crypto algorithm for key generation. Options: RSA2048. RSA3072, ECC224, ECC256 - Password for AES256 encryption of private keys - [] Path to store keys. - File names are '_bpm/.pub' and '_km/.pub' respectivly -``` - - -```bash -./cbnt-prov template Writes template JSON configuration into file - Path to the newly generated JSON configuration file. - - --revision Platform Manufacturer’s BPM revision number. - --svn Boot Policy Manifest Security Version Number - --acmsvn Authorized ACM Security Version Number - --nems 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 - --pbet Protect BIOS Environment Timer (PBET) value. - --ibbflags IBB Control flags - --mchbar MCHBAR address - --vdtbar VTDPVC0BAR address - --dmabase0 Low DMA protected range base - --dmasize0 Low DMA protected range limit - --dmabase1 High DMA protected range base. - --dmasize1 High DMA protected range limit. - --entrypoint IBB (Startup BIOS) entry point - --sintmin OEM authorized SinitMinSvn value - --txtflags TXT Element control flags - --powerdowninterval Duration of Power Down in 5 sec increments - --acpibaseoffset ACPI IO offset. - --powermbaseoffset ACPI MMIO offset. - --cmosoff0 CMOS byte in bank 0 to store platform wakeup time - --cmosoff1 Second CMOS byte in bank 0 to store platform wakeup time -``` - -Workflows -========== - -I. Boot Policy / Key Manifest Generation/Signing/Stitching -------------------------------- - -1. Create a template config file -```bash -./cbnt-prov template ./config.json -``` - -2. Create keys for signing of Key Manifest (KM) and Boot Policy Manifest (BPM) -Algorithm: RSA, BitSize: 2048, no password for enryption of private key files -```bash -./cbnt-prov key-gen RSA2048 "" --path=./Keys/mykey -``` - -3. Generate Key Manifest (KM) -```bash -./cbnt-prov km-gen ./KM/km_unsigned.bin ./Keys/mykey_km_pub.pem \ - --config=./config.json \ - --pkhashalg=12 \ - --bpmpubkey=./Keys/mykey_bpmpub.pem \ - --bpmhashalgo=12 -``` - -4. Generation of Boot Policy Manifest (BPM) -```bash -./cbnt-prov bpm-gen ./BPM/bpm_unsigned.bin ./firmware.rom --config=./config.json -``` - -5. Sign Key Manifest (KM) -```bash -./cbnt-prov km-sign ./KM/km_unsigned.bin ./KM/km_signed.bin ./Keys/myKey_km_priv.pem "" -``` - -6. Sign Boot Policy Manifest (BPM) -```bash -./cbnt-prov bpm-sign ./BPM/bpm_unsigned.bin ./BPM/bpm_signed.bin ./Keys/myKey_bpm_priv.pem "" - -``` - -7. Export ACM for stitching (Firmware image must contain an ACM) -Skip this if you already have an ACM for stitching -```bash -./cbnt-prov export-acm ./firmware.rom ./ACM/acm_export.bin -``` - -8. Stitch BPM, KM and ACM into firmware image -```bash -./cbnt-prov stitch ./firmware.rom ./ACM/acm.bin ./KM/km_signed.bin ./BPM/bpm_signed.bin -``` - -II. Read config from a CBnT enabled firmware image -------------------------------------------- -```bash -./cbnt-prov read-config ./config.json ./firmware.rom -``` - -III Export KM, BPM and ACM from CBnT enabled firmware image ------------------------------------------------- -1. Export of KM -```bash -./cbnt-prov export-km ./firmware.rom ./KM/km_export.bin -``` - -2. Export BPM -```bash -./cbnt-prov export-km ./firmware.rom ./BPM/bpm_export.bin -``` - -3. Export ACM -```bash -./cbnt-prov export-acm ./firmware.rom ./ACM/acm_export.bin -``` - -IV. Show details of exported KM, BPM, ACM --------------------------------------- -1. Show details of KM -```bash -./cbnt-prov show-km ./KM/km_signed.bin -``` - -2. Show details of BPM -```bash -./cbnt-prov show-bpm ./BPM/bpm_signed.bin -``` - -3. Show details of ACM -```bash -./cbnt-prov show-acm ./ACM/acm_signed.bin -``` - -4. Show all -```bash -./cbnt-prov show-all ./firmware.rom -``` diff --git a/cmd/cbnt-prov/cmd.go b/cmd/cbnt-prov/cmd.go deleted file mode 100644 index 2d82c9e3..00000000 --- a/cmd/cbnt-prov/cmd.go +++ /dev/null @@ -1,1034 +0,0 @@ -package main - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "encoding/binary" - "errors" - "fmt" - "io" - "os" - - "github.com/linuxboot/fiano/pkg/intel/metadata/fit" - - "github.com/linuxboot/fiano/pkg/uefi" - - "github.com/9elements/converged-security-suite/v2/pkg/provisioning/cbnt" - "github.com/9elements/converged-security-suite/v2/pkg/tools" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/bootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/key" -) - -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 Manufacturer’s 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 []string `flag optional name:"ibbhash" help:"IBB Hash Algorithm. E.g.: SHA256, SHA384, SM3"` - // 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 generateACMCmd struct { - ACMOut string `arg required name:"acm" help:"Path to the newly generated ACM headers binary file." type:"path"` - ConfigIn string `flag optional name:"config" help:"Path to the JSON config file." type:"path"` - ConfigOut string `flag optional name:"out" help:"Path to write applied config to" type:"path"` - BodyPath string `flag optional name:"bodypath" help:"Path to the ACM body" type:"path"` - RSAPrivateKeyPEM string `flag optional name:"rsaprivkeypem" help:"RSA key used to sign the ACM" type:"path"` - - ModuleType fit.ACModuleType `flag optional name:"moduletype"` - ModuleSubType fit.ACModuleSubType `flag optional name:"modulesubtype"` - ChipsetID fit.ACChipsetID `flag optional name:"chipsetid"` - Flags fit.ACFlags `flag optional name:"flags"` - ModuleVendor fit.ACModuleVendor `flag optional name:"modulevendor"` - Date fit.BCDDate `flag optional name:"date"` - Size uint64 `flag optional name:"size"` - TXTSVN fit.TXTSVN `flag optional name:"txtsvn"` - SESVN fit.SESVN `flag optional name:"sesvn"` - CodeControl fit.CodeControl `flag optional name:"codecontrol"` - ErrorEntryPoint fit.ErrorEntryPoint `flag optional name:"errorentrypoint"` - GDTLimit fit.GDTLimit `flag optional name:"gdtlimit"` - GDTBasePtr fit.GDTBasePtr `flag optional name:"gdtbaseptr"` - SegSel fit.SegSel `flag optional name:"segsel"` - EntryPoint fit.EntryPoint `flag optional name:"entrypoint"` -} - -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 Manufacturer’s 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 string `flag optional name:"pkhashalg" help:"Hash algorithm of OEM public key digest. E.g.: SHA256, SHA384, SM3"` - 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 string `flag optional name:"bpmhashalgo" help:"Hash algorithm for bpm public signing key.. 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 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 Manufacturer’s 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 []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 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"` - SignAlgo string `arg required name:"signalgo" help:"Signing algorithm for KM. E.g.: RSASSA, RSAPSS, SM2"` - 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"` - SignAlgo string `arg required name:"signalgo" help:"Signing algorithm for KM. E.g.: RSASSA, RSAPSS, SM2"` - 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 `arg required name:"acm" help:"Path to the ACM binary file." type:"path"` - KM string `arg required name:"km" help:"Path to the Key Manifest binary file." type:"path"` - BPM string `arg required 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"` -} - -type printFITCmd struct { - BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"` -} - -type verifyKMSigCmd struct { - KM string `arg required name:"km" help:"Path to the Key Manifest binary file." type:"path"` -} -type verifyBPMSigCmd struct { - BPM string `arg required name:"bpm" help:"Path to the Boot Policy Manifest binary file." type:"path"` -} - -func (v *versionCmd) Run(ctx *context) error { - tools.ShowVersion(programName, gittag, gitcommit) - return nil -} - -func (kmp *kmPrintCmd) Run(ctx *context) error { - data, err := os.ReadFile(kmp.Path) - if err != nil { - return err - } - var km *key.Manifest - _, kmEntry, _, err := cbnt.ParseFITEntries(data) - if err != nil { - reader := bytes.NewReader(data) - km, err = cbnt.ParseKM(reader) - if err != nil { - return err - } - } else { - km, err = kmEntry.ParseData() - if err != nil { - return fmt.Errorf("unable to parse KM: %w", 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 := os.ReadFile(bpmp.Path) - if err != nil { - return err - } - var bpm *bootpolicy.Manifest - bpmEntry, _, _, err := cbnt.ParseFITEntries(data) - if err != nil { - reader := bytes.NewReader(data) - bpm, err = cbnt.ParseBPM(reader) - if err != nil { - return err - } - } else { - bpm, err = bpmEntry.ParseData() - if err != nil { - return fmt.Errorf("unable to parse BPM: %w", 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 := os.ReadFile(acmp.Path) - if err != nil { - return err - } - _, _, acmEntry, err := cbnt.ParseFITEntries(data) - if err == nil { - data = acmEntry.DataSegmentBytes - } - 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 := os.ReadFile(biosp.Path) - if err != nil { - return err - } - table, err := fit.GetTable(data) - if err != nil { - return err - } - fmt.Printf("%s", table.String()) - err = cbnt.PrintCBnTStructures(data) - if err != nil { - return err - } - return nil -} - -func (acme *acmExportCmd) Run(ctx *context) error { - data, err := os.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 := os.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 := os.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 err error - var cbnto cbnt.Options - tmpKM := key.NewManifest() - tmpKM.Revision = g.Revision - tmpKM.KMSVN = g.SVN - tmpKM.KMID = g.ID - tmpKM.PubKeyHashAlg, err = manifest.GetAlgFromString(g.PKHashAlg) - if err != nil { - return err - } - tmpKM.Hash = g.KMHashes - // Create KM_Hash for BPM pub signing key - if g.BpmPubkey != "" { - var bpmha manifest.Algorithm - bpmha, err = manifest.GetAlgFromString(g.BpmHashAlg) - if err != nil { - return err - } - kh, err := cbnt.GetBPMPubHash(g.BpmPubkey, bpmha) - if err != nil { - return err - } - tmpKM.Hash = kh - } else { - return fmt.Errorf("add --bpmpubkey= as argument") - } - 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 { - //Cut signature from binary - bKM = bKM[:int(options.KeyManifest.KeyManifestSignatureOffset)] - } - if err = os.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)) - - ibbhashalgs := make([]manifest.Algorithm, 0) - for _, item := range g.IbbHash { - hash, err := manifest.GetAlgFromString(item) - if err != nil { - return err - } - ibbhashalgs = append(ibbhashalgs, hash) - } - - for iterator := range se.DigestList.List { - se.DigestList.List[iterator].HashAlg = ibbhashalgs[iterator] - } - - ibbs, err := cbnt.FindAdditionalIBBs(g.BIOS) - if err != nil { - return fmt.Errorf("FindAdditionalIBBs: %w", err) - } - for counter := range ibbs { - ibbs[counter].Flags = g.IbbSegFlag - } - 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 fmt.Errorf("GenerateBPM: %w", 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 = os.WriteFile(g.BPM, bBPM, 0600); err != nil { - return fmt.Errorf("unable to write BPM to file: %w", err) - } - return nil -} - -func (g *generateACMCmd) config() (*cbnt.Options, error) { - if g.ConfigIn != "" { - config, err := cbnt.ParseConfig(g.ConfigIn) - if err != nil { - return nil, fmt.Errorf("unable to parse config file '%s': %w", g.ConfigIn, err) - } - return config, nil - } - - var acmHeaders fit.EntrySACMData3 - acmHeaders.HeaderVersion = fit.ACHeaderVersion3 - acmHeaders.HeaderLen.SetSize(uint64(binary.Size(acmHeaders))) - acmHeaders.ModuleType = g.ModuleType - acmHeaders.ModuleSubType = g.ModuleSubType - acmHeaders.ChipsetID = g.ChipsetID - acmHeaders.Flags = g.Flags - acmHeaders.ModuleVendor = g.ModuleVendor - acmHeaders.Date = g.Date - acmHeaders.Size.SetSize(g.Size) - acmHeaders.TXTSVN = g.TXTSVN - acmHeaders.SESVN = g.SESVN - acmHeaders.CodeControl = g.CodeControl - acmHeaders.ErrorEntryPoint = g.ErrorEntryPoint - acmHeaders.GDTLimit = g.GDTLimit - acmHeaders.GDTBasePtr = g.GDTBasePtr - acmHeaders.SegSel = g.SegSel - acmHeaders.EntryPoint = g.EntryPoint - acmHeaders.KeySize.SetSize(384) - return &cbnt.Options{ - ACMHeaders: &acmHeaders, - }, nil -} - -func (g *generateACMCmd) Run(ctx *context) error { - config, err := g.config() - if err != nil { - return fmt.Errorf("unable to construct basic ACM headers from the provided config: %w", err) - } - - if g.ConfigOut != "" { - out, err := os.Create(g.ConfigOut) - if err != nil { - return err - } - if err := cbnt.WriteConfig(out, config); err != nil { - return err - } - } - - acm := fit.EntrySACMData{ - EntrySACMDataInterface: config.ACMHeaders, - } - if g.BodyPath != "" { - bodyData, err := os.ReadFile(g.BodyPath) - if err != nil { - return fmt.Errorf("unable to read the ACM body file '%s': %w", g.BodyPath, err) - } - - acm.UserArea = bodyData - } - - if g.RSAPrivateKeyPEM != "" { - return fmt.Errorf("signing is not implemented, yet") - } - - var acmBytes bytes.Buffer - if _, err := acm.WriteTo(&acmBytes); err != nil { - return fmt.Errorf("unable to compile the ACM module: %w", err) - } - - if err = os.WriteFile(g.ACMOut, acmBytes.Bytes(), 0600); err != nil { - return fmt.Errorf("unable to write KM to file: %w", err) - } - return nil -} - -func (s *signKMCmd) Run(ctx *context) error { - encKey, err := os.ReadFile(s.Key) - if err != nil { - return err - } - privkey, err := cbnt.DecryptPrivKey(encKey, s.Password) - if err != nil { - return err - } - kmRaw, err := os.ReadFile(s.KmIn) - if err != nil { - return err - } - signAlgo, err := manifest.GetAlgFromString(s.SignAlgo) - 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(signAlgo, km.PubKeyHashAlg, privkey.(crypto.Signer), unsignedKM); err != nil { - return err - } - bKMSigned, err := cbnt.WriteKM(&km) - if err != nil { - return err - } - if err := os.WriteFile(s.KmOut, bKMSigned, 0600); err != nil { - return err - } - return nil -} - -func (s *signBPMCmd) Run(ctx *context) error { - encKey, err := os.ReadFile(s.Key) - if err != nil { - return err - } - key, err := cbnt.DecryptPrivKey(encKey, s.Password) - if err != nil { - return err - } - bpmRaw, err := os.ReadFile(s.BpmIn) - if err != nil { - return err - } - signAlgo, err := manifest.GetAlgFromString(s.SignAlgo) - 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(signAlgo, 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 = os.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 - se.DigestList.List = make([]manifest.HashStructure, len(t.IbbHash)) - - ibbhashalgs := make([]manifest.Algorithm, 0) - for _, item := range t.IbbHash { - hash, err := manifest.GetAlgFromString(item) - if err != nil { - return err - } - ibbhashalgs = append(ibbhashalgs, hash) - } - - for iterator := range se.DigestList.List { - se.DigestList.List[iterator].HashAlg = ibbhashalgs[iterator] - } - - 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 := os.ReadFile(s.KM) - if err != nil { - return err - } - sig, err := os.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 := os.WriteFile(s.Out, kmRaw, 0644); err != nil { - return err - } - return nil -} - -func (s *stitchingBPMCmd) Run(ctx *context) error { - bpmData, err := os.ReadFile(s.BPM) - if err != nil { - return err - } - sig, err := os.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 := os.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 = os.ReadFile(s.BPM); err != nil { - return err - } - } - if s.KM != "" { - if km, err = os.ReadFile(s.KM); err != nil { - return err - } - } - if s.ACM != "" { - if acm, err = os.ReadFile(s.ACM); err != nil { - return err - } - } - if s.ME != "" { - if me, err = os.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 := os.ReadFile(s.BIOS) - if err != nil { - return err - } - meRegionOffset, meRegionSize, err := tools.GetRegion(image, uefi.RegionTypeME) - if err != nil { - return err - } - 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 invalid. Options are: RSA2048, RSA3072, ECC224, ECC256") - } - - return nil -} - -func (p printFITCmd) Run(ctx *context) error { - img, err := os.ReadFile(p.BIOS) - if err != nil { - return err - } - table, err := fit.GetTable(img) - if err != nil { - return err - } - fmt.Printf("%s", table.String()) - return nil -} - -func (v *verifyKMSigCmd) Run(ctx *context) error { - kmRaw, err := os.ReadFile(v.KM) - if err != nil { - return err - } - - var km key.Manifest - r := bytes.NewReader(kmRaw) - if _, err = km.ReadFrom(r); err != nil { - return err - } - if err := km.KeyAndSignature.Verify(kmRaw[:km.KeyAndSignatureOffset()]); err != nil { - return err - } - - return nil -} - -func (b *verifyBPMSigCmd) Run(ctx *context) error { - bpmraw, err := os.ReadFile(b.BPM) - if err != nil { - return err - } - - var bpm bootpolicy.Manifest - r := bytes.NewReader(bpmraw) - if _, err = bpm.ReadFrom(r); err != nil { - return err - } - if err := bpm.PMSE.Verify(bpmraw[:bpm.KeySignatureOffset]); err != nil { - return err - } - - 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"` - KMVerify verifyKMSigCmd `cmd help:"Verify the signature of a given KM"` - 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"` - BPMVerify verifyBPMSigCmd `cmd help:"Verify the signature of a given KM"` - BPMStitch stitchingBPMCmd `cmd help:"Stitches BPM Signatue into unsigned BPM"` - BPMExport bpmExportCmd `cmd help:"Exports BPM structures from BIOS image into file"` - - ACMGen generateACMCmd `cmd help:"Generate an ACM module (usable only for unit-tests)"` - ACMExport acmExportCmd `cmd help:"Exports ACM structures from BIOS image into file"` - ACMShow acmPrintCmd `cmd help:"Prints ACM binary in human-readable format"` - - FITShow printFITCmd `cmd help:"Prints the FIT Table of given BIOS image file"` - - 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"` -} diff --git a/cmd/pcr0tool/main.go b/cmd/pcr0tool/main.go index e2ffa076..c57ae160 100644 --- a/cmd/pcr0tool/main.go +++ b/cmd/pcr0tool/main.go @@ -14,7 +14,7 @@ import ( "github.com/9elements/converged-security-suite/v2/cmd/pcr0tool/commands/printnodes" "github.com/9elements/converged-security-suite/v2/cmd/pcr0tool/commands/sum" "github.com/9elements/converged-security-suite/v2/pkg/log" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" fianoLog "github.com/linuxboot/fiano/pkg/log" ) @@ -52,7 +52,7 @@ func setupFlag() { } func main() { - manifest.StrictOrderCheck = false // some firmwares have incorrect elements order, should parse them anyway + cbnt.StrictOrderCheck = false // some firmwares have incorrect elements order, should parse them anyway fianoLog.DefaultLogger = log.DummyLogger{} setupFlag() diff --git a/cmd/txt-prov/main.go b/cmd/txt-prov/main.go index 94e0cf01..23f028a8 100644 --- a/cmd/txt-prov/main.go +++ b/cmd/txt-prov/main.go @@ -3,7 +3,7 @@ package main import ( "github.com/9elements/converged-security-suite/v2/pkg/log" "github.com/alecthomas/kong" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" fianoLog "github.com/linuxboot/fiano/pkg/log" ) @@ -24,7 +24,7 @@ func main() { Compact: true, Summary: true, })) - manifest.StrictOrderCheck = cli.ManifestStrictOrderCheck + cbnt.StrictOrderCheck = cli.ManifestStrictOrderCheck fianoLog.DefaultLogger = log.DummyLogger{} // Run commands diff --git a/cmd/txt-suite/main.go b/cmd/txt-suite/main.go index 4a41964f..ba629f21 100644 --- a/cmd/txt-suite/main.go +++ b/cmd/txt-suite/main.go @@ -3,7 +3,7 @@ package main import ( "github.com/9elements/converged-security-suite/v2/pkg/log" "github.com/alecthomas/kong" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" fianoLog "github.com/linuxboot/fiano/pkg/log" ) @@ -37,7 +37,7 @@ func main() { Compact: true, Summary: true, })) - manifest.StrictOrderCheck = cli.ManifestStrictOrderCheck + cbnt.StrictOrderCheck = cli.ManifestStrictOrderCheck fianoLog.DefaultLogger = log.DummyLogger{} err := ctx.Run(&context{}) ctx.FatalIfErrorf(err) diff --git a/go.mod b/go.mod index 709d05a6..15f333d7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.19 require ( github.com/9elements/go-linux-lowlevel-hw v0.0.0-20220518111144-a82949f8ff5b github.com/alecthomas/kong v0.7.1 - github.com/creasty/defaults v1.6.0 github.com/davecgh/go-spew v1.1.1 github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e github.com/edsrzf/mmap-go v1.1.0 @@ -16,13 +15,12 @@ require ( github.com/google/go-tpm v0.3.3 github.com/google/uuid v1.3.0 github.com/klauspost/cpuid/v2 v2.2.3 - github.com/linuxboot/fiano v1.1.3 + github.com/linuxboot/fiano v1.1.4-0.20230131115913-85ddba13ba44 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/marcoguerri/go-tpm-tcti v0.0.0-20210425104733-8e8c8fe68e60 github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 github.com/stretchr/testify v1.8.1 github.com/tidwall/pretty v1.2.1 - github.com/tjfoc/gmsm v1.4.1 github.com/ulikunitz/xz v0.5.11 github.com/xaionaro-facebook/go-dmidecode v0.0.0-20220413144237-c42d5bef2498 github.com/xaionaro-go/bytesextra v0.0.0-20220103144954-846e454ddea9 @@ -41,8 +39,9 @@ require ( github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/intel-go/cpuid v0.0.0-20200819041909-2aa72927c3e2 // indirect + github.com/intel-go/cpuid v0.0.0-20220614022739-219e067757cb // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/term v0.4.0 // indirect diff --git a/go.sum b/go.sum index a7aeaed1..4c813bd4 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/creasty/defaults v1.6.0 h1:ltuE9cfphUtlrBeomuu8PEyISTXnxqkBIoQfXgv7BSc= -github.com/creasty/defaults v1.6.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -281,8 +279,9 @@ github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/intel-go/cpuid v0.0.0-20200819041909-2aa72927c3e2 h1:h+RKaNPjka7LRJGoeub/IQBdXSoEaJjfADkBq02hvjw= github.com/intel-go/cpuid v0.0.0-20200819041909-2aa72927c3e2/go.mod h1:RmeVYf9XrPRbRc3XIx0gLYA8qOFvNoPOfaEZduRlEp4= +github.com/intel-go/cpuid v0.0.0-20220614022739-219e067757cb h1:Fg0Y/RDZ6UPwl3o7/IzPbneDq8g9+gH6DPs42KFUsy8= +github.com/intel-go/cpuid v0.0.0-20220614022739-219e067757cb/go.mod h1:RmeVYf9XrPRbRc3XIx0gLYA8qOFvNoPOfaEZduRlEp4= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -324,8 +323,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linuxboot/fiano v1.1.3 h1:/EFBlsC+896J0YbU0CHi7lSm5dOcE08vE0ro9jBAl50= -github.com/linuxboot/fiano v1.1.3/go.mod h1:1FwHUkd0fdHTvACyx6IdVn1GBAiS5mG2CM5b0nLMLxo= +github.com/linuxboot/fiano v1.1.4-0.20230131115913-85ddba13ba44 h1:Hp7L8kitx3x2MEXmL5NqVLD2/lzGtjYJnNEmjWv1ado= +github.com/linuxboot/fiano v1.1.4-0.20230131115913-85ddba13ba44/go.mod h1:1FwHUkd0fdHTvACyx6IdVn1GBAiS5mG2CM5b0nLMLxo= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= diff --git a/pkg/pcr/flow_detection.go b/pkg/pcr/flow_detection.go index 66708f98..6f71df30 100644 --- a/pkg/pcr/flow_detection.go +++ b/pkg/pcr/flow_detection.go @@ -5,8 +5,8 @@ import ( "fmt" amd "github.com/linuxboot/fiano/pkg/amd/manifest" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" "github.com/9elements/converged-security-suite/v2/pkg/errors" "github.com/9elements/converged-security-suite/v2/pkg/registers" @@ -55,7 +55,7 @@ func DetectTPM(firmware Firmware, regs registers.Registers) (tpmdetection.Type, if data == nil { return 0, fmt.Errorf("unable to parse EntrySACM: %w", err) } - _, chipset, err := manifest.ParseChipsetACModuleInformation(bytes.NewBuffer(data.UserArea)) + _, chipset, err := cbnt.ParseChipsetACModuleInformation(bytes.NewBuffer(data.UserArea)) if err != nil { return 0, fmt.Errorf("failed to read ChipsetACModuleInformation, err: %w", err) } @@ -69,7 +69,7 @@ func DetectTPM(firmware Firmware, regs registers.Registers) (tpmdetection.Type, // chipset.TPMInfoList is an offset in bytes from ACM start. image := firmware.ImageBytes() - var tpmInfo manifest.TPMInfoList + var tpmInfo cbnt.TPMInfoList sacmOffset := fitEntry.Headers.Address.Offset(uint64(len(image))) _, err = tpmInfo.ReadFrom(bytes.NewBuffer(image[sacmOffset+uint64(chipset.TPMInfoList):])) if err != nil { @@ -243,11 +243,11 @@ func isCBnT(fitEntries []fit.Entry) (bool, error) { for _, fitEntry := range fitEntries { switch fitEntry := fitEntry.(type) { case *fit.EntryKeyManifestRecord: - data, err := fitEntry.ParseData() - if data == nil { + data, data2, err := fitEntry.ParseData() + if data == nil && data2 == nil { return false, fmt.Errorf("unable to parse KeyManifest policy record: %w", err) } - if data.Version < 0x21 { + if data != nil { return false, nil } keyManifestFound = true diff --git a/pkg/pcr/get_measurements_pcr0_cbnt0t.go b/pkg/pcr/get_measurements_pcr0_cbnt0t.go index 8cbe87a1..d05441de 100644 --- a/pkg/pcr/get_measurements_pcr0_cbnt0t.go +++ b/pkg/pcr/get_measurements_pcr0_cbnt0t.go @@ -6,10 +6,10 @@ import ( "github.com/9elements/converged-security-suite/v2/pkg/registers" pkgbytes "github.com/linuxboot/fiano/pkg/bytes" + "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/fit" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/bootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/key" ) type pcr0Data struct { @@ -98,7 +98,7 @@ func MeasurePCR0Data(config MeasurementConfig, imageSize uint64, fitEntries []fi // Note: +2 - skip array size field to get the first element offsetToTheFirstDigest := bpmOffset + bpManifest.SEOffset() + bpManifest.SE[0].DigestListOffset() + (bpManifest.SE[0].DigestList.ListOffset() + 2) - if config.PCR0DataIbbDigestHashAlgorithm == manifest.AlgUnknown || config.PCR0DataIbbDigestHashAlgorithm == manifest.AlgNull { + if config.PCR0DataIbbDigestHashAlgorithm == cbnt.AlgUnknown || config.PCR0DataIbbDigestHashAlgorithm == cbnt.AlgNull { // take the fist element as stated in the doc above data.ibbDigest = pkgbytes.Range{ Offset: offsetToTheFirstDigest + (digests[0].HashBufferOffset() + 2), @@ -142,35 +142,35 @@ func getACM(fitEntries []fit.Entry) (*fit.EntrySACMData, *fit.EntrySACM, error) return nil, nil, fmt.Errorf("ACM FIT entry is not found") } -func getKeyManifest(fitEntries []fit.Entry) (*key.Manifest, *fit.EntryKeyManifestRecord, error) { +func getKeyManifest(fitEntries []fit.Entry) (*cbntkey.Manifest, *fit.EntryKeyManifestRecord, error) { for _, fitEntry := range fitEntries { switch fitEntry := fitEntry.(type) { case *fit.EntryKeyManifestRecord: - km, err := fitEntry.ParseData() - if err != nil { + _, km2, err := fitEntry.ParseData() + if err != nil && km2 != nil { return nil, nil, err } - return km, fitEntry, nil + return km2, fitEntry, nil } } return nil, nil, fmt.Errorf("key manifest FIT entry is not found") } -func getBootPolicyManifest(fitEntries []fit.Entry) (*bootpolicy.Manifest, *fit.EntryBootPolicyManifestRecord, error) { +func getBootPolicyManifest(fitEntries []fit.Entry) (*cbntbootpolicy.Manifest, *fit.EntryBootPolicyManifestRecord, error) { for _, fitEntry := range fitEntries { switch fitEntry := fitEntry.(type) { case *fit.EntryBootPolicyManifestRecord: - bpManifest, err := fitEntry.ParseData() - if err != nil { + _, bpManifest2, err := fitEntry.ParseData() + if err != nil && bpManifest2 != nil { return nil, nil, err } - return bpManifest, fitEntry, nil + return bpManifest2, fitEntry, nil } } return nil, nil, fmt.Errorf("boot policy manifest FIT entry is not found") } -// MeasureKeyManifest returns a measurement containing CBnT key manifest. +// MeasureKeyManifest returns a measurement containing CBnT key cbnt. func MeasureKeyManifest(imageSize uint64, fitEntries []fit.Entry) (*Measurement, error) { _, kmFITEntry, err := getKeyManifest(fitEntries) if err != nil { @@ -188,7 +188,7 @@ func MeasureKeyManifest(imageSize uint64, fitEntries []fit.Entry) (*Measurement, }, nil } -// MeasureBootPolicy returns a measurement containing CBnT key manifest. +// MeasureBootPolicy returns a measurement containing CBnT key cbnt. func MeasureBootPolicy(imageSize uint64, fitEntries []fit.Entry) (*Measurement, error) { _, bpmFITEntry, err := getBootPolicyManifest(fitEntries) if err != nil { diff --git a/pkg/pcr/get_measurements_pcr0_legacytxtenabled.go b/pkg/pcr/get_measurements_pcr0_legacytxtenabled.go index abf8fc0f..117cb454 100644 --- a/pkg/pcr/get_measurements_pcr0_legacytxtenabled.go +++ b/pkg/pcr/get_measurements_pcr0_legacytxtenabled.go @@ -7,8 +7,8 @@ import ( "fmt" "github.com/9elements/converged-security-suite/v2/pkg/errors" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" ) // MeasureInit returns the fake measurement for TPM initialization, it @@ -83,16 +83,16 @@ func MeasureACMDate(imageSize uint64, fitEntries []fit.Entry) (*Measurement, err // MeasureACMDateInPlace returns a measurement of ACM date, but without hashing // it (it is used in obsolete TPM1.2 flows; a bug of the initial implementation?). -func MeasureACMDateInPlace(hashAlg manifest.Algorithm, imageSize uint64, fitEntries []fit.Entry) (*Measurement, error) { +func MeasureACMDateInPlace(hashAlg cbnt.Algorithm, imageSize uint64, fitEntries []fit.Entry) (*Measurement, error) { m := Measurement{ ID: MeasurementIDACMDateInPlace, } var hashSize int switch hashAlg { - case manifest.AlgSHA1: + case cbnt.AlgSHA1: hashSize = sha1.New().Size() - case manifest.AlgSHA256: + case cbnt.AlgSHA256: hashSize = sha256.New().Size() default: return nil, fmt.Errorf("unknown hash algorithm: %v", hashAlg) diff --git a/pkg/pcr/measure_option.go b/pkg/pcr/measure_option.go index c6b2c124..3d9bf6ec 100644 --- a/pkg/pcr/measure_option.go +++ b/pkg/pcr/measure_option.go @@ -3,9 +3,9 @@ package pcr import ( "github.com/9elements/converged-security-suite/v2/pkg/tpmdetection" "github.com/google/go-tpm/tpm2" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" "github.com/9elements/converged-security-suite/v2/pkg/registers" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" ) // MeasureOption is the interface of an option which may change @@ -28,7 +28,7 @@ type SetIBBHashDigest tpm2.Algorithm // Apply implements `MeasureOption` func (opt SetIBBHashDigest) Apply(config *MeasurementConfig) error { - config.PCR0DataIbbDigestHashAlgorithm = manifest.Algorithm(opt) + config.PCR0DataIbbDigestHashAlgorithm = cbnt.Algorithm(opt) return nil } diff --git a/pkg/pcr/measurement_config.go b/pkg/pcr/measurement_config.go index 1e3aedb2..5c9ff293 100644 --- a/pkg/pcr/measurement_config.go +++ b/pkg/pcr/measurement_config.go @@ -3,7 +3,7 @@ package pcr import ( "github.com/9elements/converged-security-suite/v2/pkg/registers" "github.com/9elements/converged-security-suite/v2/pkg/tpmdetection" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt" ) // MeasurementConfig is the structure used to store different gates about @@ -25,7 +25,7 @@ type MeasurementConfig struct { // PCR0DataIbbDigestHashAlgorithm defines hash algorithm that should be used for pcr0Data.ibbDigest // TPM_ALG_ERROR will use the first element (by default) - PCR0DataIbbDigestHashAlgorithm manifest.Algorithm + PCR0DataIbbDigestHashAlgorithm cbnt.Algorithm // TPMDevice defines a TPM device version that performed the measurements. // Value TypeNoTPM means undefined diff --git a/pkg/provisioning/bootguard/bootguard.go b/pkg/provisioning/bootguard/bootguard.go new file mode 100644 index 00000000..e73b95fc --- /dev/null +++ b/pkg/provisioning/bootguard/bootguard.go @@ -0,0 +1,792 @@ +package bootguard + +import ( + "bytes" + "crypto" + "encoding/json" + "errors" + "fmt" + "io" + "os" + + "github.com/9elements/converged-security-suite/v2/pkg/tools" + "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" + "github.com/linuxboot/fiano/pkg/intel/metadata/fit" + "github.com/tidwall/pretty" +) + +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) { + 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 cbntKMReader(km *cbntkey.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 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) + } + manifest, err = cbntKMReader(vdata.CBNTkm) + if err == nil { + b.Version, _ = bgheader.DetectBGV(manifest) + } + if b.Version == 0 { + return nil, fmt.Errorf("NewVData: can't identify bootguard header") + } + b.VData = vdata + return &b, nil +} + +func NewBPM(bpm io.ReadSeeker) (*BootGuard, error) { + var b BootGuard + if bpm == nil { + return nil, fmt.Errorf("manifest is nil") + } + var err error + b.Version, err = bgheader.DetectBGV(bpm) + if err != nil { + return nil, err + } + switch b.Version { + case bgheader.Version10: + b.VData.BGbpm = bgbootpolicy.NewManifest() + _, 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() + _, err = b.VData.CBNTbpm.ReadFrom(bpm) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + default: + return nil, fmt.Errorf("NewBPM: can't identify bootguard header") + } + return &b, nil +} + +func NewKM(km io.ReadSeeker) (*BootGuard, error) { + var b BootGuard + if km == nil { + return nil, fmt.Errorf("manifest is nil") + } + var err error + b.Version, err = bgheader.DetectBGV(km) + if err != nil { + return nil, err + } + switch b.Version { + case bgheader.Version10: + b.VData.BGkm = bgkey.NewManifest() + _, 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() + _, err = b.VData.CBNTkm.ReadFrom(km) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + default: + return nil, fmt.Errorf("NewKM: can't identify bootguard header") + } + return &b, nil +} + +func NewBPMAndKM(bpm io.ReadSeeker, km io.ReadSeeker) (*BootGuard, error) { + var b BootGuard + if bpm != nil && km != nil { + return nil, fmt.Errorf("either both or one manifest is nil") + } + var err error + bpmV, err := bgheader.DetectBGV(bpm) + if err != nil { + return nil, err + } + kmV, err := bgheader.DetectBGV(km) + if err != nil { + return nil, err + } + if bpmV != kmV { + return nil, fmt.Errorf("km and bpm version number differ") + } + switch bpmV { + case bgheader.Version10: + b.VData.BGbpm = bgbootpolicy.NewManifest() + b.VData.BGkm = bgkey.NewManifest() + _, err := b.VData.BGbpm.ReadFrom(bpm) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + _, err = b.VData.BGkm.ReadFrom(km) + 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) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + _, err = b.VData.CBNTkm.ReadFrom(km) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + default: + return nil, fmt.Errorf("NewBPMAndKM: can't identify bootguard header") + } + return &b, nil +} + +func NewBPMAndKMFromBIOS(biosFilepath string, jsonFilepath *os.File) (*BootGuard, error) { + bios, err := os.ReadFile(biosFilepath) + if err != nil { + return nil, err + } + bpmEntry, kmEntry, _, err := ParseFITEntries(bios) + if err != nil { + return nil, err + } + var b BootGuard + b.Version, err = bgheader.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()) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + _, err = b.VData.BGkm.ReadFrom(kmEntry.Reader()) + 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()) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + _, err = b.VData.CBNTkm.ReadFrom(kmEntry.Reader()) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + default: + return nil, fmt.Errorf("NewBPMAndKMFromBIOS: can't identify bootguard header") + } + data, err := json.Marshal(b.VData) + if err != nil { + return nil, err + } + json := pretty.Pretty(data) + if _, err = jsonFilepath.Write(json); err != nil { + return nil, err + } + return &b, nil +} + +// ValidateBPM reads from a binary, parses into the boot policy manifest structure +// and validates the structure +func (b *BootGuard) ValidateBPM() error { + switch b.Version { + case bgheader.Version10: + return b.VData.BGbpm.Validate() + case bgheader.Version20: + return b.VData.CBNTbpm.Validate() + default: + return fmt.Errorf("ValidateBPM: can't identify bootguard header") + } +} + +// ValidateKM reads from a binary source, parses into the key manifest structure +// and validates the structure +func (b *BootGuard) ValidateKM() error { + switch b.Version { + case bgheader.Version10: + return b.VData.BGkm.Validate() + case bgheader.Version20: + return b.VData.CBNTkm.Validate() + default: + return fmt.Errorf("ValidateKM: can't identify bootguard header") + } +} + +// PrintBPM prints the boot policy manifest in human readable +func (b *BootGuard) PrintBPM() { + switch b.Version { + case bgheader.Version10: + b.VData.BGbpm.Print() + case bgheader.Version20: + b.VData.CBNTbpm.Print() + default: + fmt.Println("PrintBPM: can't identify bootguard header") + } +} + +// PrintKM prints the key manifest in human readable +func (b *BootGuard) PrintKM() { + switch b.Version { + case bgheader.Version10: + b.VData.BGkm.Print() + case bgheader.Version20: + b.VData.CBNTkm.Print() + default: + fmt.Println("PrintKM: can't identify bootguard header") + } +} + +// WriteKM returns a key manifest as bytes in format defined in #575623. +func (b *BootGuard) WriteKM() ([]byte, error) { + var err error + buf := new(bytes.Buffer) + switch b.Version { + case bgheader.Version10: + _, err = b.VData.BGkm.WriteTo(buf) + case bgheader.Version20: + _, err = b.VData.CBNTkm.WriteTo(buf) + default: + fmt.Println("WriteKM: can't identify bootguard header") + } + return buf.Bytes(), err +} + +// WriteBPM returns a boot policy manifest as byte slice +func (b *BootGuard) WriteBPM() ([]byte, error) { + var err error + buf := new(bytes.Buffer) + switch b.Version { + case bgheader.Version10: + _, err = b.VData.BGbpm.WriteTo(buf) + case bgheader.Version20: + _, err = b.VData.BGbpm.WriteTo(buf) + default: + fmt.Println("WriteBPM: can't identify bootguard header") + } + return buf.Bytes(), err +} + +// WriteJSON returns the entire VData structure in JSON format +func (b *BootGuard) WriteJSON(f *os.File) error { + cfg, err := json.Marshal(b.VData) + if err != nil { + return err + } + json := pretty.Pretty(cfg) + if _, err := f.Write(json); err != nil { + return err + } + return nil +} + +// ReadJSON returns the entire VData structure in JSON format +func (b *BootGuard) ReadJSON(filepath string) error { + data, err := os.ReadFile(filepath) + if err != nil { + return err + } + if err = json.Unmarshal(data, &b.VData); err != nil { + return err + } + return nil +} + +// 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: + 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: + if err := b.VData.CBNTkm.KeyAndSignature.FillSignature(0, pubKey, signature, b.VData.CBNTkm.PubKeyHashAlg); err != nil { + return nil, err + } + b.VData.CBNTkm.RehashRecursive() + if err := b.VData.CBNTkm.Validate(); err != nil { + return nil, err + } + default: + fmt.Println("StitchKM: can't identify bootguard header") + } + return b.WriteKM() +} + +// 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.KeySignature.FillSignature(0, pubKey, signature, bg.AlgNull); err != nil { + return nil, err + } + + b.VData.BGbpm.RehashRecursive() + if err := b.VData.BGbpm.Validate(); err != nil { + return nil, err + } + case bgheader.Version20: + b.VData.CBNTbpm.PMSE = *cbntbootpolicy.NewSignature() + if err := b.VData.CBNTbpm.PMSE.KeySignature.FillSignature(0, pubKey, signature, cbnt.AlgNull); err != nil { + return nil, err + } + + b.VData.CBNTbpm.RehashRecursive() + if err := b.VData.CBNTbpm.Validate(); err != nil { + return nil, err + } + default: + fmt.Println("StitchBPM: can't identify bootguard header") + } + return b.WriteBPM() +} + +// SignKM signs an unsigned KM with signAlgo and private key as input +func (b *BootGuard) SignKM(signAlgo string, privkey crypto.PrivateKey) ([]byte, error) { + buf := new(bytes.Buffer) + switch b.Version { + case bgheader.Version10: + signAlgo, err := bg.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, privkey.(crypto.Signer), unsignedKM); err != nil { + return nil, err + } + case bgheader.Version20: + signAlgo, err := cbnt.GetAlgFromString(signAlgo) + if err != nil { + return nil, err + } + b.VData.CBNTkm.RehashRecursive() + _, err = b.VData.CBNTkm.WriteTo(buf) + if err != nil { + return nil, err + } + unsignedKM := buf.Bytes()[:b.VData.CBNTkm.KeyAndSignatureOffset()] + if err = b.VData.CBNTkm.SetSignature(signAlgo, b.VData.CBNTkm.PubKeyHashAlg, privkey.(crypto.Signer), unsignedKM); err != nil { + return nil, err + } + default: + fmt.Println("SignKM: can't identify bootguard header") + } + return b.WriteKM() +} + +// SignBPM signs an unsigned KM with signAlgo and private key as input +func (b *BootGuard) SignBPM(signAlgo string, privkey crypto.PrivateKey) ([]byte, error) { + buf := new(bytes.Buffer) + switch b.Version { + case bgheader.Version10: + signAlgo, err := bg.GetAlgFromString(signAlgo) + if err != nil { + return nil, err + } + b.VData.BGbpm.PMSE = *bgbootpolicy.NewSignature() + 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 { + return nil, err + } + case bgheader.Version20: + signAlgo, err := cbnt.GetAlgFromString(signAlgo) + if err != nil { + return nil, err + } + b.VData.CBNTbpm.PMSE = *cbntbootpolicy.NewSignature() + b.VData.CBNTbpm.RehashRecursive() + _, err = b.VData.CBNTbpm.WriteTo(buf) + if err != nil { + return nil, err + } + unsignedBPM := buf.Bytes()[:b.VData.CBNTbpm.PMSE.KeySignatureOffset()] + if err = b.VData.CBNTbpm.PMSE.SetSignature(signAlgo, b.VData.CBNTbpm.PMSE.Key.KeyAlg, privkey.(crypto.Signer), unsignedBPM); err != nil { + return nil, err + } + default: + fmt.Println("SignBPM: can't identify bootguard header") + } + return b.WriteKM() +} + +// VerifyKM verifies a signed KM +func (b *BootGuard) VerifyKM() error { + buf := new(bytes.Buffer) + switch b.Version { + case bgheader.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 { + return err + } + case bgheader.Version20: + _, 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 { + return err + } + default: + fmt.Println("VerifyKM: can't identify bootguard header") + } + return nil +} + +// VerifyBPM verifies a signed BPM +func (b *BootGuard) VerifyBPM() error { + buf := new(bytes.Buffer) + switch b.Version { + case bgheader.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 { + return err + } + case bgheader.Version20: + _, 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 { + return err + } + default: + fmt.Println("VerifyBPM: can't identify bootguard header") + } + return nil +} + +// CalculateNEMSize calculates No Eviction Memory and returns it as count of 4K pages. +func (b *BootGuard) CalculateNEMSize(image []byte, acm *tools.ACM) (uint16, error) { + var totalSize uint32 + if acm == nil { + return 0, fmt.Errorf("ACM is nil") + } + fitTable, err := fit.GetTable(image) + if err != nil { + return 0, fmt.Errorf("unable to get FIT: %w", err) + } + fitEntries := fitTable.GetEntries(image) + if len(fitEntries) == 0 || fitEntries[0].GetEntryBase().Headers.Type() != fit.EntryTypeFITHeaderEntry { + return 0, fmt.Errorf("unable to get FIT headers") + } + hdr := fitEntries[0] + if err != nil { + return 0, err + } + totalSize += keySignatureElementMaxSize + totalSize += uint32(hdr.GetEntryBase().Headers.Size.Uint32() << 4) + totalSize += uint32(2048) + totalSize += keySignatureElementMaxSize + totalSize += uint32(acm.Header.GetSize().Size()) + totalSize += defaultStackAndDataSize + switch b.Version { + case bgheader.Version10: + totalSize += uint32((&bgbootpolicy.BPMH{}).TotalSize()) + totalSize += uint32(b.VData.BGbpm.SE[0].TotalSize()) + for _, ibb := range b.VData.BGbpm.SE[0].IBBSegments { + totalSize += ibb.Size + } + if b.VData.BGbpm.PME != nil { + totalSize += uint32(b.VData.BGbpm.PME.DataSize) + } + totalSize += uint32(12) + totalSize += keySignatureElementMaxSize + if (totalSize + additionalNEMSize) > defaultLLCSize { + return 0, fmt.Errorf("NEM size is bigger than LLC %d", totalSize+additionalNEMSize) + } + if (totalSize % 4096) != 0 { + totalSize += 4096 - (totalSize % 4096) + } + return uint16(bgbootpolicy.NewSize4K(totalSize)), nil + case bgheader.Version20: + totalSize += uint32(b.VData.CBNTkm.KeyManifestSignatureOffset) + totalSize += uint32((&cbntbootpolicy.BPMH{}).TotalSize()) + for _, se := range b.VData.CBNTbpm.SE { + totalSize += uint32(se.ElementSize) + for _, ibb := range se.IBBSegments { + totalSize += ibb.Size + } + } + if b.VData.CBNTbpm.PCDE != nil { + totalSize += uint32(b.VData.CBNTbpm.PCDE.ElementSize) + } + if b.VData.CBNTbpm.PME != nil { + totalSize += uint32(b.VData.CBNTbpm.PME.ElementSize) + } + totalSize += uint32(12) + totalSize += keySignatureElementMaxSize + if b.VData.CBNTbpm.TXTE != nil { + totalSize += uint32(b.VData.CBNTbpm.TXTE.ElementSize) + } + if (totalSize + additionalNEMSize) > defaultLLCSize { + return 0, fmt.Errorf("NEM size is bigger than LLC %d", totalSize+additionalNEMSize) + } + if (totalSize % 4096) != 0 { + totalSize += 4096 - (totalSize % 4096) + } + return uint16(cbntbootpolicy.NewSize4K(totalSize)), nil + default: + return 0, fmt.Errorf("CalculateNEMSize: can't identify bootguard header") + } +} + +// GetBPMPubHash takes the path to public BPM signing key and hash algorithm +// and returns a hash with hashAlg of pub BPM singing key +func (b *BootGuard) GetBPMPubHash(pubKeyFilePath, hashAlgo string) error { + var data []byte + pubkey, err := ReadPubKey(pubKeyFilePath) + if err != nil { + return err + } + var kAs cbnt.Key + if err := kAs.SetPubKey(pubkey); err != nil { + return err + } + switch b.Version { + case bgheader.Version10: + hashAlg, err := bg.GetAlgFromString(hashAlgo) + if err != nil { + return err + } + hash, err := hashAlg.Hash() + if err != nil { + return err + } + k := kAs.Data[4:] + if _, err := hash.Write(k); err != nil { + return err + } + data = hash.Sum(nil) + hStruc := bg.HashStructure{ + HashAlg: bg.Algorithm(hashAlg), + } + hStruc.HashBuffer = data + b.VData.BGkm.BPKey = hStruc + case bgheader.Version20: + hashAlg, err := cbnt.GetAlgFromString(hashAlgo) + if err != nil { + return err + } + hash, err := hashAlg.Hash() + if err != nil { + return err + } + k := kAs.Data[4:] + if _, err := hash.Write(k); err != nil { + return err + } + data = hash.Sum(nil) + var keyHashes []cbntkey.Hash + hStruc := &cbnt.HashStructure{ + HashAlg: cbnt.Algorithm(hashAlg), + } + hStruc.HashBuffer = data + + kH := cbntkey.Hash{ + Usage: cbntkey.UsageBPMSigningPKD, + Digest: *hStruc, + } + b.VData.CBNTkm.Hash = append(keyHashes, kH) + default: + fmt.Println("can't identify bootguard header") + } + return nil +} + +func (b *BootGuard) GetIBBsDigest(image []byte, hashAlgo string) (digest []byte, err error) { + switch b.Version { + case bgheader.Version10: + hashAlg, err := bg.GetAlgFromString(hashAlgo) + if err != nil { + return nil, err + } + hash, err := hashAlg.Hash() + if err != nil { + return nil, err + } + ibbs := b.VData.BGbpm.SE[0].IBBSegments + reader := bytes.NewReader(image) + ibbSegments := make([][]byte, len(ibbs)) + for idx, ibb := range ibbs { + if ibb.Flags&(1<<0) != 0 { + continue + } + addr, err := tools.CalcImageOffset(image, uint64(ibb.Base)) + if err != nil { + return nil, fmt.Errorf("unable to calculate the offset: %w", err) + } + _, err = reader.Seek(int64(addr), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("got error from Seek: %w", err) + } + size := uint64(ibb.Size) + ibbSegments[idx] = make([]byte, size) + _, err = reader.Read(ibbSegments[idx]) + if err != nil { + return nil, fmt.Errorf("unable to read the segment: %w", err) + } + } + for _, segment := range ibbSegments { + _, err = hash.Write(segment) + if err != nil { + return nil, err + } + } + digest = hash.Sum(nil) + case bgheader.Version20: + hashAlg, err := cbnt.GetAlgFromString(hashAlgo) + if err != nil { + return nil, err + } + hash, err := hashAlg.Hash() + if err != nil { + return nil, err + } + ibbs := b.VData.CBNTbpm.SE[0].IBBSegments + reader := bytes.NewReader(image) + ibbSegments := make([][]byte, len(ibbs)) + for idx, ibb := range ibbs { + if ibb.Flags&(1<<0) != 0 { + continue + } + addr, err := tools.CalcImageOffset(image, uint64(ibb.Base)) + if err != nil { + return nil, fmt.Errorf("unable to calculate the offset: %w", err) + } + _, err = reader.Seek(int64(addr), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("got error from Seek: %w", err) + } + size := uint64(ibb.Size) + ibbSegments[idx] = make([]byte, size) + _, err = reader.Read(ibbSegments[idx]) + if err != nil { + return nil, fmt.Errorf("unable to read the segment: %w", err) + } + } + for _, segment := range ibbSegments { + _, err = hash.Write(segment) + if err != nil { + return nil, err + } + } + digest = hash.Sum(nil) + default: + fmt.Println("can't identify bootguard header") + } + return digest, nil +} + +// GenerateBPM generates a Boot Policy Manifest with the given config and firmware image +func (b *BootGuard) GenerateBPMFromImage(biosFilepath string) (*BootGuard, error) { + data, err := os.ReadFile(biosFilepath) + if err != nil { + return nil, fmt.Errorf("unable to read file '%s': %w", biosFilepath, err) + } + switch b.Version { + case bgheader.Version10: + hashAlgo := b.VData.BGbpm.SE[0].Digest.HashAlg.String() + d, err := b.GetIBBsDigest(data, hashAlgo) + if err != nil { + return nil, fmt.Errorf("unable to getIBBsDigest for %v: %w", hashAlgo, err) + } + b.VData.BGbpm.SE[0].Digest.HashBuffer = make([]byte, len(d)) + copy(b.VData.BGbpm.SE[0].Digest.HashBuffer, d) + case bgheader.Version20: + for iterator, item := range b.VData.CBNTbpm.SE[0].DigestList.List { + d, err := b.GetIBBsDigest(data, item.HashAlg.String()) + if err != nil { + return nil, fmt.Errorf("unable to getIBBsDigest for %v: %w", item.HashAlg, err) + } + b.VData.CBNTbpm.SE[0].DigestList.List[iterator].HashBuffer = make([]byte, len(d)) + copy(b.VData.CBNTbpm.SE[0].DigestList.List[iterator].HashBuffer, d) + } + default: + fmt.Println("can't identify bootguard header") + } + return b, nil +} diff --git a/pkg/provisioning/cbnt/constants.go b/pkg/provisioning/bootguard/constants.go similarity index 91% rename from pkg/provisioning/cbnt/constants.go rename to pkg/provisioning/bootguard/constants.go index 534cd0f8..cf60d7c8 100644 --- a/pkg/provisioning/cbnt/constants.go +++ b/pkg/provisioning/bootguard/constants.go @@ -1,4 +1,4 @@ -package cbnt +package bootguard const ( keySignatureElementMaxSize = 3072 // how this value was calculated? diff --git a/pkg/provisioning/cbnt/keygen.go b/pkg/provisioning/bootguard/keygen.go similarity index 87% rename from pkg/provisioning/cbnt/keygen.go rename to pkg/provisioning/bootguard/keygen.go index 0905e6bd..9cce39e3 100644 --- a/pkg/provisioning/cbnt/keygen.go +++ b/pkg/provisioning/bootguard/keygen.go @@ -1,4 +1,4 @@ -package cbnt +package bootguard import ( "crypto" @@ -234,3 +234,32 @@ func ReadPubKey(path string) (crypto.PublicKey, error) { } return nil, fmt.Errorf("failed to parse public key") } + +func parsePrivateKey(raw []byte) (crypto.Signer, error) { + for { + block, rest := pem.Decode(raw) + if block == nil { + break + } + if block.Type != "CERTIFICATE" { + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err == nil { + if key, ok := key.(crypto.Signer); ok { + return key, nil + } + return nil, fmt.Errorf("found unknown private key type (%T) in PKCS#8 wrapping", key) + } + key, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err == nil { + if key, ok := key.(crypto.Signer); ok { + return key, nil + } + return nil, fmt.Errorf("found unknown private key type (%T) in PKCS#1 wrapping", key) + } + return nil, err + + } + raw = rest + } + return nil, fmt.Errorf("failed to parse private key") +} diff --git a/pkg/provisioning/cbnt/structures.go b/pkg/provisioning/bootguard/structures.go similarity index 53% rename from pkg/provisioning/cbnt/structures.go rename to pkg/provisioning/bootguard/structures.go index a28dfd4d..4c1d1b60 100644 --- a/pkg/provisioning/cbnt/structures.go +++ b/pkg/provisioning/bootguard/structures.go @@ -1,4 +1,13 @@ -package cbnt +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" +) // CMOSIoAddress holds information about the location of on-demand power down requests in CMOS. // The structure is a substructure used in PowerDownRequest structure. @@ -49,3 +58,31 @@ type Pcr7Data struct { BPMKey [32]byte BPMKeyHash []byte } + +// IbbSegment exports the struct of IBB Segments +type IbbSegment struct { + Offset uint32 `json:"offset"` // + Size uint32 `json:"size"` // + Flags uint16 `json:"flags"` // +} + +// KeyHash export for usage as cmd line argument type +type KeyHash struct { + Usage uint64 `json:"usage"` // + Hash string `json:"hash"` // + Algorithm cbnt.Algorithm `json:"algorithm"` // +} + +// 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"` +} + +// BootGuard unification structure, operates on manifests and reader +type BootGuard struct { + VData VersionedData `json:"bootguard"` + Version bgheader.BootGuardVersion +} diff --git a/pkg/provisioning/cbnt/test_artifacts/km.signed b/pkg/provisioning/bootguard/test_artifacts/km.signed similarity index 100% rename from pkg/provisioning/cbnt/test_artifacts/km.signed rename to pkg/provisioning/bootguard/test_artifacts/km.signed diff --git a/pkg/provisioning/cbnt/test_artifacts/km.unsigned b/pkg/provisioning/bootguard/test_artifacts/km.unsigned similarity index 100% rename from pkg/provisioning/cbnt/test_artifacts/km.unsigned rename to pkg/provisioning/bootguard/test_artifacts/km.unsigned diff --git a/pkg/provisioning/cbnt/test_artifacts/test_bpm b/pkg/provisioning/bootguard/test_artifacts/test_bpm similarity index 100% rename from pkg/provisioning/cbnt/test_artifacts/test_bpm rename to pkg/provisioning/bootguard/test_artifacts/test_bpm diff --git a/pkg/provisioning/cbnt/test_artifacts/test_bpm.pub b/pkg/provisioning/bootguard/test_artifacts/test_bpm.pub similarity index 100% rename from pkg/provisioning/cbnt/test_artifacts/test_bpm.pub rename to pkg/provisioning/bootguard/test_artifacts/test_bpm.pub diff --git a/pkg/provisioning/cbnt/test_artifacts/test_km b/pkg/provisioning/bootguard/test_artifacts/test_km similarity index 100% rename from pkg/provisioning/cbnt/test_artifacts/test_km rename to pkg/provisioning/bootguard/test_artifacts/test_km diff --git a/pkg/provisioning/cbnt/test_artifacts/test_km.pub b/pkg/provisioning/bootguard/test_artifacts/test_km.pub similarity index 100% rename from pkg/provisioning/cbnt/test_artifacts/test_km.pub rename to pkg/provisioning/bootguard/test_artifacts/test_km.pub diff --git a/pkg/provisioning/cbnt/tools.go b/pkg/provisioning/bootguard/tools.go similarity index 68% rename from pkg/provisioning/cbnt/tools.go rename to pkg/provisioning/bootguard/tools.go index 7fe7df39..d3292fac 100644 --- a/pkg/provisioning/cbnt/tools.go +++ b/pkg/provisioning/bootguard/tools.go @@ -1,16 +1,15 @@ -package cbnt +package bootguard import ( + "bytes" "fmt" "io" "os" "github.com/9elements/converged-security-suite/v2/pkg/tools" "github.com/9elements/converged-security-suite/v2/pkg/uefi/consts" + "github.com/linuxboot/fiano/pkg/intel/metadata/cbnt/cbntbootpolicy" "github.com/linuxboot/fiano/pkg/intel/metadata/fit" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/bootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/common/pretty" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/key" "github.com/linuxboot/fiano/pkg/cbfs" ) @@ -39,48 +38,33 @@ func WriteCBnTStructures(image []byte, bpmFile, kmFile, acmFile *os.File) error return nil } -// PrintCBnTStructures takes a firmware image and prints boot policy manifest, key manifest, ACM, chipset, processor and tpm information if available. -func PrintCBnTStructures(image []byte) error { +// PrintStructures takes a firmware image and prints boot policy manifest, key manifest, ACM, chipset, processor and tpm information if available. +func PrintStructures(image []byte) error { var acm *tools.ACM - var chipsets *tools.Chipsets - var processors *tools.Processors - var tpms *tools.TPMs - var err, err2 error + var err error bpmEntry, kmEntry, acmEntry, err := ParseFITEntries(image) if err != nil { return err } - bpm, err := bpmEntry.ParseData() + bpm, err := NewBPM(bpmEntry.Reader()) if err != nil { return fmt.Errorf("unable to parse BPM: %w", err) } - km, err := kmEntry.ParseData() + km, err := NewKM(kmEntry.Reader()) if err != nil { return fmt.Errorf("unable to parse KM: %w", err) } - acm, chipsets, processors, tpms, err, err2 = tools.ParseACM(acmEntry.DataSegmentBytes) - if err != nil || err2 != nil { + acm, err = tools.ParseACM(bytes.NewReader(acmEntry.DataSegmentBytes)) + if err != nil { return err } - - if bpm != nil { - fmt.Println(bpm.PrettyString(0, true)) - } - if km != nil { - if km.KeyAndSignature.Signature.DataTotalSize() < 1 { - fmt.Println(km.PrettyString(0, true, pretty.OptionOmitKeySignature(true))) - } else { - fmt.Println(km.PrettyString(0, true, pretty.OptionOmitKeySignature(false))) - } - } + km.PrintKM() + bpm.PrintBPM() if acm != nil { acm.PrettyPrint() - chipsets.PrettyPrint() - processors.PrettyPrint() - tpms.PrettyPrint() } return nil } @@ -108,58 +92,6 @@ func ParseFITEntries(image []byte) (bpm *fit.EntryBootPolicyManifestRecord, km * return bpm, km, acm, nil } -// CalculateNEMSize calculates No Eviction Memory and returns it as count of 4K pages. -func CalculateNEMSize(image []byte, bpm *bootpolicy.Manifest, km *key.Manifest, acm *tools.ACM) (bootpolicy.Size4K, error) { - var totalSize uint32 - if bpm == nil || km == nil || acm == nil { - return 0, fmt.Errorf("BPM, KM or ACM are nil") - } - fitTable, err := fit.GetTable(image) - if err != nil { - return 0, fmt.Errorf("unable to get FIT: %w", err) - } - fitEntries := fitTable.GetEntries(image) - if len(fitEntries) == 0 || fitEntries[0].GetEntryBase().Headers.Type() != fit.EntryTypeFITHeaderEntry { - return 0, fmt.Errorf("unable to get FIT headers") - } - hdr := fitEntries[0] - if err != nil { - return 0, err - } - totalSize += uint32(km.KeyManifestSignatureOffset) - totalSize += keySignatureElementMaxSize - totalSize += uint32(hdr.GetEntryBase().Headers.Size.Uint32() << 4) - totalSize += uint32(2048) - totalSize += keySignatureElementMaxSize - totalSize += uint32((&bootpolicy.BPMH{}).TotalSize()) - for _, se := range bpm.SE { - totalSize += uint32(se.ElementSize) - for _, ibb := range se.IBBSegments { - totalSize += ibb.Size - } - } - if bpm.PCDE != nil { - totalSize += uint32(bpm.PCDE.ElementSize) - } - if bpm.PME != nil { - totalSize += uint32(bpm.PME.ElementSize) - } - totalSize += uint32(12) - totalSize += keySignatureElementMaxSize - if bpm.TXTE != nil { - totalSize += uint32(bpm.TXTE.ElementSize) - } - totalSize += acm.Header.Size - totalSize += defaultStackAndDataSize - if (totalSize + additionalNEMSize) > defaultLLCSize { - return 0, fmt.Errorf("NEM size is bigger than LLC %d", totalSize+additionalNEMSize) - } - if (totalSize % 4096) != 0 { - totalSize += 4096 - (totalSize % 4096) - } - return bootpolicy.NewSize4K(totalSize), nil -} - // StitchFITEntries takes a firmware filename, an acm, a boot policy manifest and a key manifest as byte slices // and writes the information into the Firmware Interface Table of the firmware image. func StitchFITEntries(biosFilename string, acm, bpm, km []byte) error { @@ -274,8 +206,8 @@ func StitchFITEntries(biosFilename string, acm, bpm, km []byte) error { // FindAdditionalIBBs takes a coreboot image, searches cbfs files for // additional IBBSegment. -func FindAdditionalIBBs(imagepath string) ([]bootpolicy.IBBSegment, error) { - ibbs := make([]bootpolicy.IBBSegment, 0) +func FindAdditionalIBBs(imagepath string) ([]cbntbootpolicy.IBBSegment, error) { + ibbs := make([]cbntbootpolicy.IBBSegment, 0) image, err := os.Open(imagepath) if err != nil { return nil, err @@ -302,7 +234,7 @@ func FindAdditionalIBBs(imagepath string) ([]bootpolicy.IBBSegment, error) { } for _, entry := range fitentries { if entry.GetEntryBase().Headers.Type() == fit.EntryTypeBIOSStartupModuleEntry { - ibb := bootpolicy.NewIBBSegment() + ibb := cbntbootpolicy.NewIBBSegment() ibb.Base = uint32(entry.GetEntryBase().Headers.Address.Pointer()) ibb.Size = entry.GetEntryBase().Headers.Size.Uint32() << 4 ibbs = append(ibbs, *ibb) @@ -320,7 +252,7 @@ func FindAdditionalIBBs(imagepath string) ([]bootpolicy.IBBSegment, error) { "fallback/verstage", "bootblock": - ibb := bootpolicy.NewIBBSegment() + ibb := cbntbootpolicy.NewIBBSegment() ibb.Base = uint32(flashBase) + cbfsbaseaddr + seg.GetFile().RecordStart + seg.GetFile().SubHeaderOffset ibb.Size = seg.GetFile().Size ibb.Flags = 0 diff --git a/pkg/provisioning/cbnt/config.go b/pkg/provisioning/cbnt/config.go deleted file mode 100644 index 17f8f92c..00000000 --- a/pkg/provisioning/cbnt/config.go +++ /dev/null @@ -1,302 +0,0 @@ -package cbnt - -import ( - "bytes" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - "encoding/json" - "fmt" - "hash" - "io" - "os" - - "github.com/9elements/converged-security-suite/v2/pkg/tools" - "github.com/creasty/defaults" - "github.com/linuxboot/fiano/pkg/intel/metadata/fit" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/bootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/key" - "github.com/tidwall/pretty" - "github.com/tjfoc/gmsm/sm3" -) - -// IbbSegment exports the struct of IBB Segments -type IbbSegment struct { - Offset uint32 `json:"offset"` // - Size uint32 `json:"size"` // - Flags uint16 `json:"flags"` // -} - -// KeyHash export for usage as cmd line argument type -type KeyHash struct { - Usage uint64 `json:"usage"` // - Hash string `json:"hash"` // - Algorithm manifest.Algorithm `json:"algorithm"` // -} - -// Options presents all available options for CBnT configuarion file. -type Options struct { - BootPolicyManifest *bootpolicy.Manifest - KeyManifest *key.Manifest - ACMHeaders *fit.EntrySACMData3 -} - -// ParseConfig parses a boot guard option json file -func ParseConfig(filepath string) (*Options, error) { - var cbnto Options - data, err := os.ReadFile(filepath) - if err != nil { - return nil, err - } - if err = json.Unmarshal(data, &cbnto); err != nil { - return nil, err - } - return &cbnto, nil -} - -func setBPMHeader(cbnto *Options, bpm *bootpolicy.Manifest) (*bootpolicy.BPMH, error) { - header := bootpolicy.NewBPMH() - if err := defaults.Set(header); err != nil { - return nil, err - } - header.BPMRevision = cbnto.BootPolicyManifest.BPMRevision - header.BPMSVN = cbnto.BootPolicyManifest.BPMH.BPMSVN - header.ACMSVNAuth = cbnto.BootPolicyManifest.BPMH.ACMSVNAuth - header.NEMDataStack = cbnto.BootPolicyManifest.BPMH.NEMDataStack - header.KeySignatureOffset = uint16(bpm.PMSEOffset() + bpm.PMSE.KeySignatureOffset()) - - return header, nil -} - -func getIBBSegment(ibbs []bootpolicy.IBBSegment, image []byte) ([][]byte, error) { - reader := bytes.NewReader(image) - ibbSegments := make([][]byte, len(ibbs)) - for idx, ibb := range ibbs { - if ibb.Flags&(1<<0) != 0 { - continue - } - //offset := uint64(ibb.BaseOffset()) - addr, err := tools.CalcImageOffset(image, uint64(ibb.Base)) - if err != nil { - return nil, fmt.Errorf("unable to calculate the offset: %w", err) - } - _, err = reader.Seek(int64(addr), io.SeekStart) - if err != nil { - return nil, fmt.Errorf("got error from Seek: %w", err) - } - size := uint64(ibb.Size) - ibbSegments[idx] = make([]byte, size) - _, err = reader.Read(ibbSegments[idx]) - if err != nil { - return nil, fmt.Errorf("unable to read the segment: %w", err) - } - } - return ibbSegments, nil -} - -func getIBBsDigest(ibbs []bootpolicy.IBBSegment, image []byte, algo manifest.Algorithm) (hashout []byte, err error) { - var hashFunc hash.Hash - switch algo { - case manifest.AlgSHA1: - hashFunc = sha1.New() - case manifest.AlgSHA256: - hashFunc = sha256.New() - case manifest.AlgSHA384: - hashFunc = sha512.New384() - case manifest.AlgSHA512: - hashFunc = sha512.New512_256() - case manifest.AlgSM3: - hashFunc = sm3.New() - case manifest.AlgNull: - return nil, nil - default: - return nil, fmt.Errorf("couldn't match requested hash algorithm: 0x%x", algo) - } - segments, err := getIBBSegment(ibbs, image) - if err != nil { - return nil, err - } - for _, segment := range segments { - _, err = hashFunc.Write(segment) - if err != nil { - return nil, err - } - } - hashout = hashFunc.Sum(nil) - - return hashout, nil -} - -func setIBBSegment(cbnto *Options, image []byte) (*bootpolicy.SE, error) { - for iterator, item := range cbnto.BootPolicyManifest.SE[0].DigestList.List { - d, err := getIBBsDigest(cbnto.BootPolicyManifest.SE[0].IBBSegments, image, item.HashAlg) - if err != nil { - return nil, fmt.Errorf("unable to getIBBsDigest for %v: %w", item.HashAlg, err) - } - cbnto.BootPolicyManifest.SE[0].DigestList.List[iterator].HashBuffer = make([]byte, len(d)) - copy(cbnto.BootPolicyManifest.SE[0].DigestList.List[iterator].HashBuffer, d) - } - - return &cbnto.BootPolicyManifest.SE[0], nil -} - -func setTXTElement(cbnto *Options) (*bootpolicy.TXT, error) { - return cbnto.BootPolicyManifest.TXTE, nil -} - -func setPCDElement(cbnto *Options) (*bootpolicy.PCD, error) { - pcde := bootpolicy.NewPCD() - if cbnto.BootPolicyManifest.PCDE == nil { - return nil, nil - } - pcde.Data = cbnto.BootPolicyManifest.PCDE.Data - return pcde, nil -} - -func setPMElement(cbnto *Options) (*bootpolicy.PM, error) { - pme := bootpolicy.NewPM() - if cbnto.BootPolicyManifest.PME == nil { - return nil, nil - } - pme.Data = cbnto.BootPolicyManifest.PME.Data - return pme, nil -} - -func setPMSElement(cbnto *Options, bpm *bootpolicy.Manifest) (*bootpolicy.Signature, error) { - psme := bootpolicy.NewSignature() - return psme, nil -} - -// SetKM takes Options struct and initializes a new KM with the given configuration. -func SetKM(cbnto *Options) (*key.Manifest, error) { - return cbnto.KeyManifest, nil -} - -// GenerateBPM generates a Boot Policy Manifest with the given config and firmware image -func GenerateBPM(cbnto *Options, biosFilepath string) (*bootpolicy.Manifest, error) { - bpm := bootpolicy.NewManifest() - data, err := os.ReadFile(biosFilepath) - if err != nil { - return nil, fmt.Errorf("unable to read file '%s': %w", biosFilepath, err) - } - se, err := setIBBSegment(cbnto, data) - if err != nil { - return nil, fmt.Errorf("setIBBSegment: %w", err) - } - bpm.SE = append(bpm.SE, *se) - bpm.TXTE, err = setTXTElement(cbnto) - if err != nil { - return nil, fmt.Errorf("setTXTElement: %w", err) - } - bpm.PCDE, err = setPCDElement(cbnto) - if err != nil { - return nil, fmt.Errorf("setPCDElement: %w", err) - } - bpm.PME, err = setPMElement(cbnto) - if err != nil { - return nil, fmt.Errorf("setPMElement: %w", err) - } - bpmh, err := setBPMHeader(cbnto, bpm) - if err != nil { - return nil, fmt.Errorf("setBPMHeader: %w", err) - } - bpm.BPMH = *bpmh - pmse, err := setPMSElement(cbnto, bpm) - if err != nil { - return nil, fmt.Errorf("setPMSElement: %w", err) - } - bpm.PMSE = *pmse - - return bpm, nil -} - -// WriteConfig writes a CBnT config file to the given path with given options. -func WriteConfig(f *os.File, cbnto *Options) error { - cfg, err := json.Marshal(cbnto) - if err != nil { - return err - } - json := pretty.Pretty(cfg) - if _, err := f.Write(json); err != nil { - return err - } - return nil -} - -// ReadConfigFromBIOSImage reads boot guard options, boot policy manifest and key manifest from a given firmware image -// and writes that to a given file in json format -func ReadConfigFromBIOSImage(biosFilepath string, configFilepath *os.File) (*Options, error) { - var cbnto Options - var bpm *bootpolicy.Manifest - var km *key.Manifest - bios, err := os.ReadFile(biosFilepath) - if err != nil { - return nil, err - } - bpmEntry, kmEntry, _, err := ParseFITEntries(bios) - if err != nil { - return nil, err - } - - bpm, err = bpmEntry.ParseData() - if err != nil { - return nil, fmt.Errorf("ReadConfigurationFromBIOSImage: unable to get BPM: %w", err) - } - - km, err = kmEntry.ParseData() - if err != nil { - return nil, fmt.Errorf("ReadConfigurationFromBIOSImage: unable to get KM: %w", err) - } - - /* Boot Policy Manifest */ - // BPMH - cbnto.BootPolicyManifest = bpm - - /* Key Manifest */ - cbnto.KeyManifest = km - data, err := json.Marshal(cbnto) - if err != nil { - return nil, err - } - json := pretty.Pretty(data) - if _, err = configFilepath.Write(json); err != nil { - return nil, err - } - return &cbnto, nil -} - -// GetBPMPubHash takes the path to public BPM signing key and hash algorithm -// and returns a hash with hashAlg of pub BPM singing key -func GetBPMPubHash(path string, hashAlg manifest.Algorithm) ([]key.Hash, error) { - var data []byte - pubkey, err := ReadPubKey(path) - if err != nil { - return nil, err - } - hash, err := hashAlg.Hash() - if err != nil { - return nil, err - } - var kAs manifest.Key - if err := kAs.SetPubKey(pubkey); err != nil { - return nil, err - } - k := kAs.Data[4:] - if _, err := hash.Write(k); err != nil { - return nil, err - } - data = hash.Sum(nil) - var keyHashes []key.Hash - hStruc := &manifest.HashStructure{ - HashAlg: manifest.Algorithm(hashAlg), - } - hStruc.HashBuffer = data - - kH := key.Hash{ - Usage: key.UsageBPMSigningPKD, - Digest: *hStruc, - } - keyHashes = append(keyHashes, kH) - return keyHashes, nil -} diff --git a/pkg/provisioning/cbnt/config_test.go b/pkg/provisioning/cbnt/config_test.go deleted file mode 100644 index dc962170..00000000 --- a/pkg/provisioning/cbnt/config_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package cbnt - -import "testing" - -func TestParseConfigValid(T *testing.T) { - -} - -func TestParseConfigInvalid(T *testing.T) { - -} - -func TestSetBPMHeaderValid(T *testing.T) { - -} - -func TestSetBPMHeaderInvalidBadCBnTO(T *testing.T) { - -} - -func TestSetBPMHeaderInvalidBadManifest(T *testing.T) { - -} - -func TestSetIBBSegmentValid(T *testing.T) { - -} - -func TestSetIBBSegmentInvalidCBnTO(T *testing.T) { - -} - -func TestSetIBBSegmentInvalidImage(T *testing.T) { - -} - -func TestTXTElementValid(T *testing.T) { - -} - -func TestTXTElementInvalidBadCBnTO(T *testing.T) { - -} - -func TestSetPCDElementValid(T *testing.T) { - -} - -func TestSetPCDElementInvalidCBnTO(T *testing.T) { - -} - -func TestPMElementValid(T *testing.T) { - -} - -func TestPMElementInvalidCBnTO(T *testing.T) { - -} diff --git a/pkg/provisioning/cbnt/marshal.go b/pkg/provisioning/cbnt/marshal.go deleted file mode 100644 index 411b9df7..00000000 --- a/pkg/provisioning/cbnt/marshal.go +++ /dev/null @@ -1,86 +0,0 @@ -package cbnt - -import ( - "bytes" - "crypto" - "crypto/x509" - "encoding/pem" - "fmt" - - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/bootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/key" -) - -// WriteKM returns a key manifest as bytes in format defined in #575623. -func WriteKM(km *key.Manifest) ([]byte, error) { - buf := new(bytes.Buffer) - _, err := km.WriteTo(buf) - return buf.Bytes(), err -} - -// WriteBPM returns a boot policy manifest as byte slice -func WriteBPM(bpm *bootpolicy.Manifest) ([]byte, error) { - buf := new(bytes.Buffer) - _, err := bpm.WriteTo(buf) - return buf.Bytes(), err -} - -// StitchKM returns a key manifest manifest as byte slice -func StitchKM(km *key.Manifest, pubKey crypto.PublicKey, signature []byte) ([]byte, error) { - if err := km.KeyAndSignature.FillSignature(0, pubKey, signature, km.PubKeyHashAlg); err != nil { - return nil, err - } - km.RehashRecursive() - if err := km.Validate(); err != nil { - return nil, err - } - return WriteKM(km) -} - -// StitchBPM returns a boot policy manifest as byte slice -func StitchBPM(bpm *bootpolicy.Manifest, pubKey crypto.PublicKey, signature []byte) ([]byte, error) { - PMSEString := [8]byte{0x5f, 0x5f, 0x50, 0x4d, 0x53, 0x47, 0x5f, 0x5f} - bpm.PMSE.StructInfo = bootpolicy.StructInfo{} - bpm.PMSE.StructInfo.ID = PMSEString - bpm.PMSE.StructInfo.Version = 0x20 - - if err := bpm.PMSE.KeySignature.FillSignature(0, pubKey, signature, manifest.AlgNull); err != nil { - return nil, err - } - - bpm.RehashRecursive() - if err := bpm.Validate(); err != nil { - return nil, err - } - return WriteBPM(bpm) -} - -func parsePrivateKey(raw []byte) (crypto.Signer, error) { - for { - block, rest := pem.Decode(raw) - if block == nil { - break - } - if block.Type != "CERTIFICATE" { - key, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err == nil { - if key, ok := key.(crypto.Signer); ok { - return key, nil - } - return nil, fmt.Errorf("found unknown private key type (%T) in PKCS#8 wrapping", key) - } - key, err = x509.ParsePKCS1PrivateKey(block.Bytes) - if err == nil { - if key, ok := key.(crypto.Signer); ok { - return key, nil - } - return nil, fmt.Errorf("found unknown private key type (%T) in PKCS#1 wrapping", key) - } - return nil, err - - } - raw = rest - } - return nil, fmt.Errorf("failed to parse private key") -} diff --git a/pkg/provisioning/cbnt/unmarshal.go b/pkg/provisioning/cbnt/unmarshal.go deleted file mode 100644 index 3a24ab61..00000000 --- a/pkg/provisioning/cbnt/unmarshal.go +++ /dev/null @@ -1,55 +0,0 @@ -package cbnt - -import ( - "errors" - "fmt" - "io" - - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/bootpolicy" - "github.com/linuxboot/fiano/pkg/intel/metadata/manifest/key" -) - -// ParseBPM reads from a binary and parses into the boot policy manifest structure -func ParseBPM(reader io.Reader) (*bootpolicy.Manifest, error) { - bpm := &bootpolicy.Manifest{} - _, err := bpm.ReadFrom(reader) - if err != nil && !errors.Is(err, io.EOF) { - return nil, err - } - return bpm, nil -} - -// ValidateBPM reads from a binary, parses into the boot policy manifest structure -// and validates the structure -func ValidateBPM(reader io.Reader) error { - bpm := &bootpolicy.Manifest{} - _, err := bpm.ReadFrom(reader) - if err != nil && !errors.Is(err, io.EOF) { - return err - } - return bpm.Validate() -} - -// ParseKM reads from a binary source and parses into the key manifest structure -func ParseKM(reader io.Reader) (*key.Manifest, error) { - km := &key.Manifest{} - _, err := km.ReadFrom(reader) - if err != nil && !errors.Is(err, io.EOF) { - return nil, err - } - return km, nil -} - -// ValidateKM reads from a binary source, parses into the key manifest structure -// and validates the structure -func ValidateKM(reader io.Reader) error { - km := &key.Manifest{} - _, err := km.ReadFrom(reader) - if err != nil && !errors.Is(err, io.EOF) { - return err - } - if km.PubKeyHashAlg != km.KeyAndSignature.Signature.HashAlg { - return fmt.Errorf("header pubkey hash algorithm doesn't match signature hash") - } - return km.Validate() -} diff --git a/pkg/test/fit.go b/pkg/test/fit.go index 96324d8a..f101f342 100644 --- a/pkg/test/fit.go +++ b/pkg/test/fit.go @@ -506,22 +506,19 @@ func PolicyAllowsTXT(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Conf // BIOSACMValid checks if BIOS ACM is valid func BIOSACMValid(txtAPI hwapi.LowLevelHardwareInterfaces, _ *tools.Configuration) (bool, error, error) { - acm, _, _, _, err, internalerr := biosACM(txtAPI, fitHeaders) + acm, err := biosACM(txtAPI, fitHeaders) - return acm != nil, err, internalerr + return acm != nil, err, nil } // BIOSACMSizeCorrect checks if BIOS ACM size is correct func BIOSACMSizeCorrect(txtAPI hwapi.LowLevelHardwareInterfaces, _ *tools.Configuration) (bool, error, error) { - acm, _, _, _, err, internalerr := biosACM(txtAPI, fitHeaders) - if internalerr != nil { - return false, nil, internalerr - } + acm, err := biosACM(txtAPI, fitHeaders) if err != nil { return false, err, nil } - if acm.Header.Size%64 != 0 { + if acm.Header.GetSize().Size()%64 != 0 { return false, fmt.Errorf("BIOSACM Size is not correct "), nil } return true, nil, nil @@ -539,18 +536,19 @@ func BIOSACMAlignmentCorrect(txtAPI hwapi.LowLevelHardwareInterfaces, _ *tools.C return false, nil, fmt.Errorf("ReadPhysBuf failed at %v with error: %v", hdr.Address.Pointer(), err) } - acm, err := tools.ParseACMHeader(buf1) + r := bytes.NewReader(buf1) + acm, err := tools.ParseACM(r) if err != nil { return false, nil, fmt.Errorf("can't Parse BIOS ACM header correctly") } - ret, err := tools.ValidateACMHeader(acm) + ret, err := acm.ValidateACMHeader() if !ret { return false, nil, fmt.Errorf("validating BIOS ACM Header failed: %v", err) } - size := uint64(math.Pow(2, math.Ceil(math.Log(float64(acm.Size*4))/math.Log(2)))) + size := uint64(math.Pow(2, math.Ceil(math.Log(float64(acm.Header.GetSize().Size()))/math.Log(2)))) if hdr.Address.Pointer()&(size-1) > 0 { return false, fmt.Errorf("BIOSACM not aligned at %x", size), nil } @@ -562,10 +560,7 @@ func BIOSACMAlignmentCorrect(txtAPI hwapi.LowLevelHardwareInterfaces, _ *tools.C // BIOSACMMatchesChipset checks if BIOS ACM matches chipset func BIOSACMMatchesChipset(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Configuration) (bool, error, error) { - _, chp, _, _, err, internalerr := biosACM(txtAPI, fitHeaders) - if internalerr != nil { - return false, nil, internalerr - } + acm, err := biosACM(txtAPI, fitHeaders) if err != nil { return false, err, nil } @@ -578,7 +573,7 @@ func BIOSACMMatchesChipset(txtAPI hwapi.LowLevelHardwareInterfaces, config *tool return false, nil, err } - for _, ch := range chp.IDList { + for _, ch := range acm.Chipsets.IDList { a := ch.VendorID == txt.Vid b := ch.DeviceID == txt.Did @@ -600,10 +595,7 @@ func BIOSACMMatchesChipset(txtAPI hwapi.LowLevelHardwareInterfaces, config *tool // BIOSACMMatchesCPU checks if BIOS ACM matches CPU func BIOSACMMatchesCPU(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Configuration) (bool, error, error) { - _, _, cpus, _, err, internalerr := biosACM(txtAPI, fitHeaders) - if internalerr != nil { - return false, nil, internalerr - } + acm, err := biosACM(txtAPI, fitHeaders) if err != nil { return false, err, nil } @@ -616,7 +608,7 @@ func BIOSACMMatchesCPU(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Co fms := txtAPI.CPUSignature() - for _, cpu := range cpus.IDList { + for _, cpu := range acm.Processors.IDList { a := fms&cpu.FMSMask == cpu.FMS b := platform&cpu.PlatformMask == cpu.PlatformID @@ -628,7 +620,7 @@ func BIOSACMMatchesCPU(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Co return false, fmt.Errorf("BIOS Startup Module and CPU doesn't match"), nil } -func biosACM(txtAPI hwapi.LowLevelHardwareInterfaces, fitHeaders fit.Table) (*tools.ACM, *tools.Chipsets, *tools.Processors, *tools.TPMs, error, error) { +func biosACM(txtAPI hwapi.LowLevelHardwareInterfaces, fitHeaders fit.Table) (*tools.ACM, error) { for _, hdr := range fitHeaders { if hdr.Type() == fit.EntryTypeStartupACModuleEntry { buf1 := make([]byte, tools.ACMheaderLen*4) @@ -636,44 +628,42 @@ func biosACM(txtAPI hwapi.LowLevelHardwareInterfaces, fitHeaders fit.Table) (*to err := txtAPI.ReadPhysBuf(int64(hdr.Address), buf1) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("ReadPhysBuf failed at %v with error: %v", hdr.Address, err) + return nil, fmt.Errorf("ReadPhysBuf failed at %v with error: %v", hdr.Address, err) } - - acm, err := tools.ParseACMHeader(buf1) + r := bytes.NewReader(buf1) + acm, err := tools.ParseACM(r) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("cannot Parse BIOS ACM header correctly"), nil + return nil, fmt.Errorf("cannot Parse BIOS ACM header correctly: %v", err) } - ret, err := tools.ValidateACMHeader(acm) + ret, err := acm.ValidateACMHeader() if !ret { - return nil, nil, nil, nil, fmt.Errorf("validating BIOS ACM Header failed: %v", err), nil + return nil, fmt.Errorf("validating BIOS ACM Header failed: %v", err) } - buf2 := make([]byte, acm.Size*4) + buf2 := make([]byte, acm.Header.GetSize().Size()) err = txtAPI.ReadPhysBuf(int64(hdr.Address), buf2) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("cannot read BIOS ACM completly") + return nil, fmt.Errorf("cannot read BIOS ACM completly: %v", err) } - return tools.ParseACM(buf2) + r2 := bytes.NewReader(buf2) + return tools.ParseACM(r2) } } - return nil, nil, nil, nil, fmt.Errorf("no BIOS ACM in FIT"), nil + return nil, fmt.Errorf("no BIOS ACM in FIT") } // SINITandBIOSACMnoNPW checks that in BIOS integrated ACMs (SINIT, BIOS) are production worthy func SINITandBIOSACMnoNPW(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Configuration) (bool, error, error) { - biosACMs, _, _, _, err, internalerr := biosACM(txtAPI, fitHeaders) - if internalerr != nil { - return false, nil, internalerr - } + biosACMs, err := biosACM(txtAPI, fitHeaders) if err != nil { return false, err, nil } - biosACMFlags := biosACMs.Header.ParseACMFlags() + biosACMFlags := biosACMs.ParseACMFlags() if biosACMFlags.PreProduction || biosACMFlags.DebugSigned { return false, fmt.Errorf("BIOS ACM is either debug signed or NPW"), nil } @@ -685,14 +675,11 @@ func SINITandBIOSACMnoNPW(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools if err != nil { return false, nil, err } - sinitACMs, _, _, _, err, internalerr := sinitACM(txtAPI, regs) - if internalerr != nil { - return false, nil, internalerr - } + sinitACMs, err := sinitACM(txtAPI, regs) if err != nil { return false, err, nil } - sinitACMFlags := sinitACMs.Header.ParseACMFlags() + sinitACMFlags := sinitACMs.ParseACMFlags() if sinitACMFlags.PreProduction || sinitACMFlags.DebugSigned { return false, fmt.Errorf("SINIT ACM is either debug signed or NPW"), nil } @@ -709,18 +696,15 @@ func SINITACMcomplyTPMSpec(txtAPI hwapi.LowLevelHardwareInterfaces, config *tool if err != nil { return false, nil, err } - _, _, _, tpms, err, internalerr := sinitACM(txtAPI, regs) - if internalerr != nil { - return false, nil, internalerr - } + acm, err := sinitACM(txtAPI, regs) if err != nil { return false, err, nil } - res := (1 >> tpms.Capabilities & (uint32(tools.TPMFamilyDTPM12) | uint32(tools.TPMFamilyDTPMBoth))) + res := (1 >> acm.TPMs.Capabilities & (uint32(tools.TPMFamilyDTPM12) | uint32(tools.TPMFamilyDTPMBoth))) if res == 0 && config.TPM == hwapi.TPMVersion12 && testtpmispresent.Result == ResultPass { return true, nil, nil } - res = (1 >> tpms.Capabilities & (uint32(tools.TPMFamilyDTPM20) | uint32(tools.TPMFamilyDTPMBoth))) + res = (1 >> acm.TPMs.Capabilities & (uint32(tools.TPMFamilyDTPM20) | uint32(tools.TPMFamilyDTPMBoth))) if res == 0 && config.TPM == hwapi.TPMVersion20 && testtpmispresent.Result == ResultPass { return true, nil, nil } diff --git a/pkg/test/memory.go b/pkg/test/memory.go index ec974101..db4ef47d 100644 --- a/pkg/test/memory.go +++ b/pkg/test/memory.go @@ -1,6 +1,7 @@ package test import ( + "bytes" "fmt" "github.com/9elements/converged-security-suite/v2/pkg/tools" @@ -232,7 +233,7 @@ var ( biosdata tools.TXTBiosData ) -//nolint +// nolint const ( //Heapsize from newer spec - Document 575623 minHeapSize = uint32(0xF0000) @@ -489,25 +490,11 @@ func SINITInTXT(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Configura if err != nil { return false, nil, err } - - sinitBuf := make([]byte, regs.SinitSize) - err = txtAPI.ReadPhysBuf(int64(regs.SinitBase), sinitBuf) - if err != nil { - return false, nil, err - } - - acm, _, _, _, err, internalerr := tools.ParseACM(sinitBuf) - if internalerr != nil { - return false, nil, internalerr - } + acm, err := sinitACM(txtAPI, regs) if err != nil { return false, err, nil } - if acm == nil { - return false, fmt.Errorf("ACM is nil"), nil - } - - if acm.Header.ModuleType != 2 { + if acm.Header.GetModuleType() != 2 { return false, fmt.Errorf("SINIT in TXT: ACM ModuleType not 2"), nil } return true, nil, nil @@ -524,24 +511,16 @@ func SINITMatchesChipset(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools. if err != nil { return false, nil, err } - - acm, chps, _, _, err, internalerr := sinitACM(txtAPI, regs) - if internalerr != nil { - return false, nil, internalerr - } + acm, err := sinitACM(txtAPI, regs) if err != nil { return false, err, nil } - if chps == nil { - return false, fmt.Errorf("CHPS is nil"), nil - } - - for _, ch := range chps.IDList { + for _, ch := range acm.Chipsets.IDList { a := ch.VendorID == regs.Vid b := ch.DeviceID == regs.Did if a && b { - if acm.Header.Flags&1 != 0 { + if acm.Header.GetFlags()&1 != 0 { if ch.RevisionID®s.Rid == regs.Rid { return true, nil, nil } @@ -567,10 +546,7 @@ func SINITMatchesCPU(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Conf return false, nil, err } - _, _, cpus, _, err, internalerr := sinitACM(txtAPI, regs) - if internalerr != nil { - return false, nil, internalerr - } + acm, err := sinitACM(txtAPI, regs) if err != nil { return false, err, nil } @@ -583,7 +559,7 @@ func SINITMatchesCPU(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Conf fms := txtAPI.CPUSignature() - for _, cpu := range cpus.IDList { + for _, cpu := range acm.Processors.IDList { a := fms&cpu.FMSMask == cpu.FMS b := platform&cpu.PlatformMask == cpu.PlatformID @@ -758,17 +734,24 @@ func ServerModeTXT(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Config return false, fmt.Errorf("servermode not active"), nil } -//ReleaseFusedFSBI checks if the FSBI is release fused +// ReleaseFusedFSBI checks if the FSBI is release fused func ReleaseFusedFSBI(txtAPI hwapi.LowLevelHardwareInterfaces, config *tools.Configuration) (bool, error, error) { return false, nil, fmt.Errorf("ReleaseFusedFSBI: Unimplemented") } -func sinitACM(txtAPI hwapi.LowLevelHardwareInterfaces, regs tools.TXTRegisterSpace) (*tools.ACM, *tools.Chipsets, *tools.Processors, *tools.TPMs, error, error) { +func sinitACM(txtAPI hwapi.LowLevelHardwareInterfaces, regs tools.TXTRegisterSpace) (*tools.ACM, error) { sinitBuf := make([]byte, regs.SinitSize) err := txtAPI.ReadPhysBuf(int64(regs.SinitBase), sinitBuf) if err != nil { - return nil, nil, nil, nil, nil, err + return nil, err } - - return tools.ParseACM(sinitBuf) + r := bytes.NewReader(sinitBuf) + acm, err := tools.ParseACM(r) + if err != nil { + return nil, err + } + if acm == nil { + return nil, fmt.Errorf("ACM is nil") + } + return tools.ParseACM(r) } diff --git a/pkg/tools/acm.go b/pkg/tools/acm.go index 37f8b4b3..5cc1a75c 100644 --- a/pkg/tools/acm.go +++ b/pkg/tools/acm.go @@ -7,6 +7,7 @@ import ( "io" "github.com/google/go-tpm/tpm2" + "github.com/linuxboot/fiano/pkg/intel/metadata/fit" ) const ( @@ -19,11 +20,11 @@ const ( //ACMChipsetTypeSinitRevoc as defined in Document 315168-016 Chapter A.1 Table 10. Chipset AC Module Information Table ACMChipsetTypeSinitRevoc uint8 = 0x09 //ACMTypeChipset as defined in Document 315168-016 Chapter A.1 Table 8. Authenticated Code Module Format - ACMTypeChipset uint16 = 0x02 + ACMTypeChipset fit.ACModuleType = 0x02 //ACMSubTypeReset FIXME ACMSubTypeReset uint16 = 0x01 //ACMVendorIntel as defined in Document 315168-016 Chapter A.1 Table 8. Authenticated Code Module Format - ACMVendorIntel uint32 = 0x8086 + ACMVendorIntel fit.ACModuleVendor = 0x8086 //TPMExtPolicyIllegal as defined in Document 315168-016 Chapter A.1 Table 16. TPM Capabilities Field TPMExtPolicyIllegal uint8 = 0x00 @@ -54,14 +55,14 @@ const ( ACMheaderLen uint32 = 161 //ACMModuleSubtypeSinitACM is an enum - ACMModuleSubtypeSinitACM uint16 = 0 + ACMModuleSubtypeSinitACM fit.ACModuleSubType = 0 //ACMModuleSubtypeCapableOfExecuteAtReset is a flag and enum Based on EDK2 Silicon/Intel/Tools/FitGen/FitGen.c - ACMModuleSubtypeCapableOfExecuteAtReset uint16 = 1 + ACMModuleSubtypeCapableOfExecuteAtReset fit.ACModuleSubType = 1 //ACMModuleSubtypeAncModule is a flag Based on EDK2 Silicon/Intel/Tools/FitGen/FitGen.c - ACMModuleSubtypeAncModule uint16 = 2 + ACMModuleSubtypeAncModule fit.ACModuleSubType = 2 ) -//UUID represents an UUID +// UUID represents an UUID type UUID struct { Field1 uint32 Field2 uint16 @@ -94,7 +95,7 @@ type ACMInfo struct { TPMInfoList uint32 } -//ChipsetID describes the chipset ID found in the ACM header +// ChipsetID describes the chipset ID found in the ACM header type ChipsetID struct { Flags uint32 VendorID uint16 @@ -103,13 +104,13 @@ type ChipsetID struct { Reserved [3]uint16 } -//Chipsets hold a list of supported chipset IDs as found in the ACM header +// Chipsets hold a list of supported chipset IDs as found in the ACM header type Chipsets struct { Count uint32 IDList []ChipsetID } -//ProcessorID describes the processor ID found in the ACM header +// ProcessorID describes the processor ID found in the ACM header type ProcessorID struct { FMS uint32 FMSMask uint32 @@ -117,7 +118,7 @@ type ProcessorID struct { PlatformMask uint64 } -//Processors hold a list of supported processor IDs as found in the ACM header +// Processors hold a list of supported processor IDs as found in the ACM header type Processors struct { Count uint32 IDList []ProcessorID @@ -130,40 +131,6 @@ type TPMs struct { AlgID []tpm2.Algorithm } -// ACMHeader exports the structure of ACM Header found in the firmware interface table -type ACMHeader struct { - ModuleType uint16 - ModuleSubType uint16 - HeaderLen uint32 - HeaderVersion uint32 - ChipsetID uint16 - Flags uint16 - ModuleVendor uint32 - Date uint32 - Size uint32 - TxtSVN uint16 - SeSVN uint16 - CodeControl uint32 - ErrorEntryPoint uint32 - GDTLimit uint32 - GDTBase uint32 - SegSel uint32 - EntryPoint uint32 - Reserved2 [64]uint8 - KeySize uint32 - ScratchSize uint32 - PubKey [256]uint8 - PubExp uint32 - Signature [256]uint8 -} - -// ACM exports the structure of Authenticated Code Modules found in the Firmware Interface Table(FIT) -type ACM struct { - Header ACMHeader - Scratch []byte - Info ACMInfo -} - // ACMFlags exports the ACM header flags type ACMFlags struct { Production bool @@ -171,141 +138,122 @@ type ACMFlags struct { DebugSigned bool } -// ParseACMHeader exports the functionality of parsing an ACM Header -func ParseACMHeader(data []byte) (*ACMHeader, error) { - var acm ACMHeader - buf := bytes.NewReader(data) - err := binary.Read(buf, binary.LittleEndian, &acm) - - if err != nil { - return nil, fmt.Errorf("can't read ACM Header") - } - - return &acm, nil +type ACM struct { + Header *fit.EntrySACMData + Info ACMInfo + Chipsets Chipsets + Processors Processors + TPMs TPMs } // ValidateACMHeader validates an ACM Header found in the Firmware Interface Table (FIT) -func ValidateACMHeader(acmheader *ACMHeader) (bool, error) { - if acmheader.ModuleType != uint16(2) { +func (a *ACM) ValidateACMHeader() (bool, error) { + if a.Header == nil { + return false, fmt.Errorf("ACM structure not available, input leads to parser error") + } + if uint16(a.Header.GetModuleType()) != uint16(2) { return false, fmt.Errorf("BIOS ACM ModuleType is not 2, this is not specified") } // Early version of TXT used an enum in ModuleSubType // That was changed to flags. Check if unsupported flags are present - if acmheader.ModuleSubType > (ACMModuleSubtypeAncModule | ACMModuleSubtypeCapableOfExecuteAtReset) { + if a.Header.GetModuleSubType() > (ACMModuleSubtypeAncModule | ACMModuleSubtypeCapableOfExecuteAtReset) { return false, fmt.Errorf("BIOS ACM ModuleSubType contains unknown flags") } - if acmheader.HeaderLen < uint32(ACMheaderLen) { + if uint32(a.Header.GetHeaderLen()) < uint32(ACMheaderLen) { return false, fmt.Errorf("BIOS ACM HeaderLength is smaller than 4*161 Byte") } - if acmheader.Size == 0 { + if a.Header.GetSize().Size() == 0 { return false, fmt.Errorf("BIOS ACM Size can't be zero") } - if acmheader.ModuleVendor != ACMVendorIntel { + if a.Header.GetModuleVendor() != ACMVendorIntel { return false, fmt.Errorf("AC Module Vendor is not Intel. Only Intel as Vendor is allowed") } - if acmheader.KeySize*4 != uint32(len(acmheader.PubKey)) { - return false, fmt.Errorf("ACM keysize of 0x%x not supported yet", acmheader.KeySize*4) - } - if acmheader.ScratchSize > acmheader.Size { + if a.Header.GetScratchSize() > a.Header.GetSize() { return false, fmt.Errorf("ACM ScratchSize is bigger than ACM module size") } return true, nil } -//ParseACM deconstructs a byte array containing the raw ACM into it's components -func ParseACM(data []byte) (*ACM, *Chipsets, *Processors, *TPMs, error, error) { - var acmheader ACMHeader - var acminfo ACMInfo - var processors Processors - var chipsets Chipsets - var tpms TPMs - - buf := bytes.NewReader(data) - err := binary.Read(buf, binary.LittleEndian, &acmheader) +func (a *ACM) ParseACMInfo() error { + userArea := bytes.NewReader(a.Header.UserArea) + if err := binary.Read(userArea, binary.LittleEndian, &a.Info); err != nil { + return err + } + totalACM := make([]byte, a.Header.GetSize().Size()) + _, err := a.Header.Write(totalACM) if err != nil { - return nil, nil, nil, nil, nil, err + return err } - scratch := make([]byte, acmheader.ScratchSize*4) - - err = binary.Read(buf, binary.LittleEndian, &scratch) + buf := bytes.NewReader(totalACM) + _, err = buf.Seek(int64(a.Info.ChipsetIDList), io.SeekStart) if err != nil { - return nil, nil, nil, nil, nil, err + return fmt.Errorf("unable to seek: %w", err) } - - if (acmheader.ModuleSubType & ACMModuleSubtypeAncModule) > 0 { - // ANC modules do not have an ACMINFO header - acm := ACM{acmheader, scratch, acminfo} - return &acm, &chipsets, &processors, &tpms, nil, nil + if err := binary.Read(buf, binary.LittleEndian, &a.Chipsets.Count); err != nil { + return err } - - err = binary.Read(buf, binary.LittleEndian, &acminfo) - if err != nil { - return nil, nil, nil, nil, nil, err + a.Chipsets.IDList = make([]ChipsetID, a.Chipsets.Count) + if err := binary.Read(buf, binary.LittleEndian, &a.Chipsets.IDList); err != nil { + return err } - - acm := ACM{acmheader, scratch, acminfo} - - _, err = buf.Seek(int64(acm.Info.ChipsetIDList), io.SeekStart) + _, err = buf.Seek(int64(a.Info.ProcessorIDList), io.SeekStart) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("unable to seek: %w", err) + return fmt.Errorf("unable to seek: %w", err) } - err = binary.Read(buf, binary.LittleEndian, &chipsets.Count) - if err != nil { - return nil, nil, nil, nil, nil, err + if err := binary.Read(buf, binary.LittleEndian, &a.Processors.Count); err != nil { + return err } - - chipsets.IDList = make([]ChipsetID, chipsets.Count) - err = binary.Read(buf, binary.LittleEndian, &chipsets.IDList) + a.Processors.IDList = make([]ProcessorID, a.Processors.Count) + if err := binary.Read(buf, binary.LittleEndian, &a.Processors.IDList); err != nil { + return err + } + _, err = buf.Seek(int64(a.Info.TPMInfoList), io.SeekStart) if err != nil { - return nil, nil, nil, nil, nil, err + return fmt.Errorf("unable to seek: %w", err) + } + if err := binary.Read(buf, binary.LittleEndian, &a.TPMs.Capabilities); err != nil { + return err } + if err := binary.Read(buf, binary.LittleEndian, &a.TPMs.Count); err != nil { + return err + } + a.TPMs.AlgID = make([]tpm2.Algorithm, a.TPMs.Count) + if err := binary.Read(buf, binary.LittleEndian, &a.TPMs.AlgID); err != nil { + return err + } + return nil +} - _, err = buf.Seek(int64(acm.Info.ProcessorIDList), io.SeekStart) +// ParseACM deconstructs a byte array containing the raw ACM into it's components +func ParseACM(r io.Reader) (*ACM, error) { + var acm ACM + var err error + acm.Header, err = fit.ParseSACMData(r) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("unable to seek: %w", err) + return nil, err } - err = binary.Read(buf, binary.LittleEndian, &processors.Count) - if err != nil { - return nil, nil, nil, nil, nil, err + if (acm.Header.GetModuleSubType() & ACMModuleSubtypeAncModule) > 0 { + // ANC modules do not have an ACMINFO header + return &acm, nil } + if err := acm.ParseACMInfo(); err != nil { + return nil, err + } + return &acm, nil +} - processors.IDList = make([]ProcessorID, processors.Count) - err = binary.Read(buf, binary.LittleEndian, &processors.IDList) - if err != nil { - return nil, nil, nil, nil, nil, err - } - - if acm.Info.ACMVersion >= 5 { - _, err = buf.Seek(int64(acm.Info.TPMInfoList), io.SeekStart) - if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("unable to seek: %w", err) - } - err = binary.Read(buf, binary.LittleEndian, &tpms.Capabilities) - if err != nil { - return nil, nil, nil, nil, nil, err - } - - err = binary.Read(buf, binary.LittleEndian, &tpms.Count) - if err != nil { - return nil, nil, nil, nil, nil, err - } - - tpms.AlgID = make([]tpm2.Algorithm, tpms.Count) - for i := 0; i < int(tpms.Count); i++ { - err = binary.Read(buf, binary.LittleEndian, &tpms.AlgID[i]) - if err != nil { - return nil, nil, nil, nil, nil, err - } - } - } - - return &acm, &chipsets, &processors, &tpms, nil, nil +// ParseACMFlags parses the ACM Header flags +func (a *ACM) ParseACMFlags() *ACMFlags { + var flags ACMFlags + raw := a.Header.GetFlags() + flags.Production = (raw>>15)&1 == 0 && (raw>>14)&1 == 0 + flags.PreProduction = (raw>>14)&1 != 0 + flags.DebugSigned = (raw>>15)&1 != 0 + return &flags } -//LookupACMSize returns the ACM size func LookupACMSize(header []byte) (int64, error) { var acmSize uint32 - buf := bytes.NewReader(header[:32]) _, err := buf.Seek(ACMSizeOffset, io.SeekStart) if err != nil { @@ -315,102 +263,54 @@ func LookupACMSize(header []byte) (int64, error) { if err != nil { return 0, err } - return int64(acmSize * 4), nil } -// ParseACMFlags parses the ACM Header flags -func (a *ACMHeader) ParseACMFlags() *ACMFlags { - var flags ACMFlags - flags.Production = (a.Flags>>15)&1 == 0 && (a.Flags>>14)&1 == 0 - flags.PreProduction = (a.Flags>>14)&1 != 0 - flags.DebugSigned = (a.Flags>>15)&1 != 0 - return &flags -} - -//PrettyPrint prints a human readable representation of the ACMHeader -func (a *ACMHeader) PrettyPrint() { +// PrettyPrint prints a human readable representation of the ACMHeader +func (a *ACM) PrettyPrintHeader() { fmt.Println("----Authenticated Code Module----") fmt.Println() - if a.ModuleVendor == ACMVendorIntel { + if a.Header.GetModuleVendor() == ACMVendorIntel { fmt.Println(" Module Vendor: Intel") } else { fmt.Println(" Module Vendor: Unknown") } - if a.ModuleType == ACMTypeChipset { + if a.Header.GetModuleType() == ACMTypeChipset { fmt.Println(" Module Type: ACM_TYPE_CHIPSET") } else { fmt.Println(" Module Type: UNKNOWN") } - if a.ModuleSubType == ACMSubTypeReset { + if uint16(a.Header.GetModuleSubType()) == ACMSubTypeReset { fmt.Println(" Module Subtype: Execute at Reset") - } else if a.ModuleSubType == 0 { + } else if uint16(a.Header.GetModuleSubType()) == 0 { fmt.Println(" Module Subtype: 0x0") } else { fmt.Println(" Module Subtype: Unknown") } - fmt.Printf(" Module Date: 0x%02x\n", a.Date) - fmt.Printf(" Module Size: 0x%x (%d)\n", a.Size*4, a.Size*4) - - fmt.Printf(" Header Length: 0x%x (%d)\n", a.HeaderLen, a.HeaderLen) - fmt.Printf(" Header Version: %d\n", a.HeaderVersion) - fmt.Printf(" Chipset ID: 0x%02x\n", a.ChipsetID) - fmt.Printf(" Flags: 0x%02x\n", a.Flags) - fmt.Printf(" TXT SVN: 0x%08x\n", a.TxtSVN) - fmt.Printf(" SE SVN: 0x%08x\n", a.SeSVN) - fmt.Printf(" Code Control: 0x%02x\n", a.CodeControl) - fmt.Printf(" Entry Point: 0x%08x:%08x\n", a.SegSel, a.EntryPoint) - fmt.Printf(" Scratch Size: 0x%x (%d)\n", a.ScratchSize, a.ScratchSize) -} - -//PrettyPrint prints a human readable representation of the ACM -func (a *ACM) PrettyPrint() { - a.Header.PrettyPrint() - fmt.Println(" --Info Table--") - - uuidStr := fmt.Sprintf("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", - a.Info.UUID.Field1, - a.Info.UUID.Field2, - a.Info.UUID.Field3, - a.Info.UUID.Field4, - a.Info.UUID.Field5[0], - a.Info.UUID.Field5[1], - a.Info.UUID.Field5[2], - a.Info.UUID.Field5[3], - a.Info.UUID.Field5[4], - a.Info.UUID.Field5[5]) - - if uuidStr == ACMUUIDV3 { - fmt.Println(" UUID: ACM_UUID_V3") - } - - switch a.Info.ChipsetACMType { - case ACMChipsetTypeBios: - fmt.Println(" Chipset ACM: BIOS") - case ACMChipsetTypeBiosRevoc: - fmt.Println(" Chipset ACM: BIOS Revocation") - case ACMChipsetTypeSinit: - fmt.Println(" Chipset ACM: SINIT") - case ACMChipsetTypeSinitRevoc: - fmt.Println(" Chipset ACM: SINIT Revocation") - default: - fmt.Println(" Chipset ACM: Unknown") - } - - fmt.Printf(" Version: %d\n", a.Info.Version) - fmt.Printf(" Length: 0x%x (%d)\n", a.Info.Length, a.Info.Length) - fmt.Printf(" Chipset ID List: 0x%02x\n", a.Info.ChipsetIDList) - fmt.Printf(" OS SINIT Data Version: 0x%02x\n", a.Info.OSSinitDataVersion) - fmt.Printf(" Min. MLE Header Version: 0x%08x\n", a.Info.MinMleHeaderVersion) - fmt.Printf(" Capabilities: 0x%08x\n", a.Info.TxtCaps) - fmt.Printf(" ACM Version: %d\n", a.Info.ACMVersion) - fmt.Printf(" ACM Revision: %s\n", a.Info.ACMRevision) + flags := a.ParseACMFlags() + fmt.Println(" Flags:") + fmt.Printf(" Production: %t\n", flags.Production) + fmt.Printf(" Pre-Production: %t\n", flags.PreProduction) + fmt.Printf(" Debug Signed: %t\n", flags.DebugSigned) + fmt.Printf(" Module Date: 0x%02x\n", a.Header.GetDate()) + fmt.Printf(" Module Size: 0x%x (%d)\n", a.Header.GetSize().Size(), a.Header.GetSize().Size()) + + fmt.Printf(" Header Length: 0x%x (%d)\n", a.Header.GetHeaderLen(), a.Header.GetHeaderLen()) + fmt.Printf(" Header Version: %d\n", a.Header.GetHeaderVersion()) + fmt.Printf(" Chipset ID: 0x%02x\n", a.Header.GetChipsetID()) + fmt.Printf(" Flags: 0x%02x\n", a.Header.GetFlags()) + fmt.Printf(" TXT SVN: 0x%08x\n", a.Header.GetTXTSVN()) + fmt.Printf(" SE SVN: 0x%08x\n", a.Header.GetSESVN()) + fmt.Printf(" Code Control: 0x%02x\n", a.Header.GetCodeControl()) + fmt.Printf(" Entry Point: 0x%08x:%08x\n", a.Header.GetSegSel(), a.Header.GetEntryPoint()) + fmt.Printf(" Scratch Size: 0x%x (%d)\n", a.Header.GetScratchSize(), a.Header.GetScratchSize()) + fmt.Println() } -//PrettyPrint prints a human readable representation of the Chipsets -func (c *Chipsets) PrettyPrint() { +// PrettyPrint prints a human readable representation of the Chipsets +func (c Chipsets) PrettyPrint() { fmt.Println(" --Chipset List--") fmt.Printf(" Entries: %d\n", c.Count) for idx, chipset := range c.IDList { @@ -420,10 +320,11 @@ func (c *Chipsets) PrettyPrint() { fmt.Printf(" Device: 0x%02x\n", chipset.DeviceID) fmt.Printf(" Revision: 0x%02x\n", chipset.RevisionID) } + fmt.Println() } -//PrettyPrint prints a human readable representation of the Processors -func (p *Processors) PrettyPrint() { +// PrettyPrint prints a human readable representation of the Processors +func (p Processors) PrettyPrint() { fmt.Println(" --Processor List--") fmt.Printf(" Entries: %d\n", p.Count) for idx, processor := range p.IDList { @@ -433,10 +334,11 @@ func (p *Processors) PrettyPrint() { fmt.Printf(" Platform ID: 0x%02x\n", processor.PlatformID) fmt.Printf(" Platform Mask: 0x%02x\n", processor.PlatformMask) } + fmt.Println() } -//PrettyPrint prints a human readable representation of the TPMs -func (t *TPMs) PrettyPrint() { +// PrettyPrint prints a human readable representation of the TPMs +func (t TPMs) PrettyPrint() { fmt.Println(" --TPM Info List--") fmt.Println(" Capabilities:") fmt.Printf(" External Policy: %02x\n", t.Capabilities) @@ -444,4 +346,54 @@ func (t *TPMs) PrettyPrint() { for _, algo := range t.AlgID { fmt.Printf(" %v\n", algo.String()) } + fmt.Println() +} + +// PrettyPrint prints a human readable representation of the ACM +func (a *ACM) PrettyPrint() { + a.PrettyPrintHeader() + fmt.Println(" --Info Table--") + switch a.Info.ChipsetACMType { + case ACMChipsetTypeBios: + fmt.Println(" Chipset ACM: BIOS") + case ACMChipsetTypeBiosRevoc: + fmt.Println(" Chipset ACM: BIOS Revocation") + case ACMChipsetTypeSinit: + fmt.Println(" Chipset ACM: SINIT") + case ACMChipsetTypeSinitRevoc: + fmt.Println(" Chipset ACM: SINIT Revocation") + default: + fmt.Println(" Chipset ACM: Unknown") + } + uuidStr := fmt.Sprintf("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + a.Info.UUID.Field1, + a.Info.UUID.Field2, + a.Info.UUID.Field3, + a.Info.UUID.Field4, + a.Info.UUID.Field5[0], + a.Info.UUID.Field5[1], + a.Info.UUID.Field5[2], + a.Info.UUID.Field5[3], + a.Info.UUID.Field5[4], + a.Info.UUID.Field5[5]) + if uuidStr == ACMUUIDV3 { + fmt.Println(" UUID: ACM_UUID_V3") + fmt.Printf(" Version: %d\n", a.Info.Version) + fmt.Printf(" Length: 0x%x (%d)\n", a.Info.Length, a.Info.Length) + fmt.Printf(" Chipset ID List: 0x%02x\n", a.Info.ChipsetIDList) + fmt.Printf(" OS SINIT Data Version: 0x%02x\n", a.Info.OSSinitDataVersion) + fmt.Printf(" Min. MLE Header Version: 0x%08x\n", a.Info.MinMleHeaderVersion) + fmt.Printf(" Capabilities: 0x%08x\n", a.Info.TxtCaps) + fmt.Printf(" ACM Version: %d\n", a.Info.ACMVersion) + fmt.Printf(" ACM Revision: %s\n", a.Info.ACMRevision) + fmt.Printf(" Processor ID List: 0x%02x\n", a.Info.ProcessorIDList) + fmt.Printf(" TPM ID List: 0x%02x\n", a.Info.TPMInfoList) + fmt.Println() + a.Chipsets.PrettyPrint() + a.Processors.PrettyPrint() + a.TPMs.PrettyPrint() + } else { + fmt.Println(" UUID: ACM_UUID_V0") + fmt.Println() + } } diff --git a/pkg/tools/acm_test.go b/pkg/tools/acm_test.go index f8203956..9b88b6c7 100644 --- a/pkg/tools/acm_test.go +++ b/pkg/tools/acm_test.go @@ -6,17 +6,17 @@ import ( ) func TestParseandValidateACMHeader(t *testing.T) { - file, err := os.ReadFile("./tests/sinit_acm.bin") + file, err := os.Open("./tests/sinit_acm.bin") if err != nil { t.Errorf("Failed to read file: %v", err) } - header, err := ParseACMHeader(file) + acm, err := ParseACM(file) if err != nil { t.Errorf("ParseACMHeader() failed: %v", err) } - valid, err := ValidateACMHeader(header) + valid, err := acm.ValidateACMHeader() if err != nil { t.Errorf("ValidateACMHeader() failed: %v", err) } @@ -24,27 +24,19 @@ func TestParseandValidateACMHeader(t *testing.T) { t.Errorf("ValidateACMHeader() failed to validate the ACMHeader") } - header.PrettyPrint() + acm.PrettyPrint() } func TestACMParser(t *testing.T) { - file, err := os.ReadFile("./tests/sinit_acm.bin") + file, err := os.Open("./tests/sinit_acm.bin") if err != nil { t.Errorf("Failed to read file: %v", err) } - - acm, chipsets, processors, tpms, err, internalerr := ParseACM(file) - if internalerr != nil { - t.Errorf("ACMParser() failed with internal error: %v", err) - } + acm, err := ParseACM(file) if err != nil { t.Errorf("ParseACM() failed: %v", err) } - acm.PrettyPrint() - chipsets.PrettyPrint() - processors.PrettyPrint() - tpms.PrettyPrint() } func TestACMSize(t *testing.T) { @@ -52,7 +44,6 @@ func TestACMSize(t *testing.T) { if err != nil { t.Errorf("Failed to read file: %v", err) } - size, err := LookupACMSize(file) if err != nil { t.Errorf("ACMSize() failed: %v", err) @@ -68,23 +59,15 @@ func TestACMSize(t *testing.T) { } func TestACMParser2(t *testing.T) { - file, err := os.ReadFile("./tests/bios_acm.bin") + file, err := os.Open("./tests/bios_acm.bin") if err != nil { t.Errorf("ACMParser() failed: %v", err) } - - acm, chipsets, processors, tpms, err, internalerr := ParseACM(file) - if internalerr != nil { - t.Errorf("ACMParser() failed with internal error: %v", err) - } + acm, err := ParseACM(file) if err != nil { t.Errorf("ACMParser() failed: %v", err) } - acm.PrettyPrint() - chipsets.PrettyPrint() - processors.PrettyPrint() - tpms.PrettyPrint() } func TestACMSize2(t *testing.T) { diff --git a/testdata/firmware/scripts/generate_fake_intel_firmware.sh b/testdata/firmware/scripts/generate_fake_intel_firmware.sh index 56e29c84..5b94b250 100755 --- a/testdata/firmware/scripts/generate_fake_intel_firmware.sh +++ b/testdata/firmware/scripts/generate_fake_intel_firmware.sh @@ -64,7 +64,7 @@ prepareDependencies() { } setDependency UTK github.com/linuxboot/fiano/cmds/utk -setDependency CBNTPROV github.com/9elements/converged-security-suite/v2/cmd/cbnt-prov +setDependency BGPROV github.com/9elements/converged-security-suite/v2/cmd/bg-prov setDependency PCR0TOOL github.com/9elements/converged-security-suite/v2/cmd/pcr0tool setDependency FITTOOL github.com/linuxboot/fiano/cmds/fittool @@ -172,7 +172,7 @@ openssl rsa -in "$ODMPRIVKEYPATH" -pubout -out "$ODMPUBKEYPATH" 2>/dev/null # Create a dummy ACM DUMMYACMPATH="$(mktemp)" -"$CBNTPROV" acm-gen "$DUMMYACMPATH" --moduletype 2 --sesvn 1 --txtsvn 2 --date $((16#11223344)) --size 2048 +"$BGPROV" acm-gen-v3 "$DUMMYACMPATH" --moduletype 2 --sesvn 1 --txtsvn 2 --date $((16#11223344)) --size 2048 # Inject the dummy ACM (FIT entry type 0x02) at offset 20K. # In real images ACM is usually pleaced between PEI and DXE, so we use offset 20K. @@ -183,14 +183,14 @@ rm -f "$DUMMYACMPATH" # Create a KM. # # KM uses an OEM key to authorize an ODM key to issue a signed BPM. So `km-gen` will calculate -# a hash of the ODM (BPM) pubkey and store it inside KM, in turn KM is signed by the OEM (KM) key. +# a hash of the ODM (BPM) pubkey and store it inside KM, in turn KM is signed by the OEM (KM) cbntkey. # # Algorithm "SHA256" was picked arbitrary. One may use another algorithm. KMPATH="$(mktemp)" # Generated an unsigned KM -"$CBNTPROV" km-gen "$KMPATH" "$OEMPUBKEYPATH" --bpmpubkey "$ODMPUBKEYPATH" --pkhashalg=SHA256 --bpmhashalgo SHA256 +"$BGPROV" km-gen-v-2 "$KMPATH" "$OEMPUBKEYPATH" --bpmpubkey "$ODMPUBKEYPATH" --pkhashalg=SHA256 --bpmhashalgo SHA256 # Sign the KM (with no password on the private key file) -"$CBNTPROV" km-sign "$KMPATH" "$KMPATH" "$OEMPRIVKEYPATH" RSASSA "" +"$BGPROV" km-sign "$KMPATH" "$KMPATH" "$OEMPRIVKEYPATH" RSASSA "" # Inject the KM reference to FIT (FIT entry type 0x0B), by referencing it as at the offset 21K "$FITTOOL" add_raw_headers -f "$OUTPUT_FILE" --address-offset $(( 21 * 1024 )) --size "$(stat -c %s "$KMPATH")" --type $((16#B)) @@ -207,15 +207,15 @@ rm -f "$KMPATH" # BPM is signed by an ODM key, which is trusted, because its hash is included into KM, which is # signed by the OEM key (which we trust, because OEM is us). BPMPATH="$(mktemp)" -# `$CBNTPROV bpm-gen` (to generate an BPM) depends on scanning the FIT table of +# `$BGPROV bpm-gen` (to generate an BPM) depends on scanning the FIT table of # the image to find BIOS startup modules and include them as IBBs. # So first we need to add at least one entry. We will use a part of the dummy PEI # as an IBB section (similar things also happens in a real image). "$FITTOOL" add_raw_headers -f "$OUTPUT_FILE" --address-offset $((32 * 1024)) --size $((4 * 1024 / 16)) --type $((16#7)) -# Now we can execute the `$CBNTPROV bpm-gen` command -"$CBNTPROV" bpm-gen --ibbhash SHA1,SHA256 "$BPMPATH" "$OUTPUT_FILE" +# Now we can execute the `$BGPROV bpm-gen` command +"$BGPROV" bpm-gen-v-2 --ibbhash SHA1,SHA256 "$BPMPATH" "$OUTPUT_FILE" # Sign the BPM (with no password on the private key file) -"$CBNTPROV" bpm-sign "$BPMPATH" "$BPMPATH" "$ODMPRIVKEYPATH" RSASSA "" +"$BGPROV" bpm-sign "$BPMPATH" "$BPMPATH" "$ODMPRIVKEYPATH" RSASSA "" # Inject the BPM reference into FIT (FIT entry type 0x0C), by referencing it as at the offset 22K "$FITTOOL" add_raw_headers -f "$OUTPUT_FILE" --address-offset $(( 22 * 1024 )) --size "$(stat -c %s "$BPMPATH")" --type $((16#C)) @@ -224,4 +224,4 @@ rm -f "$BPMPATH" # That's it, a dummy image is ready (and is in "$OUTPUT_FILE") :) "$FITTOOL" show -f "$OUTPUT_FILE" -"$PCR0TOOL" printnodes -as-tree "$OUTPUT_FILE" \ No newline at end of file +"$PCR0TOOL" printnodes -as-tree "$OUTPUT_FILE"