Add Intel CBnT support

Update go-tpm library
Rework txt-suite cmd parsing to kong cmd line parser
Rework txt-prov cmd parsing to kong cmd line parser
This commit is contained in:
Philipp Deppenwiese 2020-09-07 12:11:21 +02:00 committed by Christopher Meis
parent 2faa705940
commit aca443109c
104 changed files with 11176 additions and 1182 deletions

View File

@ -13,34 +13,43 @@ jobs:
- checkout
- run: sudo apt install -y golint
# specify any bash command here prefixed with `run: `
- run: go run ./pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen -check ./pkg/intel/metadata/manifest ./pkg/intel/metadata/manifest/bootpolicy ./pkg/intel/metadata/manifest/key
- run: if [ "$(gofmt -l .)" != "" ]; then exit 1; fi
- run: golint -set_exit_status ./pkg/test
- run: golint -set_exit_status ./pkg/tools
- run: golint -set_exit_status ./pkg/hwapi
- run: golint -set_exit_status ./pkg/provisioning
- run: golint -set_exit_status ./pkg/provisioning/txt
- run: golint -set_exit_stATUS ./pkg/provisioning/bg
- run: golint -set_exit_status ./pkg/intel/metadata/manifest/
- run: golint -set_exit_status ./pkg/intel/metadata/manifest/bootpolicy
- run: golint -set_exit_status ./pkg/intel/metadata/manifest/key
- run: golint -set_exit_status ./cmd/txt-suite
- run: golint -set_exit_status ./cmd/txt-prov
- run: golint -set_exit_status ./cmd/bg-prov
- run: go mod download
- run: go mod verify
- run: go build -ldflags "-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG}" -o txt-suite cmd/txt-suite/*.go
- run: go build -ldflags "-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG}" -o txt-prov cmd/txt-prov/*.go
- run: go build -ldflags "-X main.gitcommit=${CIRCLE_SHA1} -X main.gittag=${CIRCLE_TAG}" -o bg-prov cmd/bg-prov/*.go
- run: go test ./pkg/hwapi/
- run: go test ./pkg/tools/
- run: go test ./pkg/test/
- run: go test ./pkg/provisioning/
- run: go test ./pkg/provisioning/txt
- run: go test ./pkg/provisioning/bg
- run: mkdir out
- run: git config user.email "circleci@circleci.com"
- run: git config user.name "CI"
- run: ./txt-suite -m > ./cmd/txt-suite/TESTPLAN.md
- 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 out/
- run: cp txt-suite txt-prov bg-prov out/
- persist_to_workspace:
root: out
paths:
- txt-suite
- txt-prov
- bg-prov
create_deb_rpm:
docker:
@ -56,28 +65,31 @@ 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/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-txt-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}.x86_64.rpm
- 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_deb.yaml --target go_txt-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}_amd64.deb
- 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
- 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_deb.yaml --target go_css-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}_amd64.deb
- run: mkdir -p out && cp *.rpm ./out/ && cp *.deb ./out/
- run: cp txt-suite ./out/
- run: cp txt-prov ./out/
- run: cp golang-txt-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}.x86_64.rpm artifact.rpm
- run: cp go_txt-suite-${CIRCLE_TAG}-${CIRCLE_BUILD_NUM}_amd64.deb artifact.deb
- 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:
root: out
paths:
- golang-txt-suite*.rpm
- go_txt-suite*.deb
- golang-css-suite*.rpm
- go_css-suite*.deb
- txt-suite
- txt-prov
- bg-prov
- store_artifacts:
path: artifact.rpm
destination: golang-txt-suite.rpm
destination: golang-css-suite.rpm
- store_artifacts:
path: artifact.deb
destination: golang-txt-suite.deb
destination: golang-css-suite.deb
publish-github-release:

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
/txt-suite
/txt-prov
/bg-prov
cmd/txt-prov/txt-prov
cmd/bg-prov/bg-prov
cmd/txt-suite/test_log.json
cmd/txt-suite/txt-suite

View File

@ -6,7 +6,7 @@ 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 | WIP |
| Intel Trusted Execution Technology CBnT | WIP | Supported |
| Intel Boot Guard | WIP | WIP |
| Intel Platform Firmware Resilience | WIP | - |
@ -21,3 +21,10 @@ Tooling & API
[Intel TXT Provisioning](cmd/txt-prov) - Provisioning of Trusted Platform Module for Intel Trusted Execution Technology usage.
[Intel CBnT Provisioning](cmd/bg-prov) - Provisioning of Converged BootGuard and Trustes Execution Technology (CBnT) usage.
Developer notes
---------------
If you need to update a Boot Policy Manifest or a Key Manifest then please
read an [instruction](./pkg/intel/metadata/manifest/README.md).

View File

@ -1 +1,334 @@
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:
```
<GO111MODULE=on> go mod download
```
Verify all downloaded dependencies run:
```
<GO111MODULE=on> go mod verify
```
To build the test suite run:
```
<GO111MODULE=on> go build -o txt-suite cmd/bg-prov/*.go
```
Commandline subcommands:
--------------
```bash
Usage of ./bg-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 von json configuration
bpm-gen
Generate BPM file based von 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 ./bg-prov
```
Every subcommand has several required or optional arguments and flags. To learn more about them:
```bash
./bg-prov <subcommand> -h
```
Extended documentation about subcommands:
--------------
```bash
./bg-prov show-km Prints Key Manifest binary in human-readable format
<path> Path to binary file containing Key Manifest
```
```bash
./bg-prov show-bpm Prints Boot Policy Manifest binary in human-readable format
<path> Path to binary file containing Boot Policy Manifest
```
```bash
./bg-prov show-acm Prints ACM binary in human-readable format
<path> Path to binary file containing Authenticated Code Module (ACM)
```
```bash
./bg-prov show-all Prints BPM, KM, FIT and ACM from Firmware image binary in human-readable format
<path> Path to full Firmaware image binary file containing Key Manifest, Boot Policy Manifest and ACM
```
```bash
./bg-prov export-acm Exports ACM binary from Firmware image into file
<bios> Path to the full Firmware image binary file.
<out> Path to the newly generated ACM binary file.
```
```bash
./bg-prov export-km Exports KM structures from Firmware image image into file
<bios> Path to the full Firmware image binary file.
<out> Path to the newly generated Key Manifest binary file.
```
```bash
./bg-prov export-bpm Exports BPM structures from Firmware image image into file
<bios> Path to the full Firmware image binary file.
<out> Path to the newly generated Boot Policy Manifest binary file.
```
```bash
./bg-prov read-config Reads config from existing BIOS file and translates it to a JSON configuration
<config> Path to the JSON config file.
<bios> Path to the full Firmware image binary file.
```
```bash
./bg-prov km-gen Generate KM file based of json configuration
<km> Path to the newly generated Key Manifest binary file.
<key> Public Boot Policy signing key
--config=STRING Path to the JSON config file.
--revision=UINT-8 Platform Manufacturers 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
./bg-prov bpm-gen Generate BPM file based of json configuration and full bios image
<bpm> Path to the newly generated Boot Policy Manifest binary file.
<bios> Path to the firmware image binary file.
--config Path to the JSON config file.
--revision Platform Manufacturers 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
./bg-prov km-sign Sign key manifest with given key
<km-in> Path to the generated Key Manifest binary file.
<km-out> Path to write the signed KM to
<km-keyfile> Path to the encrypted PKCS8 private key file.
<password> Password to decrypted PKCS8 private key file
```
```bash
./bg-prov bpm-sign Sign Boot Policy Manifest with given key
<bpm-in> Path to the newly generated Boot Policy Manifest binary file.
<bpm-out> Path to write the signed BPM to
<bpm-keyfile> Path to the encrypted PKCS8 private key file.
<password> Password to decrypt PKCS8 private key file
```
```bash
./bg-prov stitch Stitches BPM, KM and ACM into given BIOS image file
<bios> Path to the full BIOS binary file.
[<acm>] Path to the ACM binary file.
[<km>] Path to the Key Manifest binary file.
[<bpm>] Path to the Boot Policy Manifest binary file.
```
```bash
./bg-prov key-gen Generates key for KM and BPM signing
<algo> Select crypto algorithm for key generation. Options: RSA2048. RSA3072, ECC224, ECC256
<password> Password for AES256 encryption of private keys
[<path>] Path to store keys.
File names are '<path>_bpm/.pub' and '<path>_km/.pub' respectivly
```
```bash
./bg-prov template Writes template JSON configuration into file
<path> Path to the newly generated JSON configuration file.
--revision Platform Manufacturers 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
./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 ./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 ./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
```

669
cmd/bg-prov/cmd.go Normal file
View File

@ -0,0 +1,669 @@
package main
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/key"
"github.com/9elements/converged-security-suite/v2/pkg/provisioning/bg"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
"github.com/google/go-tpm/tpm2"
)
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"`
//BootGuard Manifest Header args
Revision uint8 `flag optional name:"revision" help:"Platform Manufacturers BPM revision number."`
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
ACMSVN manifest.SVN `flag optional name:"acmsvn" help:"Authorized ACM Security Version Number"`
NEMS bootpolicy.Size4K `flag optional name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"`
// IBB args
PBET bootpolicy.PBETValue `flag optional name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."`
IBBSegFlags bootpolicy.SEFlags `flag optional name:"ibbflags" help:"IBB Control flags"`
MCHBAR uint64 `flag optional name:"mchbar" help:"MCHBAR address"`
VDTBAR uint64 `flag optional name:"vdtbar" help:"VTDPVC0BAR address"`
DMABase0 uint32 `flag optional name:"dmabase0" help:"Low DMA protected range base"`
DMASize0 uint32 `flag optional name:"dmasize0" help:"Low DMA protected range limit"`
DMABase1 uint64 `flag optional name:"dmabase1" help:"High DMA protected range base."`
DMASize1 uint64 `flag optional name:"dmasize1" help:"High DMA protected range limit."`
EntryPoint uint32 `flag optional name:"entrypoint" help:"IBB (Startup BIOS) entry point"`
IbbHash []tpm2.Algorithm `flag optional name:"ibbhash" help:"IBB Hash Algorithm"`
IbbSegbase uint32 `flag optional name:"ibbsegbase" help:"Value for IbbSegment structure"`
IbbSegsize uint32 `flag optional name:"ibbsegsize" help:"Value for IBB segment structure"`
IbbSegFlag uint16 `flag optional name:"ibbsegflag" help:"Reducted"`
// TXT args
SintMin uint8 `flag optional name:"sintmin" help:"OEM authorized SinitMinSvn value"`
TXTFlags bootpolicy.TXTControlFlags `flag optional name:"txtflags" help:"TXT Element control flags"`
PowerDownInterval bootpolicy.Duration16In5Sec `flag optional name:"powerdowninterval" help:"Duration of Power Down in 5 sec increments"`
ACPIBaseOffset uint16 `flag optional name:"acpibaseoffset" help:"ACPI IO offset."`
PowermBaseOffset uint32 `flag optional name:"powermbaseoffset" help:"ACPI MMIO offset."`
CMOSOff0 uint8 `flag optional name:"cmosoff0" help:"CMOS byte in bank 0 to store platform wakeup time"`
CMOSOff1 uint8 `flag optional name:"cmosoff1" help:"Second CMOS byte in bank 0 to store platform wakeup time"`
}
type kmPrintCmd struct {
Path string `arg required name:"path" help:"Path to the Key Manifest binary file." type:"path"`
}
type bpmPrintCmd struct {
Path string `arg required name:"path" help:"Path to the Boot Policy Manifest binary file." type:"path"`
}
type acmPrintCmd struct {
Path string `arg required name:"path" help:"Path to the ACM binary file." type:"path"`
}
type biosPrintCmd struct {
Path string `arg required name:"path" help:"Path to the full BIOS binary file." type:"path"`
}
type acmExportCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly generated ACM binary file." type:"path"`
}
type kmExportCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly generated KM binary file." type:"path"`
}
type bpmExportCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly generated BPM binary file." type:"path"`
}
type generateKMCmd struct {
KM string `arg required name:"km" help:"Path to the newly generated Key Manifest binary file." type:"path"`
Key string `arg required name:"key" help:"Public signing key"`
Config string `flag optional name:"config" help:"Path to the JSON config file." type:"path"`
Revision uint8 `flag optional name:"revision" help:"Platform Manufacturers BPM revision number."`
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
ID uint8 `flag optional name:"id" help:"The key Manifest Identifier"`
PKHashAlg tpm2.Algorithm `flag optional name:"pkhashalg" help:"Hash algorithm of OEM public key digest"`
KMHashes []key.Hash `flag optional name:"kmhashes" help:"Key hashes for BPM, ACM, uCode etc"`
BpmPubkey string `flag optional name:"bpmpubkey" help:"Path to bpm public signing key"`
BpmHashAlg tpm2.Algorithm `flag optional name:"bpmhashalgo" help:"Hash algorithm for bpm public signing key"`
Out string `flag optional name:"out" help:"Path to write applied config to"`
Cut bool `flag optional name:"cut" help:"Cuts the signature before writing to binary."`
PrintME bool `flag optional name:"printme" help:"Prints the hash of KM public signing key"`
}
type generateBPMCmd struct {
BPM string `arg required name:"bpm" help:"Path to the newly generated Boot Policy Manifest binary file." type:"path"`
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
Config string `flag optional name:"config" help:"Path to the JSON config file." type:"path"`
//BootGuard Manifest Header args
Revision uint8 `flag optional name:"revision" help:"Platform Manufacturers BPM revision number."`
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
ACMSVN manifest.SVN `flag optional name:"acmsvn" help:"Authorized ACM Security Version Number"`
NEMS bootpolicy.Size4K `flag optional name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"`
// IBB args
PBET bootpolicy.PBETValue `flag optional name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."`
IBBSegFlags bootpolicy.SEFlags `flag optional name:"ibbflags" help:"IBB Control flags"`
MCHBAR uint64 `flag optional name:"mchbar" help:"MCHBAR address"`
VDTBAR uint64 `flag optional name:"vdtbar" help:"VTDPVC0BAR address"`
DMABase0 uint32 `flag optional name:"dmabase0" help:"Low DMA protected range base"`
DMASize0 uint32 `flag optional name:"dmasize0" help:"Low DMA protected range limit"`
DMABase1 uint64 `flag optional name:"dmabase1" help:"High DMA protected range base."`
DMASize1 uint64 `flag optional name:"dmasize1" help:"High DMA protected range limit."`
EntryPoint uint32 `flag optional name:"entrypoint" help:"IBB (Startup BIOS) entry point"`
IbbHash []tpm2.Algorithm `flag optional name:"ibbhash" help:"IBB Hash Algorithm"`
IbbSegbase uint32 `flag optional name:"ibbsegbase" help:"Value for IbbSegment structure"`
IbbSegsize uint32 `flag optional name:"ibbsegsize" help:"Value for IBB segment structure"`
IbbSegFlag uint16 `flag optional name:"ibbsegflag" help:"Reducted"`
// TXT args
SintMin uint8 `flag optional name:"sintmin" help:"OEM authorized SinitMinSvn value"`
TXTFlags bootpolicy.TXTControlFlags `flag optional name:"txtflags" help:"TXT Element control flags"`
PowerDownInterval bootpolicy.Duration16In5Sec `flag optional name:"powerdowninterval" help:"Duration of Power Down in 5 sec increments"`
ACPIBaseOffset uint16 `flag optional name:"acpibaseoffset" help:"ACPI IO offset."`
PowermBaseOffset uint32 `flag optional name:"powermbaseoffset" help:"ACPI MMIO offset."`
CMOSOff0 uint8 `flag optional name:"cmosoff0" help:"CMOS byte in bank 0 to store platform wakeup time"`
CMOSOff1 uint8 `flag optional name:"cmosoff1" help:"Second CMOS byte in bank 0 to store platform wakeup time"`
Out string `flag optional name:"out" help:"Path to write applied config to"`
Cut bool `flag optional name:"cut" help:"Cuts the signature before writing to binary."`
}
type signKMCmd struct {
KmIn string `arg required name:"kmin" help:"Path to the generated Key Manifest binary file." type:"path"`
KmOut string `arg required name:"kmout" help:"Path to write the signed KM to"`
Key string `arg required name:"km-keyfile" help:"Path to the encrypted PKCS8 private key file." type:"path"`
Password string `arg required name:"password" help:"Password to decrypted PKCS8 private key file"`
}
type signBPMCmd struct {
BpmIn string `arg required name:"bpmin" help:"Path to the newly generated Boot Policy Manifest binary file." type:"path"`
BpmOut string `arg required name."bpmout" help:"Path to write the signed BPM to"`
Key string `arg required name:"bpm-keyfile" help:"Path to the encrypted PKCS8 private key file." type:"path"`
Password string `arg required name:"password" help:"Password to decrypt PKCS8 private key file"`
}
type readConfigCmd struct {
Config string `arg required name:"config" help:"Path to the JSON config file." type:"path"`
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
}
type 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"`
}
type keygenCmd struct {
Algo string `arg require name:"algo" help:"Select crypto algorithm for key generation. Options: RSA2048. RSA3072, ECC224, ECC256"`
Password string `arg required name:"password" help:"Password for AES256 encryption of private keys"`
Path string `flag optional name:"path" help:"Path to store keys. File names are 'yourname_bpm/yourname_bpm.pub' and 'yourname_km/yourname_km.pub' respectivly"`
}
func (v *versionCmd) Run(ctx *context) error {
tools.ShowVersion(programName, gittag, gitcommit)
return nil
}
func (kmp *kmPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(kmp.Path)
if err != nil {
return err
}
reader := bytes.NewReader(data)
km, err := bg.ParseKM(reader)
if err != nil {
return err
}
km.Print()
if err := km.KeyAndSignature.Key.PrintMEKey(); err != nil {
return err
}
return nil
}
func (bpmp *bpmPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(bpmp.Path)
if err != nil {
return err
}
reader := bytes.NewReader(data)
bpm, err := bg.ParseBPM(reader)
if err != nil {
return err
}
bpm.Print()
return nil
}
func (acmp *acmPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(acmp.Path)
if err != nil {
return err
}
acm, chipsets, processors, tpms, err, err2 := tools.ParseACM(data)
if err != nil {
return err
}
if err2 != nil {
return err2
}
acm.PrettyPrint()
chipsets.PrettyPrint()
processors.PrettyPrint()
tpms.PrettyPrint()
return nil
}
func (biosp *biosPrintCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(biosp.Path)
if err != nil {
return err
}
err = bg.PrintFIT(data)
if err != nil {
return err
}
err = bg.PrintBootGuardStructures(data)
if err != nil {
return err
}
return nil
}
func (acme *acmExportCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(acme.BIOS)
if err != nil {
return err
}
acmfile, err := os.Create(acme.Out)
if err != nil {
return err
}
err = bg.WriteBootGuardStructures(data, nil, nil, acmfile)
if err != nil {
return err
}
return nil
}
func (kme *kmExportCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(kme.BIOS)
if err != nil {
return err
}
kmfile, err := os.Create(kme.Out)
if err != nil {
return err
}
err = bg.WriteBootGuardStructures(data, nil, kmfile, nil)
if err != nil {
return err
}
return nil
}
func (bpme *bpmExportCmd) Run(ctx *context) error {
data, err := ioutil.ReadFile(bpme.BIOS)
if err != nil {
return err
}
bpmfile, err := os.Create(bpme.Out)
if err != nil {
return err
}
err = bg.WriteBootGuardStructures(data, bpmfile, nil, nil)
if err != nil {
return err
}
return nil
}
func (g *generateKMCmd) Run(ctx *context) error {
var options *bg.BootGuardOptions
if g.Config != "" {
bgo, err := bg.ParseConfig(g.Config)
if err != nil {
return err
}
options = bgo
} else {
var bgo bg.BootGuardOptions
bgo.KeyManifest.Revision = g.Revision
bgo.KeyManifest.KMSVN = g.SVN
bgo.KeyManifest.KMID = g.ID
bgo.KeyManifest.PubKeyHashAlg = g.PKHashAlg
bgo.KeyManifest.Hash = g.KMHashes
// Create KM_Hash for BPM pub signing key
if g.BpmPubkey != "" {
kh, err := bg.GetBPMPubHash(g.BpmPubkey, g.BpmHashAlg)
if err != nil {
return err
}
bgo.KeyManifest.Hash = kh
}
options = &bgo
}
if g.Out != "" {
out, err := os.Create(g.Out)
if err != nil {
return err
}
if err := bg.WriteConfig(out, options); err != nil {
return err
}
}
km, err := bg.SetKM(options)
if err != nil {
return err
}
key, err := bg.ReadPubKey(g.Key)
if err != nil {
return err
}
if err := km.KeyAndSignature.Key.SetPubKey(key); err != nil {
return err
}
if g.PrintME {
if err := km.KeyAndSignature.Key.PrintMEKey(); err != nil {
return err
}
}
bKM, err := bg.WriteKM(km)
if err != nil {
return err
}
if g.Cut == true {
//Cut signature from binary
bKM = bKM[:int(km.KeyManifestSignatureOffset)]
}
if err = ioutil.WriteFile(g.KM, bKM, 0600); err != nil {
return fmt.Errorf("unable to write KM to file: %w", err)
}
return nil
}
func (g *generateBPMCmd) Run(ctx *context) error {
var options *bg.BootGuardOptions
if g.Config != "" {
bgo, err := bg.ParseConfig(g.Config)
if err != nil {
return err
}
options = bgo
} else {
var bgo bg.BootGuardOptions
bgo.BootPolicyManifest.BPMH.BPMRevision = g.Revision
bgo.BootPolicyManifest.BPMH.BPMSVN = g.SVN
bgo.BootPolicyManifest.BPMH.ACMSVNAuth = g.ACMSVN
bgo.BootPolicyManifest.BPMH.NEMDataStack = g.NEMS
se := bootpolicy.NewSE()
se.PBETValue = g.PBET
se.Flags = g.IBBSegFlags
se.IBBMCHBAR = g.MCHBAR
se.VTdBAR = g.VDTBAR
se.DMAProtBase0 = g.DMABase0
se.DMAProtLimit0 = g.DMASize0
se.DMAProtBase1 = g.DMABase1
se.DMAProtLimit1 = g.DMASize1
se.IBBEntryPoint = g.EntryPoint
se.DigestList.List = make([]manifest.HashStructure, len(g.IbbHash))
se.DigestList.Size = uint16(len(g.IbbHash))
for iterator := range se.DigestList.List {
se.DigestList.List[iterator].HashAlg = g.IbbHash[iterator]
}
seg := *bootpolicy.NewIBBSegment()
seg.Base = g.IbbSegbase
seg.Size = g.IbbSegsize
seg.Flags = g.IbbSegFlag
se.IBBSegments = append(se.IBBSegments, seg)
bgo.BootPolicyManifest.SE = append(bgo.BootPolicyManifest.SE, *se)
txt := bootpolicy.NewTXT()
txt.SInitMinSVNAuth = g.SintMin
txt.ControlFlags = g.TXTFlags
txt.PwrDownInterval = g.PowerDownInterval
txt.ACPIBaseOffset = g.ACPIBaseOffset
txt.PwrMBaseOffset = g.PowermBaseOffset
txt.PTTCMOSOffset0 = g.CMOSOff0
txt.PTTCMOSOffset1 = g.CMOSOff1
bgo.BootPolicyManifest.TXTE = txt
options = &bgo
}
bpm, err := bg.GenerateBPM(options, g.BIOS)
if err != nil {
return err
}
// This section is hacky, just to make the parsing work
bpm.PMSE.Key.KeyAlg = 0x01
bpm.PMSE.Signature.HashAlg = 0x01
// End of hacky section
if g.Out != "" {
out, err := os.Create(g.Out)
if err != nil {
return err
}
if err := bg.WriteConfig(out, options); err != nil {
return err
}
}
bBPM, err := bg.WriteBPM(bpm)
if err != nil {
return err
}
if g.Cut {
bBPM = bBPM[:bpm.PMSEOffset()]
}
if err = ioutil.WriteFile(g.BPM, bBPM, 0600); err != nil {
return fmt.Errorf("unable to write BPM to file: %w", err)
}
return nil
}
func (s *signKMCmd) Run(ctx *context) error {
encKey, err := ioutil.ReadFile(s.Key)
if err != nil {
return err
}
privkey, err := bg.DecryptPrivKey(encKey, s.Password)
if err != nil {
return err
}
kmRaw, err := ioutil.ReadFile(s.KmIn)
if err != nil {
return err
}
var km key.Manifest
r := bytes.NewReader(kmRaw)
_, err = km.ReadFrom(r)
if err != nil {
return err
}
km.RehashRecursive()
unsignedKM := kmRaw[:km.KeyAndSignatureOffset()]
if err = km.SetSignature(0, privkey.(crypto.Signer), unsignedKM); err != nil {
return err
}
bKMSigned, err := bg.WriteKM(&km)
if err != nil {
return err
}
if err := ioutil.WriteFile(s.KmOut, bKMSigned, 0600); err != nil {
return err
}
return nil
}
func (s *signBPMCmd) Run(ctx *context) error {
encKey, err := ioutil.ReadFile(s.Key)
if err != nil {
return err
}
key, err := bg.DecryptPrivKey(encKey, s.Password)
if err != nil {
return err
}
bpmRaw, err := ioutil.ReadFile(s.BpmIn)
if err != nil {
return err
}
var bpm bootpolicy.Manifest
r := bytes.NewReader(bpmRaw)
if _, err = bpm.ReadFrom(r); err != nil && !errors.Is(err, io.EOF) {
return err
}
kAs := bootpolicy.NewSignature()
switch key := key.(type) {
case *rsa.PrivateKey:
kAs.Key.SetPubKey(key.Public())
case *ecdsa.PrivateKey:
kAs.Key.SetPubKey(key.Public())
default:
return fmt.Errorf("Invalid key type")
}
bpm.PMSE = *kAs
bpmRaw, err = bg.WriteBPM(&bpm)
if err != nil {
return err
}
bpm.RehashRecursive()
unsignedBPM := bpmRaw[:bpm.KeySignatureOffset]
//err = bpm.PMSE.SetSignature(0, key.(crypto.Signer), unsignedBPM)
err = bpm.PMSE.Signature.SetSignature(0, key.(crypto.Signer), unsignedBPM)
if err != nil {
return fmt.Errorf("unable to make a signature: %w", err)
}
bBPMSigned, err := bg.WriteBPM(&bpm)
if err != nil {
return err
}
if err = ioutil.WriteFile(s.BpmOut, bBPMSigned, 0600); err != nil {
return fmt.Errorf("unable to write BPM to file: %w", err)
}
return nil
}
func (t *templateCmd) Run(ctx *context) error {
var bgo bg.BootGuardOptions
bgo.BootPolicyManifest.BPMH.BPMRevision = t.Revision
bgo.BootPolicyManifest.BPMH.BPMSVN = t.SVN
bgo.BootPolicyManifest.BPMH.ACMSVNAuth = t.ACMSVN
bgo.BootPolicyManifest.BPMH.NEMDataStack = t.NEMS
se := bootpolicy.NewSE()
se.PBETValue = t.PBET
se.Flags = t.IBBSegFlags
se.IBBMCHBAR = t.MCHBAR
se.VTdBAR = t.VDTBAR
se.DMAProtBase0 = t.DMABase0
se.DMAProtLimit0 = t.DMASize0
se.DMAProtBase1 = t.DMABase1
se.DMAProtLimit1 = t.DMASize1
se.IBBEntryPoint = t.EntryPoint
seg := *bootpolicy.NewIBBSegment()
seg.Base = t.IbbSegbase
seg.Size = t.IbbSegsize
seg.Flags = t.IbbSegFlag
se.IBBSegments = append(se.IBBSegments, seg)
bgo.BootPolicyManifest.SE = append(bgo.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
bgo.BootPolicyManifest.TXTE = txt
out, err := os.Create(t.Path)
if err != nil {
return err
}
if err := bg.WriteConfig(out, &bgo); 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 = bg.ReadConfigFromBIOSImage(rc.BIOS, f)
if err != nil {
return err
}
return nil
}
func (s *stitchingCmd) Run(ctx *context) error {
bpm, _ := ioutil.ReadFile(s.BPM)
km, _ := ioutil.ReadFile(s.KM)
acm, _ := ioutil.ReadFile(s.ACM)
if len(acm) == 0 && len(km) == 0 && len(bpm) == 0 {
return fmt.Errorf("at least one optional parameter required")
}
if err := bg.StitchFITEntries(s.BIOS, acm, bpm, km); err != nil {
return err
}
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 := bg.GenRSAKey(2048, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
case "RSA3072":
err := bg.GenRSAKey(3072, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
case "ECC224":
err := bg.GenECCKey(224, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
case "ECC256":
err := bg.GenECCKey(256, k.Password, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile)
if err != nil {
return err
}
default:
return fmt.Errorf("Chosen algorithm invlid. Options are: RSA2048, RSA3072, ECC224, ECC256")
}
return nil
}
var cli struct {
Debug bool `help:"Enable debug mode."`
ManifestStrictOrderCheck bool `help:"Enable checking of manifest elements order"`
Version versionCmd `cmd help:"Prints the version of the program"`
ShowKm kmPrintCmd `cmd help:"Prints Key Manifest binary in human-readable format"`
ShowBpm bpmPrintCmd `cmd help:"Prints Boot Policy Manifest binary in human-readable format"`
ShowAcm acmPrintCmd `cmd help:"Prints ACM binary in human-readable format"`
ShowAll biosPrintCmd `cmd help:"Prints BPM, KM, FIT and ACM from BIOS binary in human-readable format"`
ExportAcm acmExportCmd `cmd help:"Exports ACM structures from BIOS image into file"`
ExportKm kmExportCmd `cmd help:"Exports KM structures from BIOS image into file"`
ExportBpm bpmExportCmd `cmd help:"Exports BPM structures from BIOS image into file"`
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"`
KmGen generateKMCmd `cmd help:"Generate KM file based von json configuration"`
BpmGen generateBPMCmd `cmd help:"Generate BPM file based von json configuration"`
KmSign signKMCmd `cmd help:"Sign key manifest with given key"`
BpmSign signBPMCmd `cmd help:"Sign Boot Policy Manifest with given key"`
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"`
}

30
cmd/bg-prov/main.go Normal file
View File

@ -0,0 +1,30 @@
package main
import (
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/alecthomas/kong"
)
const (
programName = "bg-prov"
programDesc = "Intel BtG/CBnT provisioning tooling"
)
var (
gitcommit string
gittag string
)
func main() {
ctx := kong.Parse(&cli,
kong.Name(programName),
kong.Description(programDesc),
kong.UsageOnError(),
kong.ConfigureHelp(kong.HelpOptions{
Compact: true,
Summary: true,
}))
manifest.StrictOrderCheck = cli.ManifestStrictOrderCheck
err := ctx.Run(&context{Debug: cli.Debug})
ctx.FatalIfErrorf(err)
}

View File

@ -60,28 +60,30 @@ Run it as root:
./txt-prov -config lcp.json -prov
```
Commandline arguments
Commandline subcommands
--------------
```bash
Usage of ./txt-prov:
-adef
Define AUX index if not exists in TPM NVRAM
-adel
Delete AUX index if exists in TPM NVRAM
-config string
Provide a json filename with LCP configuration. Default: lcp.json (default "lcp.json")
-out string
Stores written binary PS index LCP policy into file
-pdef
Define PS index if not exists in TPM NVRAM
-pdel
Delete PS index if exists in TPM NVRAM
-pp
Provision PS & AUX index with LCP config
-pup
Update PS index content in TPM NVRAM
-show
Shows current provisioned PS & AUX index in NVRAM on stdout
-v Shows version and license information
aux-define
Define AUX index if not exists in TPM NVRAM
aux-delete
Delete AUX index if exists in TPM NVRAM
ps-define
Define PS index if not exists in TPM NVRAM
ps-delete
Delete PS index if exists in TPM NVRAM
platform-prov
Provision PS & AUX index with LCP config
ps-update
Update PS index content in TPM NVRAM
show
Shows current provisioned PS & AUX index in NVRAM on stdout
version
Shows version and license information
```
Further information are available via:
```bash
./txt-prov <subcommand> -h
```
Showing the NVRAM indices and LCP policy

View File

@ -1,40 +1,274 @@
package main
import (
"flag"
"fmt"
"io"
"github.com/9elements/converged-security-suite/v2/pkg/provisioning"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"github.com/9elements/converged-security-suite/v2/pkg/provisioning/txt"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
)
var (
// Flags
auxDelete = flag.Bool("adel", false, "Delete AUX index if exists in TPM NVRAM")
auxDefine = flag.Bool("adef", false, "Define AUX index if not exists in TPM NVRAM")
psDefine = flag.Bool("pdef", false, "Define PS index if not exists in TPM NVRAM")
psDelete = flag.Bool("pdel", false, "Delete PS index if exists in TPM NVRAM")
psUpdate = flag.Bool("pup", false, "Update PS index content in TPM NVRAM")
platformProv = flag.Bool("pp", false, "Provision PS & AUX index with LCP config")
show = flag.Bool("show", false, "Shows current provisioned PS & AUX index in NVRAM on stdout")
config = flag.String("config", "lcp.json", "Provide a json filename with LCP configuration. Default: lcp.json")
output = flag.String("out", "", "Stores written binary PS index LCP policy into file")
version = flag.Bool("v", false, "Shows version and license information")
)
// Context for kong command line parser
// We need a TPM device in most commands.
type context struct {
debug bool
}
type versionCmd struct {
}
type auxDeleteCmd struct {
Config string `arg required name:"config" default:"lcp.config" help:"Filename of LCP config file in JSON format"`
Out string `flag optional name:"out" help:"Filename to write binary PS index LCP Policy into"`
}
type auxDefineCmd struct {
}
type psDeleteCmd struct {
}
type psDefineCmd struct {
}
type psUpdateCmd struct {
Config string `arg required name:"config" default:"lcp.config" help:"Filename of LCP config file in JSON format" type:"path"`
Out string `flag optional name:"output" help:"Filename to write binary PS index LCP Policy into" type:"path"`
}
type platProvCmd struct {
Config string `arg required name:"config" default:"lcp.config" help:"Filename of LCP config file in JSON format" type:"path"`
Out string `flag optional name:"output" help:"Filename to write binary PS index LCP Policy into" type:"path"`
}
type showCmd struct {
}
var cli struct {
Debug bool `help:"Enable debug mode"`
ManifestStrictOrderCheck bool `help:"Enable checking of manifest elements order"`
Version versionCmd `cmd help:"Prints the version of the program"`
AuxDelete auxDeleteCmd `cmd help:"Delete AUX index if exists in TPM NVRAM"`
AuxDefine auxDefineCmd `cmd help:"Define AUX index if not exists in TPM NVRAM"`
PsDelete psDeleteCmd `cmd help:"Delete PS index if exists in TPM NVRAM"`
PsDefine psDefineCmd `cmd help:"Define PS index if not exists in TPM NVRAM"`
PsUpdate psUpdateCmd `cmd help:"Update PS index content in TPM NVRAM"`
PlatformProv platProvCmd `cmd help:"Provision PS & AUX index with LCP config"`
Show showCmd `cmd help:"Show current provisioned PS & AUX index in NVRAM on stdout"`
}
func (v *versionCmd) Run(ctx *context) error {
tools.ShowVersion(programName, gittag, gitcommit)
return nil
}
func (a *auxDeleteCmd) Run(ctx *context) error {
// Set Aux Delete bit in LCP Policy and writes it to PS index in TPM NVRAM
tpm, err := hwapi.NewTPM()
if err != nil {
return err
}
defer tpm.Close()
switch tpm.Version {
case hwapi.TPMVersion12:
return fmt.Errorf("TPM 1.2 not supported yet")
case hwapi.TPMVersion20:
lcp, err := loadConfig(a.Config)
if err != nil {
return fmt.Errorf("Couldn't parse LCP config file: %v", err)
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
return fmt.Errorf("Couldn't read password from stdin: %v", err)
}
if err = txt.DeleteAUXindexTPM20(tpm.RWC, lcp, passHash); err != nil {
return fmt.Errorf("Couldn't delete AUX index: %v", err)
}
if len(a.Out) > 0 {
if err = writePSPolicy2file(lcp, a.Out); err != nil {
return fmt.Errorf("Couldn't write PS Policy2 into file: %v", err)
}
}
default:
return fmt.Errorf("TPM device not recognized")
}
return nil
}
func (a *auxDefineCmd) Run(ctx *context) error {
// Define AUX index in TPM NVRAM
tpm, err := hwapi.NewTPM()
if err != nil {
return err
}
switch tpm.Version {
case hwapi.TPMVersion12:
return fmt.Errorf("TPM 1.2 not supported yet")
case hwapi.TPMVersion20:
lock, err := IsNVRAMUnlocked(tpm)
if err != nil {
return fmt.Errorf("Couldn't check if NVRAM is unlocked: %v", err)
}
if lock {
return fmt.Errorf("NVRAM is locked, please disable Intel TXT or any firmware TPM driver")
}
if err = txt.DefineAUXIndexTPM20(tpm.RWC); err != nil {
return fmt.Errorf("Couldn't define AUX index: %v", err)
}
default:
return fmt.Errorf("TPM device not recognized")
}
return nil
}
func (p *psDeleteCmd) Run(ctx *context) error {
// Delete PS index in TPM NVRAM
tpm, err := hwapi.NewTPM()
if err != nil {
return err
}
switch tpm.Version {
case hwapi.TPMVersion12:
return fmt.Errorf("TPM 1.2 not supported yet")
case hwapi.TPMVersion20:
passHash, err := readPassphraseHashTPM20()
if err != nil {
return fmt.Errorf("Couldn't read password from stdin: %v", err)
}
if err = txt.DeletePSIndexTPM20(tpm.RWC, passHash); err != nil {
return fmt.Errorf("Couldn't delete PS index: %v", err)
}
default:
return fmt.Errorf("TPM device not recognized")
}
return nil
}
func (p *psDefineCmd) Run(ctx *context) error {
// Define PS index in TPM NVRAM
tpm, err := hwapi.NewTPM()
if err != nil {
return err
}
switch tpm.Version {
case hwapi.TPMVersion12:
return fmt.Errorf("TPM 1.2 not supported yet")
case hwapi.TPMVersion20:
lock, err := IsNVRAMUnlocked(tpm)
if err != nil {
return fmt.Errorf("Couldn't check if NVRAM is unlocked: %v", err)
}
if lock {
return fmt.Errorf("NVRAM is locked, please disable Intel TXT or any firmware TPM driver")
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
return fmt.Errorf("Couldn't read password from stdin: %v", err)
}
if err = txt.DefinePSIndexTPM20(tpm.RWC, passHash); err != nil {
fmt.Errorf("Couldn't define PS index: %v", err)
}
default:
return fmt.Errorf("TPM device not recognized")
}
return nil
}
func (p *psUpdateCmd) Run(ctx *context) error {
// Writes new LCP Policy to PS index in TPM NVRAM
tpm, err := hwapi.NewTPM()
if err != nil {
return err
}
switch tpm.Version {
case hwapi.TPMVersion12:
return fmt.Errorf("TPM 1.2 not supported yet")
case hwapi.TPMVersion20:
lcp, err := loadConfig(p.Config)
if err != nil {
return fmt.Errorf("Couldn't parse LCP config file: %v", err)
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
return fmt.Errorf("Couldn't read password from stdin: %v", err)
}
if err = txt.WritePSIndexTPM20(tpm.RWC, lcp, passHash); err != nil {
return fmt.Errorf("Couldn't update PS index: %v", err)
}
if len(p.Out) > 0 {
if err = writePSPolicy2file(lcp, p.Out); err != nil {
return fmt.Errorf("Couldn't write PS Policy2 into file: %v", err)
}
}
default:
return fmt.Errorf("TPM device not recognized")
}
return nil
}
func (p *platProvCmd) Run(ctx *context) error {
// Provision PS & AUX index in TPM NVRAM with LCP Policy
tpm, err := hwapi.NewTPM()
if err != nil {
return err
}
switch tpm.Version {
case hwapi.TPMVersion12:
return fmt.Errorf("TPM 1.2 not supported yet")
case hwapi.TPMVersion20:
lock, err := IsNVRAMUnlocked(tpm)
if err != nil {
return fmt.Errorf("Couldn't check if NVRAM is unlocked: %v", err)
}
if lock {
return fmt.Errorf("NVRAM is locked, please disable Intel TXT or any firmware TPM driver")
}
lcp, err := loadConfig(p.Config)
if err != nil {
return fmt.Errorf("Couldn't parse LCP config file: %v", err)
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
return fmt.Errorf("Couldn't read password from stdin: %v", err)
}
if err = provisionTPM20(tpm.RWC, passHash, lcp); err != nil {
return fmt.Errorf("Couldn't provision PS & AUX index: %v", err)
}
if len(p.Out) > 0 {
if err = writePSPolicy2file(lcp, p.Out); err != nil {
fmt.Printf("Couldn't write PS Policy2 into file: %v\n", err)
}
}
default:
return fmt.Errorf("TPM device not recognized")
}
return nil
}
func (s *showCmd) Run(ctx *context) error {
// Show PS & AUX index content from TPM NVRAM
tpm, err := hwapi.NewTPM()
if err != nil {
return err
}
switch tpm.Version {
case hwapi.TPMVersion12:
return fmt.Errorf("TPM 1.2 not supported yet")
case hwapi.TPMVersion20:
txt.PrintProvisioningTPM20(tpm.RWC)
default:
return fmt.Errorf("TPM device not recognized")
}
return nil
}
func provisionTPM20(rw io.ReadWriter, passHash []byte, lcpPolilcy *tools.LCPPolicy2) error {
passHash, err := readPassphraseHashTPM20()
if err != nil {
return err
}
if err := provisioning.DefinePSIndexTPM20(rw, passHash); err != nil {
if err := txt.DefinePSIndexTPM20(rw, passHash); err != nil {
return fmt.Errorf("definePSIndexTPM20() failed: %v", err)
}
if err := provisioning.WritePSIndexTPM20(rw, lcpPolilcy, passHash); err != nil {
if err := txt.WritePSIndexTPM20(rw, lcpPolilcy, passHash); err != nil {
return fmt.Errorf("writePSPolicy() failed: %v", err)
}
if err := provisioning.DefineAUXIndexTPM20(rw); err != nil {
if err := txt.DefineAUXIndexTPM20(rw); err != nil {
return fmt.Errorf("defineAUXIndexTPM20() failed: %v", err)
}
return nil

View File

@ -7,7 +7,7 @@ import (
"strconv"
"strings"
prov "github.com/9elements/converged-security-suite/v2/pkg/provisioning"
txt "github.com/9elements/converged-security-suite/v2/pkg/provisioning/txt"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
)
@ -47,7 +47,7 @@ func loadConfig(filename string) (*tools.LCPPolicy2, error) {
if uint16(ver) < uint16(0x300) || uint16(ver) > uint16(0x306) {
return nil, fmt.Errorf("Invalid LCP Version. Want: 0x300 - 0x306 - Have: %v", config.Version)
}
hashAlg, ok := tools.HashAlgMap[prov.HashMapping[config.HashAlg]]
hashAlg, ok := tools.HashAlgMap[txt.HashMapping[config.HashAlg]]
if ok == false {
return nil, fmt.Errorf("Cant determin hash algorithm")
}

View File

@ -1,16 +1,12 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"github.com/9elements/converged-security-suite/v2/pkg/provisioning"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/alecthomas/kong"
)
const programName = "Intel TXT Provisioning tool"
const programName = "txt-prov"
const programDesc = "Intel TXT provisioning tool"
var (
gitcommit string
@ -18,141 +14,18 @@ var (
)
func main() {
flag.Parse()
if *version {
tools.ShowVersion(programName, gittag, gitcommit)
os.Exit(0)
}
tpmTss, err := hwapi.NewTPM()
if err != nil {
fmt.Printf("Couldn't set up tpm connection: %v\n", err)
os.Exit(1)
}
defer tpmTss.Close()
ctx := kong.Parse(&cli,
kong.Name(programName),
kong.Description(programDesc),
kong.UsageOnError(),
kong.ConfigureHelp(kong.HelpOptions{
Compact: true,
Summary: true,
}))
manifest.StrictOrderCheck = cli.ManifestStrictOrderCheck
switch tpmTss.Version {
case hwapi.TPMVersion12:
fmt.Println("TPM 1.2 not supported yet")
os.Exit(1)
case hwapi.TPMVersion20:
if *auxDelete {
lcp, err := loadConfig(*config)
if err != nil {
fmt.Printf("Couldn't parse LCP config file: %v\n", err)
os.Exit(1)
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
fmt.Printf("Couldn't read password from stdin: %v\n", err)
os.Exit(1)
}
if err = provisioning.DeleteAUXindexTPM20(tpmTss.RWC, lcp, passHash); err != nil {
fmt.Printf("Couldn't delete AUX index: %v\n", err)
os.Exit(1)
}
if len(*output) > 0 {
if err = writePSPolicy2file(lcp, *output); err != nil {
fmt.Printf("Couldn't write PS Policy2 into file: %v\n", err)
}
}
} else if *auxDefine {
lock, err := IsNVRAMUnlocked(tpmTss)
if err != nil {
fmt.Printf("Couldn't check if NVRAM is unlocked: %v\n", err)
os.Exit(1)
}
if lock {
fmt.Println("NVRAM is locked, please disable Intel TXT or any firmware TPM driver")
os.Exit(1)
}
if err = provisioning.DefineAUXIndexTPM20(tpmTss.RWC); err != nil {
fmt.Printf("Couldn't define AUX index: %v\n", err)
os.Exit(1)
}
} else if *psDefine {
lock, err := IsNVRAMUnlocked(tpmTss)
if err != nil {
fmt.Printf("Couldn't check if NVRAM is unlocked: %v\n", err)
os.Exit(1)
}
if lock {
fmt.Println("NVRAM is locked, please disable Intel TXT or any firmware TPM driver")
os.Exit(1)
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
fmt.Printf("Couldn't read password from stdin: %v\n", err)
os.Exit(1)
}
if err = provisioning.DefinePSIndexTPM20(tpmTss.RWC, passHash); err != nil {
fmt.Printf("Couldn't define PS index: %v\n", err)
os.Exit(1)
}
} else if *psDelete {
passHash, err := readPassphraseHashTPM20()
if err != nil {
fmt.Printf("Couldn't read password from stdin: %v\n", err)
os.Exit(1)
}
if err = provisioning.DeletePSIndexTPM20(tpmTss.RWC, passHash); err != nil {
fmt.Printf("Couldn't delete PS index: %v\n", err)
os.Exit(1)
}
} else if *psUpdate {
lcp, err := loadConfig(*config)
if err != nil {
fmt.Printf("Couldn't parse LCP config file: %v\n", err)
os.Exit(1)
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
fmt.Printf("Couldn't read password from stdin: %v\n", err)
os.Exit(1)
}
if err = provisioning.WritePSIndexTPM20(tpmTss.RWC, lcp, passHash); err != nil {
fmt.Printf("Couldn't update PS index: %v\n", err)
os.Exit(1)
}
if len(*output) > 0 {
if err = writePSPolicy2file(lcp, *output); err != nil {
fmt.Printf("Couldn't write PS Policy2 into file: %v\n", err)
}
}
} else if *platformProv {
lock, err := IsNVRAMUnlocked(tpmTss)
if err != nil {
fmt.Printf("Couldn't check if NVRAM is unlocked: %v\n", err)
os.Exit(1)
}
if lock {
fmt.Println("NVRAM is locked, please disable Intel TXT or any firmware TPM driver")
os.Exit(1)
}
lcp, err := loadConfig(*config)
if err != nil {
fmt.Printf("Couldn't parse LCP config file: %v\n", err)
os.Exit(1)
}
passHash, err := readPassphraseHashTPM20()
if err != nil {
fmt.Printf("Couldn't read password from stdin: %v\n", err)
os.Exit(1)
}
if err = provisionTPM20(tpmTss.RWC, passHash, lcp); err != nil {
fmt.Printf("Couldn't provision PS & AUX index: %v\n", err)
os.Exit(1)
}
if len(*output) > 0 {
if err = writePSPolicy2file(lcp, *output); err != nil {
fmt.Printf("Couldn't write PS Policy2 into file: %v\n", err)
}
}
} else if *show {
provisioning.PrintProvisioningTPM20(tpmTss.RWC)
}
default:
fmt.Println("No TPM device found")
os.Exit(1)
}
return
// Run commands
err := ctx.Run(&context{
debug: cli.Debug})
ctx.FatalIfErrorf(err)
}

View File

@ -152,8 +152,8 @@ package main
import (
"log"
"github.com/9elements/converged-security-suite/pkg/hwapi"
"github.com/9elements/converged-security-suite/pkg/test"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"github.com/9elements/converged-security-suite/v2/pkg/test"
)
func main() {
@ -180,8 +180,8 @@ package main
import (
"log"
"github.com/9elements/converged-security-suite/pkg/hwapi"
"github.com/9elements/converged-security-suite/pkg/test"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"github.com/9elements/converged-security-suite/v2/pkg/test"
)
func main() {

View File

@ -1,35 +1,130 @@
package main
import (
"flag"
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"sort"
"strconv"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/test"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
"github.com/google/go-tpm/tpm2"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
a "github.com/logrusorgru/aurora"
)
var testno = flag.String("t", "", "Select test number 1 - 50. e.g.: -t=1,2,3,4,...")
var interactive = flag.Bool("i", false, "Interactive mode. Errors will stop the testing.")
var listtests = flag.Bool("l", false, "Lists all test")
var teststomarkdown = flag.Bool("m", false, "Output test implementation state as Markdown")
var version = flag.Bool("v", false, "Shows Version, copyright info and license")
var tpmdev = flag.String("tpm", "", "Select TPM-Path. e.g.: -tpm=/dev/tpmX, with X as number of the TPM module")
var logpath = flag.String("log", "", "Give a path/filename for test result output in JSON format. e.g.: /path/to/filename.json")
var all = flag.Bool("all", false, "Run all the tests of the suite")
var uefi = flag.Bool("uefi", false, "Test if platform is UEFI boot enabled")
var txtready = flag.Bool("txtready", false, "Run TXTReady specific tests")
var tboot = flag.Bool("tboot", false, "Test if tboot hypervisor runs correctly")
var cbnt = flag.Bool("cbnt", false, "Run CBnT specific tests")
var configFile = flag.String("config", "", "Give a path/filename to configuration file")
func flagUsed() bool {
return testno != nil
type context struct {
tpmdev *hwapi.TPM
interactive bool
logpath string
}
func flagInteractive() bool {
return *interactive
type listCmd struct {
}
type markdownCmd struct {
}
type versionCmd struct {
}
type execTestsCmd struct {
Set string `required default:"all" help:"Select subset of tests. Options: all, uefi, txtready, tboot, cbnt, legacy"`
Interactive bool `optional short:"i" help:"Interactive mode. Errors will stop the testing."`
Config string `optional short:"c" help:"Path/Filename to config file."`
Log string `optional help:"Give a path/filename for test result output inJSON format. e.g.: /path/to/filename.json"`
}
var cli struct {
ManifestStrictOrderCheck bool `help:"Enable checking of manifest elements order"`
TpmDev string `short:"t" help:"Select TPM-Path. e.g.:--tpmdev=/dev/tpmX, with X as number of the TPM module"`
ExecTests execTestsCmd `cmd help:"Executes tests given be TestNo or TestSet"`
List listCmd `cmd help:"Lists all tests"`
Markdown markdownCmd `cmd help:"Output test implementation state as Markdown"`
Version versionCmd `cmd help:"Prints the version of the program"`
}
func (e *execTestsCmd) Run(ctx *context) error {
ret := false
var config tools.Configuration
if e.Config != "" {
var err error
configuration, err := tools.ParseConfig(e.Config)
if err != nil {
os.Exit(1)
}
config = *configuration
} else {
// Default TPM 2.0 Intel TXT configuration
config.LCPHash = tpm2.AlgSHA256
config.TPM = hwapi.TPMVersion20
config.TXTMode = tools.AutoPromotion
}
switch e.Set {
case "all":
fmt.Println("For more information about the documents and chapters, run: txt-suite -m")
ret = run("All", getTests(), config, e.Interactive)
case "uefi":
ret = run("UEFI", test.TestsUEFI, config, e.Interactive)
case "txtready":
fmt.Println("For more information about the documents and chapters, run: txt-suite -m")
ret = run("TXT Ready", test.TestsTXTReady, config, e.Interactive)
case "tboot":
ret = run("Tboot", test.TestsTBoot, config, e.Interactive)
case "cbnt":
return fmt.Errorf("CBnT support not implemented yet")
case "legacy":
ret = run("Legacy TXT", test.TestsLegacy, config, e.Interactive)
default:
return fmt.Errorf("No valid test set given")
}
if !ret {
return fmt.Errorf("Tests ran with errors")
}
return nil
}
func (l *listCmd) Run(ctx *context) error {
tests := getTests()
for i := range tests {
fmt.Printf("Test No: %v, %v\n", i, tests[i].Name)
}
return nil
}
func (m *markdownCmd) Run(ctx *context) error {
var teststate string
tests := getTests()
fmt.Println("Id | Test | Implemented | Document | Chapter")
fmt.Println("------------|------------|------------|------------|------------")
for i := range tests {
if tests[i].Status == test.Implemented {
teststate = ":white_check_mark:"
} else if tests[i].Status == test.NotImplemented {
teststate = ":x:"
} else {
teststate = ":clock1:"
}
docID := tests[i].SpecificationDocumentID
if docID != "" {
docID = "Document " + docID
}
fmt.Printf("%02d | %-48s | %-22s | %-28s | %-56s\n", i, tests[i].Name, teststate, docID, tests[i].SpecificationChapter)
}
return nil
}
func (v *versionCmd) Run(ctx *context) error {
tools.ShowVersion(programDesc, gittag, gitcommit)
return nil
}
func getTests() []*test.Test {
@ -52,70 +147,75 @@ func getTests() []*test.Test {
return tests
}
func listTests() {
tests := getTests()
func run(testGroup string, tests []*test.Test, config tools.Configuration, interactive bool) bool {
var result = false
f := bufio.NewWriter(os.Stdout)
for i := range tests {
fmt.Printf("Test No: %v, %v\n", i, tests[i].Name)
hwAPI := hwapi.GetAPI()
fmt.Printf("\n%s tests\n", a.Bold(a.Gray(20-1, testGroup).BgGray(4-1)))
var i int
for i = 0; i < len(testGroup)+6; i++ {
fmt.Print("_")
}
}
fmt.Println()
for idx := range tests {
if len(testnos) > 0 {
// SearchInt returns an index where to "insert" idx
i := sort.SearchInts(testnos, idx)
if i >= len(testnos) {
continue
}
// still here? i must be within testnos.
if testnos[i] != idx {
continue
}
}
func listTestsAsMarkdown() {
var teststate string
tests := getTests()
if !tests[idx].Run(hwAPI, &config) && tests[idx].Required && interactive {
result = true
break
}
fmt.Println("Id | Test | Implemented | Document | Chapter")
fmt.Println("------------|------------|------------|------------|------------")
for i := range tests {
if tests[i].Status == test.Implemented {
teststate = ":white_check_mark:"
} else if tests[i].Status == test.NotImplemented {
teststate = ":x:"
}
if !interactive {
var t []temptest
for index := range tests {
if tests[index].Status != test.NotImplemented {
ttemp := temptest{index, tests[index].Name, tests[index].Result.String(), tests[index].ErrorText, tests[index].Status.String()}
t = append(t, ttemp)
}
}
data, _ := json.MarshalIndent(t, "", "")
ioutil.WriteFile(logfile, data, 0664)
}
for index := range tests {
if tests[index].Status == test.NotImplemented {
continue
}
if tests[index].Result == test.ResultNotRun {
continue
}
fmt.Printf("%02d - ", index)
fmt.Printf("%-40s: ", a.Bold(tests[index].Name))
f.Flush()
if tests[index].Result == test.ResultPass {
fmt.Printf("%-20s", a.Bold(a.Green(tests[index].Result)))
} else {
teststate = ":clock1:"
fmt.Printf("%-20s", a.Bold(a.Red(tests[index].Result)))
}
docID := tests[i].SpecificationDocumentID
if docID != "" {
docID = "Document " + docID
if tests[index].ErrorText != "" {
fmt.Printf(" (%s)", tests[index].ErrorText)
} else if len(tests[index].ErrorText) == 0 && tests[index].Result == test.ResultFail {
fmt.Print(" (No error text given)")
}
fmt.Printf("%02d | %-48s | %-22s | %-28s | %-56s\n", i, tests[i].Name, teststate, docID, tests[i].SpecificationChapter)
fmt.Printf("\n")
f.Flush()
}
}
func deconstructFlag() ([]int, error) {
var testnos []int
var tmpstrings []string
var testrange []string
var testmin int
var testmax int
var err error
tmpstrings = strings.Split(*testno, ",")
for _, item := range tmpstrings {
if strings.Contains(item, "-") {
testrange = strings.Split(item, "-")
testmin, err = strconv.Atoi(testrange[0])
if err != nil {
return nil, err
}
testmax, err = strconv.Atoi(testrange[1])
if err != nil {
return nil, err
}
for i := testmin; i <= testmax; i++ {
testnos = append(testnos, i)
}
} else {
tmpno, err := strconv.Atoi(item)
if err != nil {
return nil, err
}
testnos = append(testnos, tmpno)
}
}
//Sort array
sort.Ints(testnos)
return testnos, nil
return result
}

View File

@ -1,21 +1,14 @@
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"sort"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"github.com/9elements/converged-security-suite/v2/pkg/test"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
a "github.com/logrusorgru/aurora"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/alecthomas/kong"
)
const programName = "Intel TXT Suite"
const (
programName = "txt-suite"
programDesc = "Intel TXT Test Suite"
)
var (
testnos []int
@ -33,136 +26,16 @@ type temptest struct {
Status string
}
func run(testGroup string, tests []*test.Test, config tools.Configuration) bool {
var result = false
f := bufio.NewWriter(os.Stdout)
hwAPI := hwapi.GetAPI()
fmt.Printf("\n%s tests\n", a.Bold(a.Gray(20-1, testGroup).BgGray(4-1)))
var i int
for i = 0; i < len(testGroup)+6; i++ {
fmt.Print("_")
}
fmt.Println()
for idx := range tests {
if len(testnos) > 0 {
// SearchInt returns an index where to "insert" idx
i := sort.SearchInts(testnos, idx)
if i >= len(testnos) {
continue
}
// still here? i must be within testnos.
if testnos[i] != idx {
continue
}
}
if !tests[idx].Run(hwAPI, &config) && tests[idx].Required && flagInteractive() {
result = true
break
}
}
if !flagInteractive() {
var t []temptest
for index := range tests {
if tests[index].Status != test.NotImplemented {
ttemp := temptest{index, tests[index].Name, tests[index].Result.String(), tests[index].ErrorText, tests[index].Status.String()}
t = append(t, ttemp)
}
}
data, _ := json.MarshalIndent(t, "", "")
ioutil.WriteFile(logfile, data, 0664)
}
for index := range tests {
if tests[index].Status == test.NotImplemented {
continue
}
if tests[index].Result == test.ResultNotRun {
continue
}
fmt.Printf("%02d - ", index)
fmt.Printf("%-40s: ", a.Bold(tests[index].Name))
f.Flush()
if tests[index].Result == test.ResultPass {
fmt.Printf("%-20s", a.Bold(a.Green(tests[index].Result)))
} else {
fmt.Printf("%-20s", a.Bold(a.Red(tests[index].Result)))
}
if tests[index].ErrorText != "" {
fmt.Printf(" (%s)", tests[index].ErrorText)
} else if len(tests[index].ErrorText) == 0 && tests[index].Result == test.ResultFail {
fmt.Print(" (No error text given)")
}
fmt.Printf("\n")
f.Flush()
}
return result
}
func main() {
ret := false
flag.Parse()
if flagUsed() == true {
testnos, _ = deconstructFlag()
}
if *logpath != "" {
logfile = *logpath
}
var config tools.Configuration
if *configFile != "" {
var err error
configuration, err := tools.ParseConfig(*configFile)
if err != nil {
os.Exit(1)
}
config = *configuration
} else {
// Default TPM 2.0 Intel TXT configuration
config.LCPHash = tools.LCPPol2HAlgSHA256
config.TPM = hwapi.TPMVersion20
config.TXTMode = tools.AutoPromotion
}
if *listtests == true {
listTests()
} else if *version == true {
tools.ShowVersion(programName, gittag, gitcommit)
} else if *teststomarkdown == true {
listTestsAsMarkdown()
} else if *all == true {
fmt.Println("For more information about the documents and chapters, run: txt-suite -m")
ret = run("All", getTests(), config)
} else if *txtready == true {
fmt.Println("For more information about the documents and chapters, run: txt-suite -m")
ret = run("TXT Ready", test.TestsTXTReady, config)
} else {
fmt.Println("For more information about the documents and chapters, run: txt-suite -m")
if *cbnt {
fmt.Println("CBnT support not implemented yet.")
os.Exit(1)
} else {
ret = run("Legacy TXT", test.TestsLegacy, config)
}
}
if *uefi == true {
ret = run("UEFI", test.TestsUEFI, config)
}
if *tboot == true {
ret = run("Tboot", test.TestsTBoot, config)
}
if ret {
os.Exit(1)
} else {
os.Exit(0)
}
ctx := kong.Parse(&cli,
kong.Name(programName),
kong.Description(programDesc),
kong.UsageOnError(),
kong.ConfigureHelp(kong.HelpOptions{
Compact: true,
Summary: true,
}))
manifest.StrictOrderCheck = cli.ManifestStrictOrderCheck
err := ctx.Run(&context{})
ctx.FatalIfErrorf(err)
}

22
go.mod
View File

@ -3,20 +3,20 @@ module github.com/9elements/converged-security-suite/v2
go 1.11
require (
github.com/dsnet/compress v0.0.1 // indirect
github.com/alecthomas/kong v0.2.11
github.com/creasty/defaults v1.5.1
github.com/dustin/go-humanize v1.0.0
github.com/fatih/camelcase v1.0.0
github.com/fatih/structtag v1.2.0 // indirect
github.com/fearful-symmetry/gomsr v0.0.1
github.com/golang/protobuf v1.4.2 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-tpm v0.3.1-0.20200831123702-16478836c95e
github.com/google/go-tpm v0.3.3-0.20210120190357-1ff48daca32f
github.com/intel-go/cpuid v0.0.0-20200819041909-2aa72927c3e2
github.com/linuxboot/fiano v5.0.0+incompatible
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/archiver v3.1.1+incompatible
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.2.2
github.com/tidwall/pretty v1.0.2
github.com/ulikunitz/xz v0.5.8 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xaionaro-go/gosrc v0.0.0-20201124181305-3fdf8476a735
github.com/xaionaro-go/unsafetools v0.0.0-20200202162159-021b112c4d30 // indirect
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect
google.golang.org/protobuf v1.25.0 // indirect
)

158
go.sum
View File

@ -1,24 +1,53 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/kong v0.2.11 h1:RKeJXXWfg9N47RYfMm0+igkxBCTF4bzbneAxaqid0c4=
github.com/alecthomas/kong v0.2.11/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creasty/defaults v1.5.1 h1:j8WexcS3d/t4ZmllX4GEkl4wIB/trOr035ajcLHCISM=
github.com/creasty/defaults v1.5.1/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fearful-symmetry/gomsr v0.0.1 h1:m208RzdTApWVbv8a9kf78rdPLQe+BY9AxRb/nSbHxSA=
github.com/fearful-symmetry/gomsr v0.0.1/go.mod h1:Qb/0Y7zwobP7v8Sji+M5mlL4N7Voyz5WaKXXRFPnLio=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@ -27,75 +56,105 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
github.com/google/go-tpm v0.2.1-0.20191106030929-f0607eac7f8a/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.2.1-0.20200624183331-16766ac45214/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.1-0.20200805192422-240a3433a7a5/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.1-0.20200825132356-42b3b03098ce h1:JGAv8EFHBu57V4n2GSE8ZFZxmZToxLKlL2vJHkK20SA=
github.com/google/go-tpm v0.3.1-0.20200825132356-42b3b03098ce/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.1-0.20200831123702-16478836c95e h1:hGzvc02KODsLoHVLgzodNriEzIZCjb/LFwy1ILlEkjM=
github.com/google/go-tpm v0.3.1-0.20200831123702-16478836c95e/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.2 h1:3iQQ2dlEf+1no7CLlfLPYzxhQy7j2G/emBqU5okydaw=
github.com/google/go-tpm v0.3.2/go.mod h1:j71sMBTfp3X5jPHz852ZOfQMUOf65Gb/Th8pRmp7fvg=
github.com/google/go-tpm v0.3.3-0.20210120190357-1ff48daca32f h1:L2/I8aWw3KRzIPQ55ps20JfD8tF/olPBJepwmGHxiXA=
github.com/google/go-tpm v0.3.3-0.20210120190357-1ff48daca32f/go.mod h1:j71sMBTfp3X5jPHz852ZOfQMUOf65Gb/Th8pRmp7fvg=
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
github.com/google/go-tpm-tools v0.1.2/go.mod h1:7P6l1Vpa84LrA4mT116MOOOLogA16bvo0SSE8020nkQ=
github.com/google/go-tpm-tools v0.1.3-0.20200626093744-11f284793aa8 h1:wNSpV8+LZMVdPGOZ9fLhjaI+AFbV7bk10zfJZKw9C8c=
github.com/google/go-tpm-tools v0.1.3-0.20200626093744-11f284793aa8/go.mod h1:43fCwoNawwJ3t2DQYH57tJvcRi8CF11ce01edlQBjqo=
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/intel-go/cpuid v0.0.0-20181003105527-1a4a6f06a1c6 h1:XboatR7lasl05yel5hNXF7kQBw2oFUGdMztcgisfhNU=
github.com/intel-go/cpuid v0.0.0-20181003105527-1a4a6f06a1c6/go.mod h1:RmeVYf9XrPRbRc3XIx0gLYA8qOFvNoPOfaEZduRlEp4=
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/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/linuxboot/fiano v5.0.0+incompatible h1:DZAZO0z9l35cakTNnkdh+yWRZfzCCJnDHmPAYW/t0No=
github.com/linuxboot/fiano v5.0.0+incompatible/go.mod h1:IPKmAwYdbidivI8+nWCBO97QkdsiF8OThAHowU8Tvdk=
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=
github.com/mholt/archiver v1.1.2 h1:xukR55YIrnhDHp10lrNtRSsAK5THpWrOCuviweNSBw4=
github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v1.0.1 h1:w6GMGWSsCI04fTM8wQRdnW74MuJISakuUU0onU0TYB4=
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xaionaro-go/gosrc v0.0.0-20201124181305-3fdf8476a735 h1:I5vlWq623SQl/E48ulUZP2YN+vlLMhzyETuOuTNtZhI=
github.com/xaionaro-go/gosrc v0.0.0-20201124181305-3fdf8476a735/go.mod h1:KWPOUqeg7VZ8gE4MQJJmG+YgmNf2yAkBiDI++R48tFg=
github.com/xaionaro-go/unsafetools v0.0.0-20200202162159-021b112c4d30 h1:6HCWbXp+IoQx6XRlVbcDlM0BSWigu8H2FM6VdXeOk5s=
github.com/xaionaro-go/unsafetools v0.0.0-20200202162159-021b112c4d30/go.mod h1:spWmgrD4QEkFsootCLGU3OLWgeeJew0KXrCsVPWBQ88=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -105,25 +164,35 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d h1:MiWWjyhUzZ+jvhZvloX6ZrUsdEghn8a64Upd8EMHglE=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -131,6 +200,7 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@ -139,12 +209,14 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -53,47 +53,6 @@ var vendors = map[TCGVendorID]string{
1196379975: "Google",
}
// HashAlg is the TPM hash algorithm id
type HashAlg uint8
var (
// HashSHA1 is the TPM 1.2 identifier for SHA1
HashSHA1 = HashAlg(tpm2.AlgSHA1)
// HashSHA256 is the TPM 2.0 identifier for SHA256
HashSHA256 = HashAlg(tpm2.AlgSHA256)
)
func (a HashAlg) cryptoHash() crypto.Hash {
switch a {
case HashSHA1:
return crypto.SHA1
case HashSHA256:
return crypto.SHA256
}
return 0
}
func (a HashAlg) goTPMAlg() tpm2.Algorithm {
switch a {
case HashSHA1:
return tpm2.AlgSHA1
case HashSHA256:
return tpm2.AlgSHA256
}
return 0
}
// String returns a human-friendly representation of the hash algorithm.
func (a HashAlg) String() string {
switch a {
case HashSHA1:
return "SHA1"
case HashSHA256:
return "SHA256"
}
return fmt.Sprintf("HashAlg<%d>", int(a))
}
// PCR encapsulates the value of a PCR at a point in time.
type PCR struct {
Index int
@ -418,7 +377,7 @@ func readPCR12(rwc io.ReadWriter, pcrIndex uint32) ([]byte, error) {
}
func readPCR20(rwc io.ReadWriter, pcrIndex uint32) ([]byte, error) {
return tpm2.ReadPCR(rwc, int(pcrIndex), HashSHA256.goTPMAlg())
return tpm2.ReadPCR(rwc, int(pcrIndex), tpm2.AlgSHA256)
}
// NewTPM returns a TPM
@ -528,13 +487,13 @@ func (t *TPM) ReadNVPublic(index uint32) ([]byte, error) {
}
// ReadPCRs reads all PCRs into the PCR structure
func (t *TPM) ReadPCRs(alg HashAlg) ([]PCR, error) {
func (t *TPM) ReadPCRs(alg tpm2.Algorithm) ([]PCR, error) {
var PCRs map[uint32][]byte
var err error
switch t.Version {
case TPMVersion12:
if alg != HashSHA1 {
if alg != tpm2.AlgSHA1 {
return nil, fmt.Errorf("non-SHA1 algorithm %v is not supported on TPM 1.2", alg)
}
PCRs, err = readAllPCRs12(t.RWC)
@ -543,7 +502,7 @@ func (t *TPM) ReadPCRs(alg HashAlg) ([]PCR, error) {
}
case TPMVersion20:
PCRs, err = readAllPCRs20(t.RWC, alg.goTPMAlg())
PCRs, err = readAllPCRs20(t.RWC, alg)
if err != nil {
return nil, fmt.Errorf("failed to read PCRs: %v", err)
}
@ -554,10 +513,14 @@ func (t *TPM) ReadPCRs(alg HashAlg) ([]PCR, error) {
out := make([]PCR, len(PCRs))
for index, digest := range PCRs {
h, err := alg.Hash()
if err != nil {
return nil, err
}
out[int(index)] = PCR{
Index: int(index),
Digest: digest,
DigestAlg: alg.cryptoHash(),
DigestAlg: h,
}
}

View File

@ -0,0 +1,52 @@
To generalize all the logic related to Boot Policy Manifest and Key Manifest
we use code generation. Therefore it is enough to create structure declarations
and run command from this directory:
```
go run ./internal/manifestcodegen/cmd/manifestcodegen/ . ./bootpolicy ./key
```
It will performe the code autogeneration in directories:
* Current directory (`.`), which contains common structures for different manifests;
* Boot Policy Manifest directory (`./bootpolicy`);
* and Key Manifest directory (`./key`).
To check if the files are in the up-to-date state, one may add option `-check`:
```
go run ./internal/manifestcodegen/cmd/manifestcodegen/ -check . ./bootpolicy ./key
```
Or if it is required to debug/trace the behavior of autogenerated code, one
may add option `-trace`:
```
go run ./internal/manifestcodegen/cmd/manifestcodegen/ -trace . ./bootpolicy ./key
```
In this case the code will write a verbose log into stdout.
If you need to edit the template, please edit file: `./internal/manifestcodegen/cmd/manifestcodegen/template_methods.tpl.go`.
# Field tags
There are few special struct field tags which are recognized by the code
generator:
* `id` -- defines the element Structure ID string (for example `__TXTS__`).
* `version` -- defines the value of `StructVersion` (see document #575623).
* `countType` -- used only for slices and it defines which variable type is
used to store amount of items of the slice. Arrays in a structure in a Manifest
is almost always prepended with a count variable, and we automatically map
it to the real amount of elements of our slice. And to do that we need to know
the bitsize of the counter, therefore this tag exists.
* `countValue` -- (see also `countType`) sometimes a counter requires special
transformations before it could be maped into the real amount of elements
of a slice. `countValue` allows to define a function to calculate the
real count value.
* `require` -- defines the value required by the document #575623.
* `default` -- defines the default value.
* `prettyValue` -- defines the function which prints the value in a pretty format.
* `rehashValue` -- is used to receive an auto-updated value, for example it could
be handy to automatically update size-fields.
See also:
```
grep -RIn field.TagGet ./
```

View File

@ -0,0 +1,37 @@
//go:generate manifestcodegen
package bootpolicy
import (
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
)
type BPMH struct {
StructInfo `id:"__ACBP__" version:"0x23" var0:"0x20" var1:"uint16(s.TotalSize())"`
KeySignatureOffset uint16 `json:"bpmh_KeySignatureOffset"`
BPMRevision uint8 `json:"bpmh_Revision"`
// PrettyString: BPM SVN
BPMSVN manifest.SVN `json:"bpmh_SNV"`
// PrettyString: ACM SVN Auth
ACMSVNAuth manifest.SVN `json:"bpmh_ACMSVN"`
Reserved0 [1]byte `require:"0" json:"bpmh_Reserved0,omitemtpy"`
NEMDataStack Size4K `json:"bpmh_NEMStackSize"`
}
// Size4K is a size in units of 4096 bytes.
type Size4K uint16
// InBytes returns the size in bytes.
func (s Size4K) InBytes() uint32 {
return uint32(s) * 4096
}
// NewSize4K returns the given size as multiple of 4K
func NewSize4K(size uint32) Size4K {
return Size4K(size / 4096)
}

View File

@ -0,0 +1,365 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy
package bootpolicy
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewBPMH returns a new instance of BPMH with
// all default values set.
func NewBPMH() *BPMH {
s := &BPMH{}
copy(s.StructInfo.ID[:], []byte(StructureIDBPMH))
s.StructInfo.Version = 0x23
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *BPMH) Validate() error {
// See tag "require"
for idx := range s.Reserved0 {
if s.Reserved0[idx] != 0 {
return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx])
}
}
return nil
}
// StructureIDBPMH is the StructureID (in terms of
// the document #575623) of element 'BPMH'.
const StructureIDBPMH = "__ACBP__"
// GetStructInfo returns current value of StructInfo of the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *BPMH) GetStructInfo() manifest.StructInfo {
return s.StructInfo
}
// SetStructInfo sets new value of StructInfo to the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *BPMH) SetStructInfo(newStructInfo manifest.StructInfo) {
s.StructInfo = newStructInfo
}
// ReadFrom reads the BPMH from 'r' in format defined in the document #575623.
func (s *BPMH) ReadFrom(r io.Reader) (int64, error) {
var totalN int64
err := binary.Read(r, binary.LittleEndian, &s.StructInfo)
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(s.StructInfo))
n, err := s.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read data: %w", err)
}
totalN += n
return totalN, nil
}
// ReadDataFrom reads the BPMH from 'r' excluding StructInfo,
// in format defined in the document #575623.
func (s *BPMH) ReadDataFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// StructInfo (ManifestFieldType: structInfo)
{
// ReadDataFrom does not read Struct, use ReadFrom for that.
}
// KeySignatureOffset (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySignatureOffset)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KeySignatureOffset': %w", err)
}
totalN += int64(n)
}
// BPMRevision (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.BPMRevision)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'BPMRevision': %w", err)
}
totalN += int64(n)
}
// BPMSVN (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.BPMSVN)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'BPMSVN': %w", err)
}
totalN += int64(n)
}
// ACMSVNAuth (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.ACMSVNAuth)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'ACMSVNAuth': %w", err)
}
totalN += int64(n)
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// NEMDataStack (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.NEMDataStack)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'NEMDataStack': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *BPMH) RehashRecursive() {
s.StructInfo.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *BPMH) Rehash() {
s.Variable0 = 0x20
s.ElementSize = uint16(s.TotalSize())
}
// WriteTo writes the BPMH into 'w' in format defined in
// the document #575623.
func (s *BPMH) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// StructInfo (ManifestFieldType: structInfo)
{
n, err := s.StructInfo.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err)
}
totalN += int64(n)
}
// KeySignatureOffset (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySignatureOffset)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KeySignatureOffset': %w", err)
}
totalN += int64(n)
}
// BPMRevision (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.BPMRevision)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'BPMRevision': %w", err)
}
totalN += int64(n)
}
// BPMSVN (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.BPMSVN)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'BPMSVN': %w", err)
}
totalN += int64(n)
}
// ACMSVNAuth (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.ACMSVNAuth)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'ACMSVNAuth': %w", err)
}
totalN += int64(n)
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// NEMDataStack (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.NEMDataStack)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'NEMDataStack': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// StructInfoSize returns the size in bytes of the value of field StructInfo
func (s *BPMH) StructInfoTotalSize() uint64 {
return s.StructInfo.TotalSize()
}
// KeySignatureOffsetSize returns the size in bytes of the value of field KeySignatureOffset
func (s *BPMH) KeySignatureOffsetTotalSize() uint64 {
return 2
}
// BPMRevisionSize returns the size in bytes of the value of field BPMRevision
func (s *BPMH) BPMRevisionTotalSize() uint64 {
return 1
}
// BPMSVNSize returns the size in bytes of the value of field BPMSVN
func (s *BPMH) BPMSVNTotalSize() uint64 {
return 1
}
// ACMSVNAuthSize returns the size in bytes of the value of field ACMSVNAuth
func (s *BPMH) ACMSVNAuthTotalSize() uint64 {
return 1
}
// Reserved0Size returns the size in bytes of the value of field Reserved0
func (s *BPMH) Reserved0TotalSize() uint64 {
return 1
}
// NEMDataStackSize returns the size in bytes of the value of field NEMDataStack
func (s *BPMH) NEMDataStackTotalSize() uint64 {
return 2
}
// StructInfoOffset returns the offset in bytes of field StructInfo
func (s *BPMH) StructInfoOffset() uint64 {
return 0
}
// KeySignatureOffsetOffset returns the offset in bytes of field KeySignatureOffset
func (s *BPMH) KeySignatureOffsetOffset() uint64 {
return s.StructInfoOffset() + s.StructInfoTotalSize()
}
// BPMRevisionOffset returns the offset in bytes of field BPMRevision
func (s *BPMH) BPMRevisionOffset() uint64 {
return s.KeySignatureOffsetOffset() + s.KeySignatureOffsetTotalSize()
}
// BPMSVNOffset returns the offset in bytes of field BPMSVN
func (s *BPMH) BPMSVNOffset() uint64 {
return s.BPMRevisionOffset() + s.BPMRevisionTotalSize()
}
// ACMSVNAuthOffset returns the offset in bytes of field ACMSVNAuth
func (s *BPMH) ACMSVNAuthOffset() uint64 {
return s.BPMSVNOffset() + s.BPMSVNTotalSize()
}
// Reserved0Offset returns the offset in bytes of field Reserved0
func (s *BPMH) Reserved0Offset() uint64 {
return s.ACMSVNAuthOffset() + s.ACMSVNAuthTotalSize()
}
// NEMDataStackOffset returns the offset in bytes of field NEMDataStack
func (s *BPMH) NEMDataStackOffset() uint64 {
return s.Reserved0Offset() + s.Reserved0TotalSize()
}
// Size returns the total size of the BPMH.
func (s *BPMH) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.StructInfoTotalSize()
size += s.KeySignatureOffsetTotalSize()
size += s.BPMRevisionTotalSize()
size += s.BPMSVNTotalSize()
size += s.ACMSVNAuthTotalSize()
size += s.Reserved0TotalSize()
size += s.NEMDataStackTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *BPMH) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "BPMH", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is structInfo
lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Key Signature Offset", "", &s.KeySignatureOffset))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "BPM Revision", "", &s.BPMRevision))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "BPM SVN", "", &s.BPMSVN))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "ACM SVN Auth", "", &s.ACMSVNAuth))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "NEM Data Stack", "", &s.NEMDataStack))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags Size4K) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Size 4 K", flags))
}
lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", flags.InBytes()))
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,28 @@
//go:generate manifestcodegen
package bootpolicy
import (
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
)
// StructInfo is the common header of any element.
type StructInfo = manifest.StructInfo
// PrettyString: Boot Policy Manifest
type Manifest struct {
// PrettyString: BPMH: Header
BPMH `rehashValue:"rehashedBPMH()" json:"bpm_Header"`
SE []SE `json:"bpm_SE"`
TXTE *TXT `json:"bpm_TXTE,omitempty"`
// PrettyString: PCDE: Platform Config Data
PCDE *PCD `json:"bpm_PCDE,omitempty"`
// PrettyString: PME: Platform Manufacturer
PME *PM `json:"bpm_PME,omitempty"`
// PrettyString: PMSE: Signature
PMSE Signature `json:"bpm_Signature"`
}
func (bpm Manifest) StructInfo() StructInfo {
return bpm.BPMH.StructInfo
}

View File

@ -0,0 +1,406 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy
package bootpolicy
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewManifest returns a new instance of Manifest with
// all default values set.
func NewManifest() *Manifest {
s := &Manifest{}
// Recursively initializing a child structure:
s.BPMH = *NewBPMH()
// Recursively initializing a child structure:
s.PMSE = *NewSignature()
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *Manifest) Validate() error {
// Recursively validating a child structure:
if err := s.BPMH.Validate(); err != nil {
return fmt.Errorf("error on field 'BPMH': %w", err)
}
// See tag "rehashValue"
{
expectedValue := BPMH(s.rehashedBPMH())
if s.BPMH != expectedValue {
return fmt.Errorf("field 'BPMH' expects write-value '%v', but has %v", expectedValue, s.BPMH)
}
}
// Recursively validating a child structure:
if err := s.PMSE.Validate(); err != nil {
return fmt.Errorf("error on field 'PMSE': %w", err)
}
return nil
}
// fieldIndexByStructID returns the position index within
// structure Manifest of the field by its StructureID
// (see document #575623, an example of StructureID value is "__KEYM__").
func (_ Manifest) fieldIndexByStructID(structID string) int {
switch structID {
case StructureIDBPMH:
return 0
case StructureIDSE:
return 1
case StructureIDTXT:
return 2
case StructureIDPCD:
return 3
case StructureIDPM:
return 4
case StructureIDSignature:
return 5
}
return -1
}
// fieldNameByIndex returns the name of the field by its position number
// within structure Manifest.
func (_ Manifest) fieldNameByIndex(fieldIndex int) string {
switch fieldIndex {
case 0:
return "BPMH"
case 1:
return "SE"
case 2:
return "TXTE"
case 3:
return "PCDE"
case 4:
return "PME"
case 5:
return "PMSE"
}
return fmt.Sprintf("invalidFieldIndex_%d", fieldIndex)
}
// ReadFrom reads the Manifest from 'r' in format defined in the document #575623.
func (s *Manifest) ReadFrom(r io.Reader) (int64, error) {
var missingFieldsByIndices = [6]bool{
0: true,
5: true,
}
var totalN int64
previousFieldIndex := int(-1)
for {
var structInfo manifest.StructInfo
err := binary.Read(r, binary.LittleEndian, &structInfo)
if err == io.EOF || err == io.ErrUnexpectedEOF {
return totalN, nil
}
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(structInfo))
structID := structInfo.ID.String()
fieldIndex := s.fieldIndexByStructID(structID)
if fieldIndex < 0 {
// TODO: report error "unknown structure ID: '"+structID+"'"
continue
}
if manifest.StrictOrderCheck && fieldIndex < previousFieldIndex {
return totalN, fmt.Errorf("invalid order of fields (%d < %d): structure '%s' is out of order", fieldIndex, previousFieldIndex, structID)
}
missingFieldsByIndices[fieldIndex] = false
var n int64
switch structID {
case StructureIDBPMH:
if fieldIndex == previousFieldIndex {
return totalN, fmt.Errorf("field 'BPMH' is not a slice, but multiple elements found")
}
s.BPMH.SetStructInfo(structInfo)
n, err = s.BPMH.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field BPMH at %d: %w", totalN, err)
}
case StructureIDSE:
var el SE
el.SetStructInfo(structInfo)
n, err = el.ReadDataFrom(r)
s.SE = append(s.SE, el)
if err != nil {
return totalN, fmt.Errorf("unable to read field SE at %d: %w", totalN, err)
}
case StructureIDTXT:
if fieldIndex == previousFieldIndex {
return totalN, fmt.Errorf("field 'TXTE' is not a slice, but multiple elements found")
}
s.TXTE = &TXT{}
s.TXTE.SetStructInfo(structInfo)
n, err = s.TXTE.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field TXTE at %d: %w", totalN, err)
}
case StructureIDPCD:
if fieldIndex == previousFieldIndex {
return totalN, fmt.Errorf("field 'PCDE' is not a slice, but multiple elements found")
}
s.PCDE = &PCD{}
s.PCDE.SetStructInfo(structInfo)
n, err = s.PCDE.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field PCDE at %d: %w", totalN, err)
}
case StructureIDPM:
if fieldIndex == previousFieldIndex {
return totalN, fmt.Errorf("field 'PME' is not a slice, but multiple elements found")
}
s.PME = &PM{}
s.PME.SetStructInfo(structInfo)
n, err = s.PME.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field PME at %d: %w", totalN, err)
}
case StructureIDSignature:
if fieldIndex == previousFieldIndex {
return totalN, fmt.Errorf("field 'PMSE' is not a slice, but multiple elements found")
}
s.PMSE.SetStructInfo(structInfo)
n, err = s.PMSE.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field PMSE at %d: %w", totalN, err)
}
default:
return totalN, fmt.Errorf("there is no field with structure ID '%s' in Manifest", structInfo.ID)
}
totalN += n
previousFieldIndex = fieldIndex
}
for fieldIndex, v := range missingFieldsByIndices {
if v {
return totalN, fmt.Errorf("field '%s' is missing", s.fieldNameByIndex(fieldIndex))
}
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *Manifest) RehashRecursive() {
s.BPMH.Rehash()
if s.TXTE != nil {
s.TXTE.Rehash()
}
if s.PCDE != nil {
s.PCDE.Rehash()
}
if s.PME != nil {
s.PME.Rehash()
}
s.PMSE.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *Manifest) Rehash() {
s.BPMH = BPMH(s.rehashedBPMH())
}
// WriteTo writes the Manifest into 'w' in format defined in
// the document #575623.
func (s *Manifest) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// BPMH (ManifestFieldType: element)
{
n, err := s.BPMH.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'BPMH': %w", err)
}
totalN += int64(n)
}
// SE (ManifestFieldType: elementList)
{
for idx := range s.SE {
n, err := s.SE[idx].WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'SE[%d]': %w", idx, err)
}
totalN += int64(n)
}
}
// TXTE (ManifestFieldType: element)
if s.TXTE != nil {
n, err := s.TXTE.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'TXTE': %w", err)
}
totalN += int64(n)
}
// PCDE (ManifestFieldType: element)
if s.PCDE != nil {
n, err := s.PCDE.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PCDE': %w", err)
}
totalN += int64(n)
}
// PME (ManifestFieldType: element)
if s.PME != nil {
n, err := s.PME.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PME': %w", err)
}
totalN += int64(n)
}
// PMSE (ManifestFieldType: element)
{
n, err := s.PMSE.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PMSE': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// BPMHSize returns the size in bytes of the value of field BPMH
func (s *Manifest) BPMHTotalSize() uint64 {
return s.BPMH.TotalSize()
}
// SESize returns the size in bytes of the value of field SE
func (s *Manifest) SETotalSize() uint64 {
var size uint64
for idx := range s.SE {
size += s.SE[idx].TotalSize()
}
return size
}
// TXTESize returns the size in bytes of the value of field TXTE
func (s *Manifest) TXTETotalSize() uint64 {
return s.TXTE.TotalSize()
}
// PCDESize returns the size in bytes of the value of field PCDE
func (s *Manifest) PCDETotalSize() uint64 {
return s.PCDE.TotalSize()
}
// PMESize returns the size in bytes of the value of field PME
func (s *Manifest) PMETotalSize() uint64 {
return s.PME.TotalSize()
}
// PMSESize returns the size in bytes of the value of field PMSE
func (s *Manifest) PMSETotalSize() uint64 {
return s.PMSE.TotalSize()
}
// BPMHOffset returns the offset in bytes of field BPMH
func (s *Manifest) BPMHOffset() uint64 {
return 0
}
// SEOffset returns the offset in bytes of field SE
func (s *Manifest) SEOffset() uint64 {
return s.BPMHOffset() + s.BPMHTotalSize()
}
// TXTEOffset returns the offset in bytes of field TXTE
func (s *Manifest) TXTEOffset() uint64 {
return s.SEOffset() + s.SETotalSize()
}
// PCDEOffset returns the offset in bytes of field PCDE
func (s *Manifest) PCDEOffset() uint64 {
return s.TXTEOffset() + s.TXTETotalSize()
}
// PMEOffset returns the offset in bytes of field PME
func (s *Manifest) PMEOffset() uint64 {
return s.PCDEOffset() + s.PCDETotalSize()
}
// PMSEOffset returns the offset in bytes of field PMSE
func (s *Manifest) PMSEOffset() uint64 {
return s.PMEOffset() + s.PMETotalSize()
}
// Size returns the total size of the Manifest.
func (s *Manifest) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.BPMHTotalSize()
size += s.SETotalSize()
size += s.TXTETotalSize()
size += s.PCDETotalSize()
size += s.PMETotalSize()
size += s.PMSETotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *Manifest) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Boot Policy Manifest", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is element
lines = append(lines, pretty.SubValue(depth+1, "BPMH: Header", "", &s.BPMH))
// ManifestFieldType is elementList
lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("SE: Array of \"IBB Segments Element\" of length %d", len(s.SE)), s.SE))
for i := 0; i < len(s.SE); i++ {
lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.SE[i].PrettyString(depth+2, true)))
}
if depth < 1 {
lines = append(lines, "")
}
// ManifestFieldType is element
lines = append(lines, pretty.SubValue(depth+1, "TXTE", "", s.TXTE))
// ManifestFieldType is element
lines = append(lines, pretty.SubValue(depth+1, "PCDE: Platform Config Data", "", s.PCDE))
// ManifestFieldType is element
lines = append(lines, pretty.SubValue(depth+1, "PME: Platform Manufacturer", "", s.PME))
// ManifestFieldType is element
lines = append(lines, pretty.SubValue(depth+1, "PMSE: Signature", "", &s.PMSE))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,48 @@
// +build !manifestcodegen
//
// To avoid errors "bpm.KeySignatureOffsetTotalSize undefined" and
// "bpm.BPMH.PrettyString undefined" we place these functions to a file
// with a build tag "!manifestcodegen"
package bootpolicy
import (
"fmt"
)
func (bpm *Manifest) rehashedBPMH() BPMH {
bpmh := bpm.BPMH
bpmh.KeySignatureOffset = uint16(bpm.PMSEOffset() + bpm.PMSE.KeySignatureOffset())
return bpmh
}
// Print prints the Manifest
func (bpm Manifest) Print() {
fmt.Printf("%v", bpm.BPMH.PrettyString(1, true))
for _, item := range bpm.SE {
fmt.Printf("%v", item.PrettyString(1, true))
}
if bpm.TXTE != nil {
fmt.Printf("%v\n", bpm.TXTE.PrettyString(1, true))
} else {
fmt.Printf(" --TXTE--\n\t not set!(optional)\n")
}
if bpm.PCDE != nil {
fmt.Printf("%v\n", bpm.PCDE.PrettyString(1, true))
} else {
fmt.Println(" --PCDE-- \n\tnot set!(optional)")
}
if bpm.PME != nil {
fmt.Printf("%v\n", bpm.PME.PrettyString(1, true))
} else {
fmt.Println(" --PME--\n\tnot set!(optional)")
}
if bpm.PMSE.Signature.DataTotalSize() < 1 {
fmt.Printf(" --PMSE--\n\tBoot Policy Manifest not signed!\n\n")
} else {
fmt.Printf("%v \n", bpm.PMSE.PrettyString(1, true))
}
}

View File

@ -0,0 +1,11 @@
package bootpolicy
import (
"testing"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/unittest"
)
func TestReadWrite(t *testing.T) {
unittest.ManifestReadWrite(t, &Manifest{}, "testdata/bpm.bin")
}

View File

@ -0,0 +1,10 @@
//go:generate manifestcodegen
package bootpolicy
// PCD holds various Platform Config Data.
type PCD struct {
StructInfo `id:"__PCDS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"`
Reserved0 [2]byte `json:"pcd_Reserved0,omitempty"`
Data []byte `json:"pcd_Data"`
}

View File

@ -0,0 +1,240 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy
package bootpolicy
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewPCD returns a new instance of PCD with
// all default values set.
func NewPCD() *PCD {
s := &PCD{}
copy(s.StructInfo.ID[:], []byte(StructureIDPCD))
s.StructInfo.Version = 0x20
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *PCD) Validate() error {
return nil
}
// StructureIDPCD is the StructureID (in terms of
// the document #575623) of element 'PCD'.
const StructureIDPCD = "__PCDS__"
// GetStructInfo returns current value of StructInfo of the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *PCD) GetStructInfo() manifest.StructInfo {
return s.StructInfo
}
// SetStructInfo sets new value of StructInfo to the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *PCD) SetStructInfo(newStructInfo manifest.StructInfo) {
s.StructInfo = newStructInfo
}
// ReadFrom reads the PCD from 'r' in format defined in the document #575623.
func (s *PCD) ReadFrom(r io.Reader) (int64, error) {
var totalN int64
err := binary.Read(r, binary.LittleEndian, &s.StructInfo)
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(s.StructInfo))
n, err := s.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read data: %w", err)
}
totalN += n
return totalN, nil
}
// ReadDataFrom reads the PCD from 'r' excluding StructInfo,
// in format defined in the document #575623.
func (s *PCD) ReadDataFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// StructInfo (ManifestFieldType: structInfo)
{
// ReadDataFrom does not read Struct, use ReadFrom for that.
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
var size uint16
err := binary.Read(r, binary.LittleEndian, &size)
if err != nil {
return totalN, fmt.Errorf("unable to the read size of field 'Data': %w", err)
}
totalN += int64(binary.Size(size))
s.Data = make([]byte, size)
n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *PCD) RehashRecursive() {
s.StructInfo.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *PCD) Rehash() {
s.Variable0 = 0
s.ElementSize = uint16(s.TotalSize())
}
// WriteTo writes the PCD into 'w' in format defined in
// the document #575623.
func (s *PCD) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// StructInfo (ManifestFieldType: structInfo)
{
n, err := s.StructInfo.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err)
}
totalN += int64(n)
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
size := uint16(len(s.Data))
err := binary.Write(w, binary.LittleEndian, size)
if err != nil {
return totalN, fmt.Errorf("unable to write the size of field 'Data': %w", err)
}
totalN += int64(binary.Size(size))
n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// StructInfoSize returns the size in bytes of the value of field StructInfo
func (s *PCD) StructInfoTotalSize() uint64 {
return s.StructInfo.TotalSize()
}
// Reserved0Size returns the size in bytes of the value of field Reserved0
func (s *PCD) Reserved0TotalSize() uint64 {
return 2
}
// DataSize returns the size in bytes of the value of field Data
func (s *PCD) DataTotalSize() uint64 {
size := uint64(binary.Size(uint16(0)))
size += uint64(len(s.Data))
return size
}
// StructInfoOffset returns the offset in bytes of field StructInfo
func (s *PCD) StructInfoOffset() uint64 {
return 0
}
// Reserved0Offset returns the offset in bytes of field Reserved0
func (s *PCD) Reserved0Offset() uint64 {
return s.StructInfoOffset() + s.StructInfoTotalSize()
}
// DataOffset returns the offset in bytes of field Data
func (s *PCD) DataOffset() uint64 {
return s.Reserved0Offset() + s.Reserved0TotalSize()
}
// Size returns the total size of the PCD.
func (s *PCD) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.StructInfoTotalSize()
size += s.Reserved0TotalSize()
size += s.DataTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *PCD) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "PCD", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is structInfo
lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0))
// ManifestFieldType is arrayDynamic
lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,9 @@
//go:generate manifestcodegen
package bootpolicy
type PM struct {
StructInfo `id:"__PMDA__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"`
Reserved0 [2]byte `require:"0" json:"pc_Reserved0,omitempty"`
Data []byte `json:"pc_Data"`
}

View File

@ -0,0 +1,246 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy
package bootpolicy
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewPM returns a new instance of PM with
// all default values set.
func NewPM() *PM {
s := &PM{}
copy(s.StructInfo.ID[:], []byte(StructureIDPM))
s.StructInfo.Version = 0x20
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *PM) Validate() error {
// See tag "require"
for idx := range s.Reserved0 {
if s.Reserved0[idx] != 0 {
return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx])
}
}
return nil
}
// StructureIDPM is the StructureID (in terms of
// the document #575623) of element 'PM'.
const StructureIDPM = "__PMDA__"
// GetStructInfo returns current value of StructInfo of the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *PM) GetStructInfo() manifest.StructInfo {
return s.StructInfo
}
// SetStructInfo sets new value of StructInfo to the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *PM) SetStructInfo(newStructInfo manifest.StructInfo) {
s.StructInfo = newStructInfo
}
// ReadFrom reads the PM from 'r' in format defined in the document #575623.
func (s *PM) ReadFrom(r io.Reader) (int64, error) {
var totalN int64
err := binary.Read(r, binary.LittleEndian, &s.StructInfo)
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(s.StructInfo))
n, err := s.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read data: %w", err)
}
totalN += n
return totalN, nil
}
// ReadDataFrom reads the PM from 'r' excluding StructInfo,
// in format defined in the document #575623.
func (s *PM) ReadDataFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// StructInfo (ManifestFieldType: structInfo)
{
// ReadDataFrom does not read Struct, use ReadFrom for that.
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
var size uint16
err := binary.Read(r, binary.LittleEndian, &size)
if err != nil {
return totalN, fmt.Errorf("unable to the read size of field 'Data': %w", err)
}
totalN += int64(binary.Size(size))
s.Data = make([]byte, size)
n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *PM) RehashRecursive() {
s.StructInfo.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *PM) Rehash() {
s.Variable0 = 0
s.ElementSize = uint16(s.TotalSize())
}
// WriteTo writes the PM into 'w' in format defined in
// the document #575623.
func (s *PM) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// StructInfo (ManifestFieldType: structInfo)
{
n, err := s.StructInfo.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err)
}
totalN += int64(n)
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
size := uint16(len(s.Data))
err := binary.Write(w, binary.LittleEndian, size)
if err != nil {
return totalN, fmt.Errorf("unable to write the size of field 'Data': %w", err)
}
totalN += int64(binary.Size(size))
n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// StructInfoSize returns the size in bytes of the value of field StructInfo
func (s *PM) StructInfoTotalSize() uint64 {
return s.StructInfo.TotalSize()
}
// Reserved0Size returns the size in bytes of the value of field Reserved0
func (s *PM) Reserved0TotalSize() uint64 {
return 2
}
// DataSize returns the size in bytes of the value of field Data
func (s *PM) DataTotalSize() uint64 {
size := uint64(binary.Size(uint16(0)))
size += uint64(len(s.Data))
return size
}
// StructInfoOffset returns the offset in bytes of field StructInfo
func (s *PM) StructInfoOffset() uint64 {
return 0
}
// Reserved0Offset returns the offset in bytes of field Reserved0
func (s *PM) Reserved0Offset() uint64 {
return s.StructInfoOffset() + s.StructInfoTotalSize()
}
// DataOffset returns the offset in bytes of field Data
func (s *PM) DataOffset() uint64 {
return s.Reserved0Offset() + s.Reserved0TotalSize()
}
// Size returns the total size of the PM.
func (s *PM) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.StructInfoTotalSize()
size += s.Reserved0TotalSize()
size += s.DataTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *PM) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "PM", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is structInfo
lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0))
// ManifestFieldType is arrayDynamic
lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,141 @@
//go:generate manifestcodegen
package bootpolicy
import (
"fmt"
"math"
"time"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
)
// PrettyString: IBB Segments Element
type SE struct {
StructInfo `id:"__IBBS__" version:"0x20" var0:"0" var1:"uint16(s.TotalSize())"`
Reserved0 [1]byte `require:"0" json:"se_Reserved0,omitempty"`
SetNumber uint8 `require:"0" json:"se_SetNumber,omitempty"`
Reserved1 [1]byte `require:"0" json:"se_Reserved1,omitempty"`
PBETValue PBETValue `json:"se_PBETValue"`
Flags SEFlags `json:"se_Flags"`
// PrettyString: IBB MCHBAR
IBBMCHBAR uint64 `json:"se_IBBMCHBAR"`
// PrettyString: VT-d BAR
VTdBAR uint64 `json:"se_VTdBAR"`
// PrettyString: DMA Protection 0 Base Address
DMAProtBase0 uint32 `json:"se_DMAProtBase0"`
// PrettyString: DMA Protection 0 Limit Address
DMAProtLimit0 uint32 `json:"se_DMAProtLimit0"`
// PrettyString: DMA Protection 1 Base Address
DMAProtBase1 uint64 `json:"se_DMAProtBase1"`
// PrettyString: DMA Protection 2 Limit Address
DMAProtLimit1 uint64 `json:"se_DMAProtLimit1"`
PostIBBHash manifest.HashStructure `json:"se_PostIBBHash"`
IBBEntryPoint uint32 `json:"se_IBBEntry"`
DigestList manifest.HashList `json:"se_DigestList"`
OBBHash manifest.HashStructure `json:"se_OBBHash"`
Reserved2 [3]byte `require:"0" json:"se_Reserved2,omitempty"`
IBBSegments []IBBSegment `countType:"uint8" json:"se_IBBSegments,omitempty"`
}
type PBETValue uint8
// PBETValue returns the raw value of the timer setting.
func (pbet PBETValue) PBETValue() uint8 {
return uint8(pbet) & 0x0f
}
// Duration returns the value as time.Duration.
func (pbet PBETValue) Duration() time.Duration {
v := pbet.PBETValue()
if v == 0 {
return math.MaxInt64
}
return time.Second * time.Duration(5+v)
}
func (pbet *PBETValue) SetDuration(duration time.Duration) time.Duration {
v := duration.Nanoseconds()/time.Second.Nanoseconds() - 5
if v <= 0 {
v = 1
}
if v >= 16 {
v = 0
}
*pbet = PBETValue(v)
return pbet.Duration()
}
type SEFlags uint32
func (flags SEFlags) Reserved0() uint32 {
return uint32(flags & 0xffffffe0)
}
// PrettyString-true: BIOS supports Top Swap remediation action
// PrettyString-false: BIOS does not support Top Swap remediation action
func (flags SEFlags) SupportsTopSwapRemediation() bool {
return flags&0x10 != 0
}
// PrettyString-true: Leave Hierarchies enabled. Cap all PCRs on failure.
// PrettyString-false: Do not leave enabled. Disable all Hierarchies or deactivate on failure.
func (flags SEFlags) TPMFailureLeavesHierarchiesEnabled() bool {
return flags&0x08 != 0
}
// PrettyString-true: Extend Authority Measurements into the Authority PCR 7
// PrettyString-false: Do not extend into the Authority PCR 7
func (flags SEFlags) AuthorityMeasure() bool {
return flags&0x04 != 0
}
// PrettyString-true: Issue TPM Start-up from Locality 3
// PrettyString-false: Disabled
func (flags SEFlags) Locality3Startup() bool {
return flags&0x02 != 0
}
// PrettyString-true: Enable DMA Protection
// PrettyString-false: Disable DMA Protection
func (flags SEFlags) DMAProtection() bool {
return flags&0x01 != 0
}
type IBBSegment struct {
Reserved [2]byte `require:"0"`
Flags uint16
Base uint32
Size uint32
}
type CachingType uint8
const (
CachingTypeWriteProtect = CachingType(iota)
CachingTypeWriteBack
CachingTypeReserved0
CachingTypeReserved1
)
// String implements fmt.Stringer.
func (c CachingType) String() string {
switch c {
case CachingTypeWriteProtect:
return "write_protect"
case CachingTypeWriteBack:
return "write_back"
case CachingTypeReserved0:
return "value_0x02"
case CachingTypeReserved1:
return "value_0x03"
}
return fmt.Sprintf("unexpected_value_0x%02X", uint8(c))
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
//go:generate manifestcodegen
package bootpolicy
import (
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
)
// Signature contains the signature of the BPM.
type Signature struct {
StructInfo `id:"__PMSG__" version:"0x20" var0:"0" var1:"0"`
manifest.KeySignature `json:"sig_KeySignature"`
}

View File

@ -0,0 +1,201 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy
package bootpolicy
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewSignature returns a new instance of Signature with
// all default values set.
func NewSignature() *Signature {
s := &Signature{}
copy(s.StructInfo.ID[:], []byte(StructureIDSignature))
s.StructInfo.Version = 0x20
// Recursively initializing a child structure:
s.KeySignature = *manifest.NewKeySignature()
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *Signature) Validate() error {
// Recursively validating a child structure:
if err := s.KeySignature.Validate(); err != nil {
return fmt.Errorf("error on field 'KeySignature': %w", err)
}
return nil
}
// StructureIDSignature is the StructureID (in terms of
// the document #575623) of element 'Signature'.
const StructureIDSignature = "__PMSG__"
// GetStructInfo returns current value of StructInfo of the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *Signature) GetStructInfo() manifest.StructInfo {
return s.StructInfo
}
// SetStructInfo sets new value of StructInfo to the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *Signature) SetStructInfo(newStructInfo manifest.StructInfo) {
s.StructInfo = newStructInfo
}
// ReadFrom reads the Signature from 'r' in format defined in the document #575623.
func (s *Signature) ReadFrom(r io.Reader) (int64, error) {
var totalN int64
err := binary.Read(r, binary.LittleEndian, &s.StructInfo)
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(s.StructInfo))
n, err := s.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read data: %w", err)
}
totalN += n
return totalN, nil
}
// ReadDataFrom reads the Signature from 'r' excluding StructInfo,
// in format defined in the document #575623.
func (s *Signature) ReadDataFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// StructInfo (ManifestFieldType: structInfo)
{
// ReadDataFrom does not read Struct, use ReadFrom for that.
}
// KeySignature (ManifestFieldType: subStruct)
{
n, err := s.KeySignature.ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KeySignature': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *Signature) RehashRecursive() {
s.StructInfo.Rehash()
s.KeySignature.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *Signature) Rehash() {
s.Variable0 = 0
s.ElementSize = 0
}
// WriteTo writes the Signature into 'w' in format defined in
// the document #575623.
func (s *Signature) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// StructInfo (ManifestFieldType: structInfo)
{
n, err := s.StructInfo.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err)
}
totalN += int64(n)
}
// KeySignature (ManifestFieldType: subStruct)
{
n, err := s.KeySignature.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KeySignature': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// StructInfoSize returns the size in bytes of the value of field StructInfo
func (s *Signature) StructInfoTotalSize() uint64 {
return s.StructInfo.TotalSize()
}
// KeySignatureSize returns the size in bytes of the value of field KeySignature
func (s *Signature) KeySignatureTotalSize() uint64 {
return s.KeySignature.TotalSize()
}
// StructInfoOffset returns the offset in bytes of field StructInfo
func (s *Signature) StructInfoOffset() uint64 {
return 0
}
// KeySignatureOffset returns the offset in bytes of field KeySignature
func (s *Signature) KeySignatureOffset() uint64 {
return s.StructInfoOffset() + s.StructInfoTotalSize()
}
// Size returns the total size of the Signature.
func (s *Signature) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.StructInfoTotalSize()
size += s.KeySignatureTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *Signature) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Signature", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is structInfo
lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo))
// ManifestFieldType is subStruct
lines = append(lines, pretty.SubValue(depth+1, "Key Signature", "", &s.KeySignature))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

Binary file not shown.

View File

@ -0,0 +1,47 @@
//go:generate manifestcodegen
package bootpolicy
import (
"fmt"
"time"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
)
type TXT struct {
StructInfo `id:"__TXTS__" version:"0x21" var0:"0" var1:"uint16(s.TotalSize())"`
Reserved0 [1]byte `require:"0" json:"txt_Reserved0,omitempty"`
SetNumber [1]byte `require:"0" json:"txt_SetNumer,omitempty"`
SInitMinSVNAuth uint8 `json:"txt_SVN"`
Reserved1 [1]byte `require:"0" json:"txt_Reserved1,omitempty"`
ControlFlags TXTControlFlags `json:"txt_Flags"`
PwrDownInterval Duration16In5Sec `json:"tx_PwrDownInterval"`
// PrettyString: PTT CMOS Offset 0
PTTCMOSOffset0 uint8 `default:"126" json:"txt_PTTCMOSOffset0"`
// PrettyString: PTT CMOS Offset 1
PTTCMOSOffset1 uint8 `default:"127" json:"txt_PTTCMOSOffset1"`
ACPIBaseOffset uint16 `default:"0x400" json:"txt_ACPIBaseOffset,omitempty"`
Reserved2 [2]byte `json:"txt_Reserved2,omitempty"`
// PrettyString: ACPI MMIO Offset
PwrMBaseOffset uint32 `default:"0xFE000000" json:"txt_PwrMBaseOffset,omitempty"`
DigestList manifest.HashList `json:"txt_DigestList"`
Reserved3 [3]byte `require:"0" json:"txt_Reserved3,omitempty"`
SegmentCount uint8 `require:"0" json:"txt_SegmentCount,omitempty"`
}
// Duration16In5Sec exports the custom type Duration16In5Sec
type Duration16In5Sec uint16
// Duration calculates a given time in multiple of 5 seconds.
func (d Duration16In5Sec) Duration() time.Duration {
return time.Second * 5 * time.Duration(d)
}
func (d Duration16In5Sec) String() string {
if d == 0 {
return "0 (infinite)"
}
return fmt.Sprintf("%d (%s)", d, d.Duration().String())
}

View File

@ -0,0 +1,112 @@
//go:generate manifestcodegen
package bootpolicy
import (
"fmt"
)
type TXTControlFlags uint32
func (flags TXTControlFlags) ExecutionProfile() ExecutionProfile {
return ExecutionProfile(flags & 0x1f)
}
type ExecutionProfile uint8
const (
ExecutionProfileA = ExecutionProfile(iota)
ExecutionProfileB
ExecutionProfileC
)
// String just implements fmt.Stringer.
func (p ExecutionProfile) String() string {
switch p {
case ExecutionProfileA:
return `A (use default selection based on differentation between clients, UP, and MP servers)`
case ExecutionProfileB:
return `B (use "Server model": rely on BIOS to configure topoligy; do not use ACHECK)`
case ExecutionProfileC:
return `C (use "Client model": do not measure BIOS into D-PCRs; use ACHECK-based alias check)`
}
return fmt.Sprintf("unexpected_execution_profile_value_0x%02X", uint8(p))
}
func (flags TXTControlFlags) MemoryScrubbingPolicy() MemoryScrubbingPolicy {
return MemoryScrubbingPolicy((flags >> 5) & 0x3)
}
type MemoryScrubbingPolicy uint8
const (
MemoryScrubbingPolicyDefault = MemoryScrubbingPolicy(iota)
MemoryScrubbingPolicyBIOS
MemoryScrubbingPolicySACM
)
// String implements fmt.Stringer.
func (policy MemoryScrubbingPolicy) String() string {
switch policy {
case MemoryScrubbingPolicyDefault:
return "BIOS if verified or backup action othersize"
case MemoryScrubbingPolicyBIOS:
return "BIOS"
case MemoryScrubbingPolicySACM:
return "S-ACM"
}
return fmt.Sprintf("unexpected_value_0x%02X", uint8(policy))
}
func (flags TXTControlFlags) BackupActionPolicy() BackupActionPolicy {
return BackupActionPolicy((flags >> 7) & 0x3)
}
type BackupActionPolicy uint8
const (
BackupActionPolicyDefault = BackupActionPolicy(iota)
BackupActionPolicyForceMemoryPowerDown
BackupActionPolicyForceBtGUnbreakableShutdown
)
// String implements fmt.Stringer.
func (policy BackupActionPolicy) String() string {
switch policy {
case BackupActionPolicyDefault:
return "memory power down if profile D or BtG unbreakable shutdown otherwise"
case BackupActionPolicyForceMemoryPowerDown:
return "memory power down"
case BackupActionPolicyForceBtGUnbreakableShutdown:
return "BtG unbreakable shutdown"
}
return fmt.Sprintf("unexpected_value_0x%02X", uint8(policy))
}
// PrettyString-true: Default setting. S-ACM is requested to extend static PCRs
// PrettyString-false: S-ACM is not requested to extend static PCRs
func (flags TXTControlFlags) IsSACMRequestedToExtendStaticPCRs() bool {
return (flags>>9)&0x01 == 0
}
func (flags TXTControlFlags) ResetAUXControl() ResetAUXControl {
return ResetAUXControl((flags >> 31) & 0x01)
}
type ResetAUXControl uint8
const (
ResetAUXControlResetAUXIndex = ResetAUXControl(iota)
ResetAUXControlDeleteAUXIndex
)
// String implements fmt.Stringer.
func (c ResetAUXControl) String() string {
switch c {
case ResetAUXControlResetAUXIndex:
return "AUX reset leaf will reset AUX index"
case ResetAUXControlDeleteAUXIndex:
return "AUX reset leaf will delete AUX index"
}
return fmt.Sprintf("unexpected_value_0x%02X", uint8(c))
}

View File

@ -0,0 +1,63 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy
package bootpolicy
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags BackupActionPolicy) PrettyString(depth uint, withHeader bool) string {
return flags.String()
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags ExecutionProfile) PrettyString(depth uint, withHeader bool) string {
return flags.String()
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags MemoryScrubbingPolicy) PrettyString(depth uint, withHeader bool) string {
return flags.String()
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags ResetAUXControl) PrettyString(depth uint, withHeader bool) string {
return flags.String()
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags TXTControlFlags) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "TXT Control Flags", flags))
}
lines = append(lines, pretty.SubValue(depth+1, "Execution Profile", "", flags.ExecutionProfile()))
lines = append(lines, pretty.SubValue(depth+1, "Memory Scrubbing Policy", "", flags.MemoryScrubbingPolicy()))
lines = append(lines, pretty.SubValue(depth+1, "Backup Action Policy", "", flags.BackupActionPolicy()))
if flags.IsSACMRequestedToExtendStaticPCRs() {
lines = append(lines, pretty.SubValue(depth+1, "Is SACM Requested To Extend Static PC Rs", "Default setting. S-ACM is requested to extend static PCRs", true))
} else {
lines = append(lines, pretty.SubValue(depth+1, "Is SACM Requested To Extend Static PC Rs", "S-ACM is not requested to extend static PCRs", false))
}
lines = append(lines, pretty.SubValue(depth+1, "Reset AUX Control", "", flags.ResetAUXControl()))
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,647 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy
package bootpolicy
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewTXT returns a new instance of TXT with
// all default values set.
func NewTXT() *TXT {
s := &TXT{}
copy(s.StructInfo.ID[:], []byte(StructureIDTXT))
s.StructInfo.Version = 0x21
// Set through tag "default":
s.PTTCMOSOffset0 = 126
// Set through tag "default":
s.PTTCMOSOffset1 = 127
// Set through tag "default":
s.ACPIBaseOffset = 0x400
// Set through tag "default":
s.PwrMBaseOffset = 0xFE000000
// Recursively initializing a child structure:
s.DigestList = *manifest.NewHashList()
// Set through tag "required":
s.SegmentCount = 0
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *TXT) Validate() error {
// See tag "require"
for idx := range s.Reserved0 {
if s.Reserved0[idx] != 0 {
return fmt.Errorf("'Reserved0[%d]' is expected to be 0, but it is %v", idx, s.Reserved0[idx])
}
}
// See tag "require"
for idx := range s.SetNumber {
if s.SetNumber[idx] != 0 {
return fmt.Errorf("'SetNumber[%d]' is expected to be 0, but it is %v", idx, s.SetNumber[idx])
}
}
// See tag "require"
for idx := range s.Reserved1 {
if s.Reserved1[idx] != 0 {
return fmt.Errorf("'Reserved1[%d]' is expected to be 0, but it is %v", idx, s.Reserved1[idx])
}
}
// Recursively validating a child structure:
if err := s.DigestList.Validate(); err != nil {
return fmt.Errorf("error on field 'DigestList': %w", err)
}
// See tag "require"
for idx := range s.Reserved3 {
if s.Reserved3[idx] != 0 {
return fmt.Errorf("'Reserved3[%d]' is expected to be 0, but it is %v", idx, s.Reserved3[idx])
}
}
// See tag "require"
if s.SegmentCount != 0 {
return fmt.Errorf("field 'SegmentCount' expects value '0', but has %v", s.SegmentCount)
}
return nil
}
// StructureIDTXT is the StructureID (in terms of
// the document #575623) of element 'TXT'.
const StructureIDTXT = "__TXTS__"
// GetStructInfo returns current value of StructInfo of the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *TXT) GetStructInfo() manifest.StructInfo {
return s.StructInfo
}
// SetStructInfo sets new value of StructInfo to the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *TXT) SetStructInfo(newStructInfo manifest.StructInfo) {
s.StructInfo = newStructInfo
}
// ReadFrom reads the TXT from 'r' in format defined in the document #575623.
func (s *TXT) ReadFrom(r io.Reader) (int64, error) {
var totalN int64
err := binary.Read(r, binary.LittleEndian, &s.StructInfo)
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(s.StructInfo))
n, err := s.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read data: %w", err)
}
totalN += n
return totalN, nil
}
// ReadDataFrom reads the TXT from 'r' excluding StructInfo,
// in format defined in the document #575623.
func (s *TXT) ReadDataFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// StructInfo (ManifestFieldType: structInfo)
{
// ReadDataFrom does not read Struct, use ReadFrom for that.
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// SetNumber (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Read(r, binary.LittleEndian, s.SetNumber[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'SetNumber': %w", err)
}
totalN += int64(n)
}
// SInitMinSVNAuth (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.SInitMinSVNAuth)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'SInitMinSVNAuth': %w", err)
}
totalN += int64(n)
}
// Reserved1 (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Read(r, binary.LittleEndian, s.Reserved1[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved1': %w", err)
}
totalN += int64(n)
}
// ControlFlags (ManifestFieldType: endValue)
{
n, err := 4, binary.Read(r, binary.LittleEndian, &s.ControlFlags)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'ControlFlags': %w", err)
}
totalN += int64(n)
}
// PwrDownInterval (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.PwrDownInterval)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'PwrDownInterval': %w", err)
}
totalN += int64(n)
}
// PTTCMOSOffset0 (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.PTTCMOSOffset0)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'PTTCMOSOffset0': %w", err)
}
totalN += int64(n)
}
// PTTCMOSOffset1 (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.PTTCMOSOffset1)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'PTTCMOSOffset1': %w", err)
}
totalN += int64(n)
}
// ACPIBaseOffset (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.ACPIBaseOffset)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'ACPIBaseOffset': %w", err)
}
totalN += int64(n)
}
// Reserved2 (ManifestFieldType: arrayStatic)
{
n, err := 2, binary.Read(r, binary.LittleEndian, s.Reserved2[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved2': %w", err)
}
totalN += int64(n)
}
// PwrMBaseOffset (ManifestFieldType: endValue)
{
n, err := 4, binary.Read(r, binary.LittleEndian, &s.PwrMBaseOffset)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'PwrMBaseOffset': %w", err)
}
totalN += int64(n)
}
// DigestList (ManifestFieldType: subStruct)
{
n, err := s.DigestList.ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'DigestList': %w", err)
}
totalN += int64(n)
}
// Reserved3 (ManifestFieldType: arrayStatic)
{
n, err := 3, binary.Read(r, binary.LittleEndian, s.Reserved3[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved3': %w", err)
}
totalN += int64(n)
}
// SegmentCount (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.SegmentCount)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'SegmentCount': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *TXT) RehashRecursive() {
s.StructInfo.Rehash()
s.DigestList.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *TXT) Rehash() {
s.Variable0 = 0
s.ElementSize = uint16(s.TotalSize())
}
// WriteTo writes the TXT into 'w' in format defined in
// the document #575623.
func (s *TXT) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// StructInfo (ManifestFieldType: structInfo)
{
n, err := s.StructInfo.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err)
}
totalN += int64(n)
}
// Reserved0 (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved0[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved0': %w", err)
}
totalN += int64(n)
}
// SetNumber (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Write(w, binary.LittleEndian, s.SetNumber[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'SetNumber': %w", err)
}
totalN += int64(n)
}
// SInitMinSVNAuth (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.SInitMinSVNAuth)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'SInitMinSVNAuth': %w", err)
}
totalN += int64(n)
}
// Reserved1 (ManifestFieldType: arrayStatic)
{
n, err := 1, binary.Write(w, binary.LittleEndian, s.Reserved1[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved1': %w", err)
}
totalN += int64(n)
}
// ControlFlags (ManifestFieldType: endValue)
{
n, err := 4, binary.Write(w, binary.LittleEndian, &s.ControlFlags)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'ControlFlags': %w", err)
}
totalN += int64(n)
}
// PwrDownInterval (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.PwrDownInterval)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PwrDownInterval': %w", err)
}
totalN += int64(n)
}
// PTTCMOSOffset0 (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.PTTCMOSOffset0)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PTTCMOSOffset0': %w", err)
}
totalN += int64(n)
}
// PTTCMOSOffset1 (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.PTTCMOSOffset1)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PTTCMOSOffset1': %w", err)
}
totalN += int64(n)
}
// ACPIBaseOffset (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.ACPIBaseOffset)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'ACPIBaseOffset': %w", err)
}
totalN += int64(n)
}
// Reserved2 (ManifestFieldType: arrayStatic)
{
n, err := 2, binary.Write(w, binary.LittleEndian, s.Reserved2[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved2': %w", err)
}
totalN += int64(n)
}
// PwrMBaseOffset (ManifestFieldType: endValue)
{
n, err := 4, binary.Write(w, binary.LittleEndian, &s.PwrMBaseOffset)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PwrMBaseOffset': %w", err)
}
totalN += int64(n)
}
// DigestList (ManifestFieldType: subStruct)
{
n, err := s.DigestList.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'DigestList': %w", err)
}
totalN += int64(n)
}
// Reserved3 (ManifestFieldType: arrayStatic)
{
n, err := 3, binary.Write(w, binary.LittleEndian, s.Reserved3[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved3': %w", err)
}
totalN += int64(n)
}
// SegmentCount (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.SegmentCount)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'SegmentCount': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// StructInfoSize returns the size in bytes of the value of field StructInfo
func (s *TXT) StructInfoTotalSize() uint64 {
return s.StructInfo.TotalSize()
}
// Reserved0Size returns the size in bytes of the value of field Reserved0
func (s *TXT) Reserved0TotalSize() uint64 {
return 1
}
// SetNumberSize returns the size in bytes of the value of field SetNumber
func (s *TXT) SetNumberTotalSize() uint64 {
return 1
}
// SInitMinSVNAuthSize returns the size in bytes of the value of field SInitMinSVNAuth
func (s *TXT) SInitMinSVNAuthTotalSize() uint64 {
return 1
}
// Reserved1Size returns the size in bytes of the value of field Reserved1
func (s *TXT) Reserved1TotalSize() uint64 {
return 1
}
// ControlFlagsSize returns the size in bytes of the value of field ControlFlags
func (s *TXT) ControlFlagsTotalSize() uint64 {
return 4
}
// PwrDownIntervalSize returns the size in bytes of the value of field PwrDownInterval
func (s *TXT) PwrDownIntervalTotalSize() uint64 {
return 2
}
// PTTCMOSOffset0Size returns the size in bytes of the value of field PTTCMOSOffset0
func (s *TXT) PTTCMOSOffset0TotalSize() uint64 {
return 1
}
// PTTCMOSOffset1Size returns the size in bytes of the value of field PTTCMOSOffset1
func (s *TXT) PTTCMOSOffset1TotalSize() uint64 {
return 1
}
// ACPIBaseOffsetSize returns the size in bytes of the value of field ACPIBaseOffset
func (s *TXT) ACPIBaseOffsetTotalSize() uint64 {
return 2
}
// Reserved2Size returns the size in bytes of the value of field Reserved2
func (s *TXT) Reserved2TotalSize() uint64 {
return 2
}
// PwrMBaseOffsetSize returns the size in bytes of the value of field PwrMBaseOffset
func (s *TXT) PwrMBaseOffsetTotalSize() uint64 {
return 4
}
// DigestListSize returns the size in bytes of the value of field DigestList
func (s *TXT) DigestListTotalSize() uint64 {
return s.DigestList.TotalSize()
}
// Reserved3Size returns the size in bytes of the value of field Reserved3
func (s *TXT) Reserved3TotalSize() uint64 {
return 3
}
// SegmentCountSize returns the size in bytes of the value of field SegmentCount
func (s *TXT) SegmentCountTotalSize() uint64 {
return 1
}
// StructInfoOffset returns the offset in bytes of field StructInfo
func (s *TXT) StructInfoOffset() uint64 {
return 0
}
// Reserved0Offset returns the offset in bytes of field Reserved0
func (s *TXT) Reserved0Offset() uint64 {
return s.StructInfoOffset() + s.StructInfoTotalSize()
}
// SetNumberOffset returns the offset in bytes of field SetNumber
func (s *TXT) SetNumberOffset() uint64 {
return s.Reserved0Offset() + s.Reserved0TotalSize()
}
// SInitMinSVNAuthOffset returns the offset in bytes of field SInitMinSVNAuth
func (s *TXT) SInitMinSVNAuthOffset() uint64 {
return s.SetNumberOffset() + s.SetNumberTotalSize()
}
// Reserved1Offset returns the offset in bytes of field Reserved1
func (s *TXT) Reserved1Offset() uint64 {
return s.SInitMinSVNAuthOffset() + s.SInitMinSVNAuthTotalSize()
}
// ControlFlagsOffset returns the offset in bytes of field ControlFlags
func (s *TXT) ControlFlagsOffset() uint64 {
return s.Reserved1Offset() + s.Reserved1TotalSize()
}
// PwrDownIntervalOffset returns the offset in bytes of field PwrDownInterval
func (s *TXT) PwrDownIntervalOffset() uint64 {
return s.ControlFlagsOffset() + s.ControlFlagsTotalSize()
}
// PTTCMOSOffset0Offset returns the offset in bytes of field PTTCMOSOffset0
func (s *TXT) PTTCMOSOffset0Offset() uint64 {
return s.PwrDownIntervalOffset() + s.PwrDownIntervalTotalSize()
}
// PTTCMOSOffset1Offset returns the offset in bytes of field PTTCMOSOffset1
func (s *TXT) PTTCMOSOffset1Offset() uint64 {
return s.PTTCMOSOffset0Offset() + s.PTTCMOSOffset0TotalSize()
}
// ACPIBaseOffsetOffset returns the offset in bytes of field ACPIBaseOffset
func (s *TXT) ACPIBaseOffsetOffset() uint64 {
return s.PTTCMOSOffset1Offset() + s.PTTCMOSOffset1TotalSize()
}
// Reserved2Offset returns the offset in bytes of field Reserved2
func (s *TXT) Reserved2Offset() uint64 {
return s.ACPIBaseOffsetOffset() + s.ACPIBaseOffsetTotalSize()
}
// PwrMBaseOffsetOffset returns the offset in bytes of field PwrMBaseOffset
func (s *TXT) PwrMBaseOffsetOffset() uint64 {
return s.Reserved2Offset() + s.Reserved2TotalSize()
}
// DigestListOffset returns the offset in bytes of field DigestList
func (s *TXT) DigestListOffset() uint64 {
return s.PwrMBaseOffsetOffset() + s.PwrMBaseOffsetTotalSize()
}
// Reserved3Offset returns the offset in bytes of field Reserved3
func (s *TXT) Reserved3Offset() uint64 {
return s.DigestListOffset() + s.DigestListTotalSize()
}
// SegmentCountOffset returns the offset in bytes of field SegmentCount
func (s *TXT) SegmentCountOffset() uint64 {
return s.Reserved3Offset() + s.Reserved3TotalSize()
}
// Size returns the total size of the TXT.
func (s *TXT) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.StructInfoTotalSize()
size += s.Reserved0TotalSize()
size += s.SetNumberTotalSize()
size += s.SInitMinSVNAuthTotalSize()
size += s.Reserved1TotalSize()
size += s.ControlFlagsTotalSize()
size += s.PwrDownIntervalTotalSize()
size += s.PTTCMOSOffset0TotalSize()
size += s.PTTCMOSOffset1TotalSize()
size += s.ACPIBaseOffsetTotalSize()
size += s.Reserved2TotalSize()
size += s.PwrMBaseOffsetTotalSize()
size += s.DigestListTotalSize()
size += s.Reserved3TotalSize()
size += s.SegmentCountTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *TXT) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "TXT", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is structInfo
lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 0", "", &s.Reserved0))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Set Number", "", &s.SetNumber))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "S Init Min SVN Auth", "", &s.SInitMinSVNAuth))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 1", "", &s.Reserved1))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Control Flags", "", &s.ControlFlags))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Pwr Down Interval", "", &s.PwrDownInterval))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "PTT CMOS Offset 0", "", &s.PTTCMOSOffset0))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "PTT CMOS Offset 1", "", &s.PTTCMOSOffset1))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "ACPI Base Offset", "", &s.ACPIBaseOffset))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 2", "", &s.Reserved2))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "ACPI MMIO Offset", "", &s.PwrMBaseOffset))
// ManifestFieldType is subStruct
lines = append(lines, pretty.SubValue(depth+1, "Digest List", "", &s.DigestList))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 3", "", &s.Reserved3))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Segment Count", "", &s.SegmentCount))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags Duration16In5Sec) PrettyString(depth uint, withHeader bool) string {
return flags.String()
}

View File

@ -0,0 +1,16 @@
package manifest
var (
// StrictOrderCheck defines if elements order checks should be performed.
// For example in the Boot Policy Manifest elements could be in a wrong
// order. And we still can parse it, but in this way `*Offset` methods
// could be confusing, since they will show the offset as they will
// be written (not as they were parsed).
//
// We require a strict order because it is explicitly required
// in the documentation #575623:
//
// > The order of the elements and the order of the fields within each
// > element are architectural and must be followed.
StrictOrderCheck = true
)

View File

@ -0,0 +1,17 @@
//go:generate manifestcodegen
package manifest
import "github.com/google/go-tpm/tpm2"
// HashStructure describes a digest.
type HashStructure struct {
HashAlg tpm2.Algorithm `default:"0x10" json:"hs_Alg"`
HashBuffer []byte `json:"hs_Buffer"`
}
// HashList describes multiple digests
type HashList struct {
Size uint16 `rehashValue:"TotalSize()"`
List []HashStructure
}

View File

@ -0,0 +1,333 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest
package manifest
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
)
// NewHashList returns a new instance of HashList with
// all default values set.
func NewHashList() *HashList {
s := &HashList{}
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *HashList) Validate() error {
// See tag "rehashValue"
{
expectedValue := uint16(s.TotalSize())
if s.Size != expectedValue {
return fmt.Errorf("field 'Size' expects write-value '%v', but has %v", expectedValue, s.Size)
}
}
return nil
}
// ReadFrom reads the HashList from 'r' in format defined in the document #575623.
func (s *HashList) ReadFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// Size (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.Size)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Size': %w", err)
}
totalN += int64(n)
}
// List (ManifestFieldType: list)
{
var count uint16
err := binary.Read(r, binary.LittleEndian, &count)
if err != nil {
return totalN, fmt.Errorf("unable to read the count for field 'List': %w", err)
}
totalN += int64(binary.Size(count))
s.List = make([]HashStructure, count)
for idx := range s.List {
n, err := s.List[idx].ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'List[%d]': %w", idx, err)
}
totalN += int64(n)
}
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *HashList) RehashRecursive() {
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *HashList) Rehash() {
s.Size = uint16(s.TotalSize())
}
// WriteTo writes the HashList into 'w' in format defined in
// the document #575623.
func (s *HashList) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// Size (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.Size)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Size': %w", err)
}
totalN += int64(n)
}
// List (ManifestFieldType: list)
{
count := uint16(len(s.List))
err := binary.Write(w, binary.LittleEndian, &count)
if err != nil {
return totalN, fmt.Errorf("unable to write the count for field 'List': %w", err)
}
totalN += int64(binary.Size(count))
for idx := range s.List {
n, err := s.List[idx].WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'List[%d]': %w", idx, err)
}
totalN += int64(n)
}
}
return totalN, nil
}
// SizeSize returns the size in bytes of the value of field Size
func (s *HashList) SizeTotalSize() uint64 {
return 2
}
// ListSize returns the size in bytes of the value of field List
func (s *HashList) ListTotalSize() uint64 {
var size uint64
size += uint64(binary.Size(uint16(0)))
for idx := range s.List {
size += s.List[idx].TotalSize()
}
return size
}
// SizeOffset returns the offset in bytes of field Size
func (s *HashList) SizeOffset() uint64 {
return 0
}
// ListOffset returns the offset in bytes of field List
func (s *HashList) ListOffset() uint64 {
return s.SizeOffset() + s.SizeTotalSize()
}
// Size returns the total size of the HashList.
func (s *HashList) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.SizeTotalSize()
size += s.ListTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *HashList) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Hash List", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Size", "", &s.Size))
// ManifestFieldType is list
lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("List: Array of \"Hash Structure\" of length %d", len(s.List)), s.List))
for i := 0; i < len(s.List); i++ {
lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.List[i].PrettyString(depth+2, true)))
}
if depth < 1 {
lines = append(lines, "")
}
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}
// NewHashStructure returns a new instance of HashStructure with
// all default values set.
func NewHashStructure() *HashStructure {
s := &HashStructure{}
// Set through tag "default":
s.HashAlg = 0x10
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *HashStructure) Validate() error {
return nil
}
// ReadFrom reads the HashStructure from 'r' in format defined in the document #575623.
func (s *HashStructure) ReadFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// HashAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.HashAlg)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'HashAlg': %w", err)
}
totalN += int64(n)
}
// HashBuffer (ManifestFieldType: arrayDynamic)
{
var size uint16
err := binary.Read(r, binary.LittleEndian, &size)
if err != nil {
return totalN, fmt.Errorf("unable to the read size of field 'HashBuffer': %w", err)
}
totalN += int64(binary.Size(size))
s.HashBuffer = make([]byte, size)
n, err := len(s.HashBuffer), binary.Read(r, binary.LittleEndian, s.HashBuffer)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'HashBuffer': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *HashStructure) RehashRecursive() {
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *HashStructure) Rehash() {
}
// WriteTo writes the HashStructure into 'w' in format defined in
// the document #575623.
func (s *HashStructure) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// HashAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.HashAlg)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'HashAlg': %w", err)
}
totalN += int64(n)
}
// HashBuffer (ManifestFieldType: arrayDynamic)
{
size := uint16(len(s.HashBuffer))
err := binary.Write(w, binary.LittleEndian, size)
if err != nil {
return totalN, fmt.Errorf("unable to write the size of field 'HashBuffer': %w", err)
}
totalN += int64(binary.Size(size))
n, err := len(s.HashBuffer), binary.Write(w, binary.LittleEndian, s.HashBuffer)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'HashBuffer': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// HashAlgSize returns the size in bytes of the value of field HashAlg
func (s *HashStructure) HashAlgTotalSize() uint64 {
return 2
}
// HashBufferSize returns the size in bytes of the value of field HashBuffer
func (s *HashStructure) HashBufferTotalSize() uint64 {
size := uint64(binary.Size(uint16(0)))
size += uint64(len(s.HashBuffer))
return size
}
// HashAlgOffset returns the offset in bytes of field HashAlg
func (s *HashStructure) HashAlgOffset() uint64 {
return 0
}
// HashBufferOffset returns the offset in bytes of field HashBuffer
func (s *HashStructure) HashBufferOffset() uint64 {
return s.HashAlgOffset() + s.HashAlgTotalSize()
}
// Size returns the total size of the HashStructure.
func (s *HashStructure) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.HashAlgTotalSize()
size += s.HashBufferTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *HashStructure) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Hash Structure", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Hash Alg", "", &s.HashAlg))
// ManifestFieldType is arrayDynamic
lines = append(lines, pretty.SubValue(depth+1, "Hash Buffer", "", &s.HashBuffer))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,26 @@
package main
import (
"fmt"
"log"
"os"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
)
func assertNoError(err error) {
if err != nil {
log.Fatal(err)
}
}
func main() {
f, err := os.Open(os.Args[1])
assertNoError(err)
m := &bootpolicy.Manifest{}
_, err = m.ReadFrom(f)
assertNoError(err)
fmt.Printf("%s", m.PrettyString(0, true))
}

View File

@ -0,0 +1,140 @@
package main
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/pkg/analyze"
)
func assertNoError(err error) {
if err != nil {
log.Fatal(err)
}
}
func deleteGeneratedFiles(dirPath string) error {
files, err := ioutil.ReadDir(dirPath)
if err != nil {
return fmt.Errorf("unable to open '%s' as dir: %w", dirPath, err)
}
for _, file := range files {
if file.IsDir() || !strings.HasSuffix(file.Name(), "_manifestcodegen.go") {
continue
}
path := filepath.Join(dirPath, file.Name())
err := os.Remove(path)
if err != nil {
return fmt.Errorf("unable to delete file '%s': %w", path, err)
}
}
return nil
}
func replaceInFiles(dirPath, oldValue, newValue string) {
// ugly terrible hack to workaround versioning support
// TODO: fix the importer to recognize `/v2/` as version, not as path
files, err := ioutil.ReadDir(dirPath)
assertNoError(err)
for _, file := range files {
if !strings.HasSuffix(file.Name(), ".go") {
continue
}
filePath := filepath.Join(dirPath, file.Name())
contents, err := ioutil.ReadFile(filePath)
assertNoError(err)
contents = bytes.Replace(contents, []byte(oldValue), []byte(newValue), -1)
err = ioutil.WriteFile(filePath, contents, 0640)
assertNoError(err)
}
}
func processPath(path string, isCheck, enableTracing bool) error {
if !isCheck {
if stat, err := os.Stat(path); err == nil && stat.IsDir() {
err := deleteGeneratedFiles(path)
if err != nil {
return fmt.Errorf("unable to delete old generated files: %w", err)
}
}
// ugly terrible hack to workaround versioning support
// TODO: fix the importer to recognize `/v2/` as version, not as path
replaceInFiles(path, "converged-security-suite/v2/pkg", "converged-security-suite/pkg")
}
// ugly terrible hack to workaround versioning support
// TODO: fix the importer to recognize `/v2/` as version, not as path
os.Setenv("GO111MODULE", "off")
var goPaths []string
if gopathEnv := os.Getenv("GOPATH"); gopathEnv != "" {
goPaths = filepath.SplitList(gopathEnv)
} else {
homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("unable to determine the homedir: %w", err)
}
goPaths = append(goPaths, filepath.Join(homeDir, "go"))
}
dirInfo, err := analyze.Scan(path, goPaths)
if err != nil {
return fmt.Errorf("unable to analyze path '%s': %w", path, err)
}
for _, fileInfo := range dirInfo.Files {
err := generateMethodsFile(*fileInfo, isCheck, enableTracing)
if err != nil {
return err
}
}
if !isCheck {
// ugly terrible hack to workaround versioning support
// TODO: fix the importer to recognize `/v2/` as version, not as path
replaceInFiles(path, "converged-security-suite/pkg", "converged-security-suite/v2/pkg")
}
return nil
}
func main() {
checkFlag := flag.Bool("check", false, "generate with tracing code")
traceFlag := flag.Bool("trace", false, "generate with tracing code")
flag.Parse()
var paths []string
switch {
case flag.NArg() > 0:
paths = append(paths, flag.Args()...)
case os.Getenv("GOFILE") != "":
paths = append(paths, os.Getenv("GOFILE"))
default:
paths = append(paths, ".")
}
errorCount := 0
for _, path := range paths {
err := processPath(path, *checkFlag, *traceFlag)
if err != nil {
log.Printf("an error: %v", err)
errorCount++
}
}
if errorCount != 0 {
os.Exit(1)
}
}

View File

@ -0,0 +1,112 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"reflect"
"strings"
"text/template"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/pkg/analyze"
"github.com/fatih/camelcase"
)
type methodsData struct {
analyze.File
EnableTracing bool
}
// generateMethodsFile generates a file using the template above.
//
// The file name is constructed from the original file name, but with
// adding suffix '_manifestcodegen' before the file extension.
func generateMethodsFile(file analyze.File, isCheck, enableTracing bool) error {
funcsMap := map[string]interface{}{
"add": func(a, b int) int { return a + b },
"ternary": func(cond bool, a, b interface{}) interface{} {
if cond {
return a
}
return b
},
"isNil": func(v interface{}) bool {
return reflect.ValueOf(v).IsNil()
},
"camelcaseToSentence": func(in string) string {
return strings.Join(camelcase.Split(in), " ")
},
}
if len(file.Structs) == 0 && len(file.BasicNamedTypes) == 0 {
return nil
}
templateMethods, err := template.New("methods").Funcs(funcsMap).Parse(templateMethods)
if err != nil {
return fmt.Errorf("unable to parse the template: %w", err)
}
if ext := path.Ext(file.Path); ext != ".go" {
return fmt.Errorf("invalid extension: '%s'", ext)
}
generatedFile := fmt.Sprintf("%s_manifestcodegen.go",
file.Path[:len(file.Path)-3],
)
var outFile string
if isCheck {
outFile = generatedFile + "-check.go"
} else {
outFile = generatedFile
}
f, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("unable to open file '%s' for writing: %w", outFile, err)
}
defer func() {
if isCheck {
err := os.Remove(outFile)
if err != nil {
log.Printf("unable to remove file '%s': %v\n", outFile, err)
}
}
err := f.Close()
if err != nil {
log.Printf("unable to close file '%s': %v\n", outFile, err)
}
}()
err = templateMethods.Execute(f, methodsData{
File: file,
EnableTracing: enableTracing,
})
if err != nil {
return fmt.Errorf("unable to write: %w", err)
}
err = exec.Command("go", "fmt", outFile).Run()
if err != nil {
return fmt.Errorf("unable to format file '%s': %w", outFile, err)
}
if isCheck {
b0, err := ioutil.ReadFile(outFile)
if err != nil {
return fmt.Errorf("unable to read a temp file '%s'", outFile)
}
b1, err := ioutil.ReadFile(generatedFile)
if err != nil {
return fmt.Errorf("unable to read file '%s'", generatedFile)
}
if bytes.Compare(b0, b1) != 0 {
return fmt.Errorf("file '%s' is not up-to-date; please run command: "+
"go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen %s",
generatedFile, file.Package.Path())
}
}
return nil
}

View File

@ -0,0 +1,548 @@
package main // TODO: replace this file with "embed", when it will be released: https://github.com/golang/go/issues/41191
const templateMethods = `// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen {{ .Package.Path }}
package {{ .Package.Name }}
import (
{{- if not .EnableTracing }}
"encoding/binary"
{{- else }}
binary "github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/tracedbinary"
{{- end }}
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
{{- if ne .Package.Name "manifest" }}
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
{{- end }}
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
{{- if ne .Package.Name "manifest" }}
_ = manifest.StructInfo{}
{{- end }}
)
{{- $manifestRootPath := ternary (ne .Package.Name "manifest") "manifest." "" }}
{{- $enableTracing := .EnableTracing }}
{{- range $index, $struct := .Structs }}
// New{{ $struct.Name }} returns a new instance of {{ $struct.Name }} with
// all default values set.
func New{{ $struct.Name }}() *{{ $struct.Name }} {
s := &{{ $struct.Name }}{}
{{- if ne $struct.ElementStructID "" }}
copy(s.StructInfo.ID[:], []byte(StructureID{{ $struct.Name }}))
s.StructInfo.Version = {{ $struct.ElementStructVersion }}
{{- end }}
{{- range $index, $field := $struct.Fields }}
{{- $fieldType := $field.ManifestFieldType.String }}
{{- if and (not $field.IsSlice) (not $field.IsPointer) (or (eq $fieldType "element") (eq $fieldType "subStruct")) }}
// Recursively initializing a child structure:
s.{{ $field.Name }} = *{{ $field.AccessPrefix }}New{{ $field.ItemTypeName }}()
{{- end }}
{{- $defaultValue := ternary (ne $field.RequiredValue "") $field.RequiredValue $field.DefaultValue }}
{{- $defaultValueSource := ternary (ne $field.RequiredValue "") "required" "default" }}
{{- if ne $defaultValue "" }}
{{- if eq $fieldType "arrayStatic" }}
{{- if ne $defaultValue "0" }}
// Set through tag "{{ $defaultValueSource }}":
for idx := range s.{{ $field.Name }} {
s.{{ $field.Name }}[idx] = {{ $defaultValue }}
}
{{- end }}
{{- else }}
// Set through tag "{{ $defaultValueSource }}":
s.{{ $field.Name }} = {{ $defaultValue }}
{{- end }}
{{- end }}
{{- end }}
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *{{ $struct.Name }}) Validate() error {
{{- range $index, $field := $struct.Fields }}
{{- $fieldType := $field.ManifestFieldType.String }}
{{- if and (not $field.IsSlice) (not $field.IsPointer) (or (eq $fieldType "element") (eq $fieldType "subStruct")) }}
// Recursively validating a child structure:
if err := s.{{ $field.Name }}.Validate(); err != nil {
return fmt.Errorf("error on field '{{ $field.Name }}': %w", err)
}
{{- end }}
{{- if ne $field.RequiredValue "" }}
// See tag "require"
{{- if eq $fieldType "arrayStatic" }}
for idx := range s.{{ $field.Name }} {
if s.{{ $field.Name }}[idx] != {{ $field.RequiredValue }} {
return fmt.Errorf("'{{ $field.Name }}[%d]' is expected to be {{ $field.RequiredValue }}, but it is %v", idx, s.{{ $field.Name }}[idx])
}
}
{{- else }}
if s.{{ $field.Name }} != {{ $field.RequiredValue }} {
return fmt.Errorf("field '{{ $field.Name }}' expects value '{{ $field.RequiredValue }}', but has %v", s.{{ $field.Name }})
}
{{- end }}
{{- end }}
{{- if ne $field.RehashValue "" }}
// See tag "rehashValue"
{
expectedValue := {{ $field.Type }}(s.{{ $field.RehashValue }})
if s.{{ $field.Name }} != expectedValue {
return fmt.Errorf("field '{{ $field.Name }}' expects write-value '%v', but has %v", expectedValue, s.{{ $field.Name }})
}
}
{{- end }}
{{- end }}
return nil
}
{{- if ne $struct.ElementStructID "" }}
// StructureID{{ $struct.Name }} is the StructureID (in terms of
// the document #575623) of element '{{ $struct.Name }}'.
const StructureID{{ $struct.Name }} = "{{ $struct.ElementStructID }}"
// GetStructInfo returns current value of StructInfo of the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *{{ $struct.Name }}) GetStructInfo() {{ $manifestRootPath }}StructInfo {
return s.StructInfo
}
// SetStructInfo sets new value of StructInfo to the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *{{ $struct.Name }}) SetStructInfo(newStructInfo {{ $manifestRootPath }}StructInfo) {
s.StructInfo = newStructInfo
}
{{- end }}
{{- if $struct.IsElementsContainer }}
// fieldIndexByStructID returns the position index within
// structure {{ $struct.Name }} of the field by its StructureID
// (see document #575623, an example of StructureID value is "__KEYM__").
func (_ {{ $struct.Name }}) fieldIndexByStructID(structID string) int {
switch structID {
{{- range $index, $field := $struct.Fields }}
case StructureID{{ $field.Struct.Name }}:
return {{ $index }}
{{- end }}
}
return -1
}
// fieldNameByIndex returns the name of the field by its position number
// within structure {{ $struct.Name }}.
func (_ {{ $struct.Name }}) fieldNameByIndex(fieldIndex int) string {
switch fieldIndex {
{{- range $index, $field := $struct.Fields }}
case {{ $index }}:
return "{{ $field.Name }}"
{{- end }}
}
return fmt.Sprintf("invalidFieldIndex_%d", fieldIndex)
}
// ReadFrom reads the {{ $struct.Name }} from 'r' in format defined in the document #575623.
func (s *{{ $struct.Name }}) ReadFrom(r io.Reader) (int64, error) {
var missingFieldsByIndices = [{{ len $struct.Fields }}]bool{
{{- range $index, $field := $struct.Fields }}
{{- if and (not $field.IsSlice) (not $field.IsPointer) }}
{{ $index }}: true,
{{- end }}
{{- end }}
}
var totalN int64
previousFieldIndex := int(-1)
for {
var structInfo {{ $manifestRootPath }}StructInfo
err := binary.Read(r, binary.LittleEndian, &structInfo)
if err == io.EOF || err == io.ErrUnexpectedEOF {
return totalN, nil
}
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
{{- if $enableTracing }}
fmt.Printf("%s header parsed, TotalN is %d -> %d\n", structInfo.ID.String(), totalN, totalN + int64(binary.Size(structInfo))){{- end}}
totalN += int64(binary.Size(structInfo))
structID := structInfo.ID.String()
fieldIndex := s.fieldIndexByStructID(structID)
if fieldIndex < 0 {
// TODO: report error "unknown structure ID: '"+structID+"'"
continue
}
if {{ $manifestRootPath }}StrictOrderCheck && fieldIndex < previousFieldIndex {
return totalN, fmt.Errorf("invalid order of fields (%d < %d): structure '%s' is out of order", fieldIndex, previousFieldIndex, structID)
}
missingFieldsByIndices[fieldIndex] = false
var n int64
switch structID {
{{- range $index, $field := $struct.Fields }}
case StructureID{{ $field.Struct.Name }}:
{{- if $field.IsSlice }}
var el {{ $field.Struct.TypeSpec.Name.String }}
el.SetStructInfo(structInfo)
n, err = el.ReadDataFrom(r)
s.{{ $field.Name }} = append(s.{{ $field.Name }}, el)
{{- else }}
if fieldIndex == previousFieldIndex {
return totalN, fmt.Errorf("field '{{ $field.Name }}' is not a slice, but multiple elements found")
}
{{- if $field.IsPointer }}
s.{{ $field.Name }} = &{{ $field.Struct.TypeSpec.Name.String }}{}
{{- end }}
s.{{ $field.Name }}.SetStructInfo(structInfo)
n, err = s.{{ $field.Name }}.ReadDataFrom(r)
{{- end }}
if err != nil {
return totalN, fmt.Errorf("unable to read field {{ $field.Name }} at %d: %w", totalN, err)
}
{{- end }}
default:
return totalN, fmt.Errorf("there is no field with structure ID '%s' in {{ $struct.Name }}", structInfo.ID)
}
{{- if $enableTracing }}
fmt.Printf("%s parsed, TotalN is %d -> %d\n", structID, totalN, totalN + n){{- end}}
totalN += n
previousFieldIndex = fieldIndex
}
for fieldIndex, v := range missingFieldsByIndices {
if v {
return totalN, fmt.Errorf("field '%s' is missing", s.fieldNameByIndex(fieldIndex))
}
}
return totalN, nil
}
{{- else }}
// ReadFrom reads the {{ $struct.Name }} from 'r' in format defined in the document #575623.
func (s *{{ $struct.Name }}) ReadFrom(r io.Reader) (int64, error) {
{{- if ne $struct.ElementStructID "" }}
var totalN int64
err := binary.Read(r, binary.LittleEndian, &s.StructInfo)
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(s.StructInfo))
n, err := s.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read data: %w", err)
}
totalN += n
return totalN, nil
}
// ReadDataFrom reads the {{ $struct.Name }} from 'r' excluding StructInfo,
// in format defined in the document #575623.
func (s *{{ $struct.Name }}) ReadDataFrom(r io.Reader) (int64, error) {
{{- end }}
totalN := int64(0)
{{- range $index, $field := $struct.Fields }}
{{- $fieldType := $field.ManifestFieldType.String }}
// {{ $field.Name }} (ManifestFieldType: {{ $field.ManifestFieldType.String }})
{{- if and ($enableTracing) (ne $fieldType "structInfo") }}
fmt.Printf("{{ $struct.Name }}.{{ $field.Name }} (old TotalN is %d)\n", totalN){{- end}}
{
{{- if eq $fieldType "endValue" }}
n, err := {{ $field.TypeStdSize}}, binary.Read(r, binary.LittleEndian, &s.{{ $field.Name }})
{{- end }}
{{- if eq $fieldType "structInfo" }}
// ReadDataFrom does not read Struct, use ReadFrom for that.
{{- end }}
{{- if eq $fieldType "subStruct" }}
{{- if $field.IsPointer }}
s.{{ $field.Name }} = &{{ $field.ItemTypeName }}{}
{{- end }}
n, err := s.{{ $field.Name }}.ReadFrom(r)
{{- end }}
{{- if eq $fieldType "arrayStatic" }}
n, err := {{ $field.TypeStdSize }}, binary.Read(r, binary.LittleEndian, s.{{ $field.Name }}[:])
{{- end }}
{{- if eq $fieldType "arrayDynamic" }}
{{- if eq $field.CountValue "" }}
var size {{ $field.CountType }}
err := binary.Read(r, binary.LittleEndian, &size)
if err != nil {
return totalN, fmt.Errorf("unable to the read size of field '{{ $field.Name }}': %w", err)
}
totalN += int64(binary.Size(size))
{{- else }}
size := {{ $field.CountType }}(s.{{ $field.CountValue }})
{{- end }}
s.{{ $field.Name }} = make([]byte, size)
n, err := len(s.{{ $field.Name }}), binary.Read(r, binary.LittleEndian, s.{{ $field.Name }})
{{- end }}
{{- if eq $fieldType "list" }}
var count {{ $field.CountType }}
err := binary.Read(r, binary.LittleEndian, &count)
if err != nil {
return totalN, fmt.Errorf("unable to read the count for field '{{ $field.Name }}': %w", err)
}
totalN += int64(binary.Size(count))
s.{{ $field.Name }} = make([]{{ $field.ItemTypeName }}, count)
for idx := range s.{{ $field.Name }} {
n, err := s.{{ $field.Name }}[idx].ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field '{{ $field.Name }}[%d]': %w", idx, err)
}
totalN += int64(n)
}
{{- end }}
{{- if and (ne $fieldType "list") (ne $fieldType "structInfo") }}
if err != nil {
return totalN, fmt.Errorf("unable to read field '{{ $field.Name }}': %w", err)
}
totalN += int64(n)
{{- end }}
}
{{- if and ($enableTracing) (ne $fieldType "structInfo") }}
fmt.Printf("{{ $struct.Name }}.{{ $field.Name }} parsed, TotalN is %d)\n", totalN){{- end}}
{{- end }}
return totalN, nil
}
{{- end }}
// RehashRecursive calls Rehash (see below) recursively.
func (s *{{ $struct.Name }}) RehashRecursive() {
{{- range $index, $field := $struct.Fields }}
{{- $fieldType := $field.ManifestFieldType.String }}
{{- if or (eq $fieldType "subStruct") (eq $fieldType "structInfo") (eq $fieldType "element") }}
{{- if $field.IsPointer }}
if s.{{ $field.Name }} != nil {
s.{{ $field.Name }}.Rehash()
}
{{- else }}
s.{{ $field.Name }}.Rehash()
{{- end }}
{{- end }}
{{- end }}
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *{{ $struct.Name }}) Rehash() {
{{- if ne $struct.ElementStructInfoVar0 "" }}
s.Variable0 = {{ $struct.ElementStructInfoVar0 }}
{{- end }}
{{- if ne $struct.ElementStructInfoVar1 "" }}
s.ElementSize = {{ $struct.ElementStructInfoVar1 }}
{{- end }}
{{- range $index, $field := $struct.Fields }}
{{- if ne $field.RehashValue "" }}
s.{{ $field.Name }} = {{ $field.Type.Name }}(s.{{ $field.RehashValue }})
{{- end }}
{{- end }}
}
// WriteTo writes the {{ $struct.Name }} into 'w' in format defined in
// the document #575623.
func (s *{{ $struct.Name }}) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
{{- range $index, $field := $struct.Fields }}
{{- $fieldType := $field.ManifestFieldType.String }}
// {{ $field.Name }} (ManifestFieldType: {{ $field.ManifestFieldType.String }})
{{ if $field.IsPointer }}if s.{{ $field.Name }} != nil {{ end }}{
{{- if eq $fieldType "endValue" }}
n, err := {{ $field.TypeStdSize }}, binary.Write(w, binary.LittleEndian, &s.{{ $field.Name }})
{{- end }}
{{- if or (eq $fieldType "subStruct") (eq $fieldType "structInfo") (eq $fieldType "element") }}
n, err := s.{{ $field.Name }}.WriteTo(w)
{{- end }}
{{- if eq $fieldType "arrayStatic" }}
n, err := {{ $field.TypeStdSize }}, binary.Write(w, binary.LittleEndian, s.{{ $field.Name }}[:])
{{- end }}
{{- if eq $fieldType "arrayDynamic" }}
{{- if eq $field.CountValue "" }}
size := {{ $field.CountType }}(len(s.{{ $field.Name }}))
err := binary.Write(w, binary.LittleEndian, size)
if err != nil {
return totalN, fmt.Errorf("unable to write the size of field '{{ $field.Name }}': %w", err)
}
totalN += int64(binary.Size(size))
{{- end }}
n, err := len(s.{{ $field.Name }}), binary.Write(w, binary.LittleEndian, s.{{ $field.Name }})
{{- end }}
{{- if eq $fieldType "list" }}
count := {{ $field.CountType }}(len(s.{{ $field.Name }}))
err := binary.Write(w, binary.LittleEndian, &count)
if err != nil {
return totalN, fmt.Errorf("unable to write the count for field '{{ $field.Name }}': %w", err)
}
totalN += int64(binary.Size(count))
{{- end }}
{{- if or (eq $fieldType "list") (eq $fieldType "elementList") }}
for idx := range s.{{ $field.Name }} {
n, err := s.{{ $field.Name }}[idx].WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field '{{ $field.Name }}[%d]': %w", idx, err)
}
totalN += int64(n)
}
{{- end }}
{{- if and (ne $fieldType "list") (ne $fieldType "elementList") }}
if err != nil {
return totalN, fmt.Errorf("unable to write field '{{ $field.Name }}': %w", err)
}
totalN += int64(n)
{{- end }}
}
{{- end }}
return totalN, nil
}
{{- range $index, $field := $struct.Fields }}
// {{ $field.Name }}Size returns the size in bytes of the value of field {{ $field.Name }}
func (s *{{ $struct.Name }}) {{ $field.Name }}TotalSize() uint64 {
{{- $fieldType := $field.ManifestFieldType.String }}
{{- if or (eq $fieldType "endValue") (eq $fieldType "arrayStatic") }}
return {{ $field.TypeStdSize }}
{{- end }}
{{- if or (eq $fieldType "subStruct") (eq $fieldType "structInfo") (eq $fieldType "element") }}
return s.{{ $field.Name }}.TotalSize()
{{- end }}
{{- if or (eq $fieldType "list") (eq $fieldType "elementList") }}
var size uint64
{{- if and (eq $fieldType "list") (eq $field.CountValue "") }}
size += uint64(binary.Size({{ $field.CountType }}(0)))
{{- end }}
for idx := range s.{{ $field.Name }} {
size += s.{{ $field.Name }}[idx].TotalSize()
}
return size
{{- end }}
{{- if eq $fieldType "arrayDynamic" }}
{{- if ne $field.CountValue "" }}
return uint64(len(s.{{ $field.Name }}))
{{- else }}
size := uint64(binary.Size({{ $field.CountType }}(0)))
size += uint64(len(s.{{ $field.Name }}))
return size
{{- end }}
{{- end }}
}
{{- end }}
{{- range $index, $field := $struct.Fields }}
// {{ $field.Name }}Offset returns the offset in bytes of field {{ $field.Name }}
func (s *{{ $struct.Name }}) {{ $field.Name }}Offset() uint64 {
{{- if eq $index 0 }}
return 0
{{- else }}
{{- $beforeField := index $struct.Fields (add $index -1) }}
return s.{{ $beforeField.Name }}Offset() + s.{{ $beforeField.Name }}TotalSize()
{{- end }}
}
{{- end }}
// Size returns the total size of the {{ $struct.Name }}.
func (s *{{ $struct.Name }}) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
{{- range $index, $field := $struct.Fields }}
size += s.{{ $field.Name }}TotalSize()
{{- end }}
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *{{ $struct.Name }}) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, {{ $struct.PrettyString | printf "%q" }}, s))
}
if s == nil {
return strings.Join(lines, "\n")
}
{{- range $index, $field := $struct.Fields }}
{{- $fieldType := $field.ManifestFieldType.String }}
// ManifestFieldType is {{ $fieldType }}
{{- if or (eq $fieldType "list") (eq $fieldType "elementList") }}
lines = append(lines, pretty.Header(depth+1, fmt.Sprintf({{ printf "%s: Array of \"%s\" of length %%d" $field.Name $field.Struct.PrettyString | printf "%q"}}, len(s.{{ $field.Name }})), s.{{ $field.Name }}))
for i := 0; i<len(s.{{ $field.Name }}); i++ {
lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i) + strings.TrimSpace(s.{{ $field.Name }}[i].PrettyString(depth+2, true)))
}
if depth < 1 {
lines = append(lines, "")
}
{{- else }}
{{- $fieldValue := ternary $field.IsPointer (printf "s.%s" $field.Name) (printf "&s.%s" $field.Name) }}
{{- $prettyValue := ternary (ne $field.PrettyValue "") (printf "s.%s" $field.PrettyValue) $fieldValue }}
lines = append(lines, pretty.SubValue(depth+1, {{ $field.PrettyString | printf "%q" }}, "", {{ $prettyValue }}))
{{- end }}
{{- end }}
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}
{{- end }}
{{- range $index,$type := .BasicNamedTypes }}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags {{ $type.Name }}) PrettyString(depth uint, withHeader bool) string {
{{- if not (isNil ($type.MethodByName "String")) }}
return flags.String()
{{- else }}
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, {{ $type.PrettyString | printf "%q" }}, flags))
}
{{- range $index, $method := $type.Methods }}
{{- if $method.ReturnsFlagValue }}
{{- if eq $method.ReturnsTypeName "bool" }}
if flags.{{ $method.Name }}() {
lines = append(lines, pretty.SubValue(depth+1, "{{ $method.Name.Name | camelcaseToSentence }}", {{ $method.PrettyStringForResult true | printf "%q" }}, true))
} else {
lines = append(lines, pretty.SubValue(depth+1, "{{ $method.Name.Name | camelcaseToSentence }}", {{ $method.PrettyStringForResult false | printf "%q" }}, false))
}
{{- else }}
lines = append(lines, pretty.SubValue(depth+1, "{{ $method.Name.Name | camelcaseToSentence }}", "", flags.{{ $method.Name }}()))
{{- end }}
{{- end }}
{{- end }}
return strings.Join(lines, "\n")
{{- end }}
}
{{- end }}
`

View File

@ -0,0 +1,34 @@
package analyze
import (
"fmt"
"github.com/xaionaro-go/gosrc"
)
// BasicNamedType exports the structure BasiNamedType.
type BasicNamedType struct {
gosrc.AstTypeSpec
Parent *File
}
// PrettyString returns the instance of BasicNamedType as formatted string.
func (t BasicNamedType) PrettyString() (string, error) {
result, err := getPrettyString(t.TypeSpec.Name, t.TypeSpec.Doc, t.TypeSpec.Comment, "PrettyString:")
if err != nil {
err = fmt.Errorf("unable to get PrettyString for '%s'", t.TypeSpec.Name.Name)
}
return result, err
}
// Methods returns the functions of the BasicNamedType instance
func (t BasicNamedType) Methods() Funcs {
var result Funcs
for _, method := range t.AstTypeSpec.Methods() {
result = append(result, &Func{
Parent: t.Parent,
Func: *method,
})
}
return result
}

View File

@ -0,0 +1,206 @@
package analyze
import (
"fmt"
"go/ast"
"go/types"
"math"
"path/filepath"
"strings"
"github.com/xaionaro-go/gosrc"
)
// Field is just a wrapper around gosrc.Field to provide necessary information
// for code generation.
type Field struct {
gosrc.Field
Parent *Struct
}
// ItemTypeName returns the type of the given field.
func (field Field) ItemTypeName() string {
return field.Field.ItemTypeName().Name
}
// AccessPrefix returns a prefix of a given field.
func (field Field) AccessPrefix() string {
itemTypeName := field.Field.ItemTypeName()
if itemTypeName.Path == filepath.Dir(field.Parent.Parent.Path) {
// Not imported, the same package, so no prefix is required
return ""
}
// Imported. As temporary solution we use directory name
// as the package name.
return filepath.Base(itemTypeName.Path) + "."
}
// TypeStdSize returns the size (in bytes) of the field's value, if it has
// a static size.
//
// For example if the field has type `uint32` or `[4]byte`, then the returned
// value will be `4`.
//
// Basically this is something like `binary.Size(struct.Field)`.
func (field Field) TypeStdSize() int64 {
// We set MaxInt64 as wordSize, because we expect wordSize to be
// never used, so MaxInt64 will help us to reveal any errors.
//
// maxAlign is 1 since the data is packed.
return field.Field.TypeStdSize(math.MaxInt64, 1)
}
// CountType returns the name of the type used to store the count of items
// of the slice. According to document #575623 it is usually uint16, but
// sometimes it is something else (for example uint8).
//
// This method is used only for types ManifestFieldTypeByteArrayDynamic and
// ManifestFieldTypeList.
//
// For example in table 5-11 (of document #575623) there are in particular
// fields `Count and `Digest`, where `Digest` is a list of HASH_STRUCTURE items
// and `Count` is the amount of these items. CountType defines the type of
// the field `Count` in this case (which is `uint16`).
func (field Field) CountType() string {
if countType, ok := field.TagGet("countType"); ok {
return countType
}
return "uint16"
}
// CountValue returns the value of Tag countValue
func (field Field) CountValue() string {
result, _ := field.TagGet("countValue")
return result
}
// RequiredValue returns value of the Tag required
func (field Field) RequiredValue() string {
result, _ := field.TagGet("require")
return result
}
// DefaultValue returns the value of the Tag default
func (field Field) DefaultValue() string {
result, _ := field.TagGet("default")
return result
}
// PrettyValue returns the value of the Tag prettyValue
func (field Field) PrettyValue() string {
result, _ := field.TagGet("prettyValue")
return result
}
// RehashValue returns the value of the tag rehashValue
func (field Field) RehashValue() string {
result, _ := field.TagGet("rehashValue")
return result
}
// IsElement returns bool true if a Struct is elements of a field.
func (field Field) IsElement() bool {
_struct := field.Struct()
if _struct == nil {
return false
}
info := _struct.ElementStructInfoField()
return info != nil
}
// IsFlags returns bool true if a field is of type Flags
func (field Field) IsFlags() bool {
namedType, ok := field.TypeValue.Type.(*types.Named)
if !ok {
return false
}
return strings.HasSuffix(namedType.Obj().Name(), "Flags")
}
func (field Field) isElementStructInfo() bool {
return field.Field.ItemTypeName().Name == "StructInfo"
}
// ElementStructID returns the ElementStructID of a Field
func (field Field) ElementStructID() string {
_struct := field.Struct()
if _struct == nil {
return ""
}
return _struct.ElementStructID()
}
// Struct returns a pointer to the most upper field structure instance
func (field Field) Struct() *Struct {
return field.Parent.Parent.Parent.StructByName(field.Field.ItemTypeName().Name)
}
// ManifestFieldType returns the ManifestFieldType of the field instance
func (field Field) ManifestFieldType() (ManifestFieldType, error) {
_struct := field.Parent
if _struct == nil {
return ManifestFieldTypeUndefined, fmt.Errorf("internal error: parent is not defined")
}
typ := gosrc.TypeDeepest(field.TypeValue.Type)
if typCasted, ok := typ.(*types.Pointer); ok {
typ = typCasted.Elem()
}
if typCasted, ok := typ.(*types.Named); ok {
typ = typCasted.Underlying()
}
switch typ := typ.(type) {
case *types.Array:
switch typ := typ.Elem().(type) {
case *types.Basic:
if typ.Kind() != types.Uint8 {
return ManifestFieldTypeUndefined, fmt.Errorf("static array, but not of bytes in %s.%s: %s", _struct.Name(), field.Name(), typ.String())
}
return ManifestFieldTypeByteArrayStatic, nil
default:
return ManifestFieldTypeUndefined, fmt.Errorf("static array, but not of bytes in %s.%s: %s", _struct.Name(), field.Name(), typ.String())
}
case *types.Slice:
switch elemType := typ.Elem().(type) {
case *types.Basic:
if elemType.Kind() != types.Uint8 {
return ManifestFieldTypeUndefined, fmt.Errorf("dynamic array, but not of bytes in %s.%s: %s", _struct.Name(), field.Name(), typ.String())
}
return ManifestFieldTypeByteArrayDynamic, nil
default:
if field.IsElement() {
return ManifestFieldTypeElementList, nil
}
return ManifestFieldTypeList, nil
}
case *types.Struct:
if field.isElementStructInfo() {
return ManifestFieldTypeStructInfo, nil
}
if field.IsElement() {
return ManifestFieldTypeElement, nil
}
return ManifestFieldTypeSubStruct, nil
case *types.Basic:
return ManifestFieldTypeEndValue, nil
case *types.Interface:
return ManifestFieldTypeUndefined, fmt.Errorf("do not know how to handle an interface of '%s.%s'", _struct.Name(), field.Name())
}
return ManifestFieldTypeUndefined, fmt.Errorf("unknown case: %s:%T: %v", field.Name(), typ, typ.String())
}
// PrettyString returns a formatted string of the field structure instance
func (field Field) PrettyString() (string, error) {
result, err := getPrettyString(&ast.Ident{Name: field.Name()}, field.Doc, field.Comment, "PrettyString:")
if err != nil {
err = fmt.Errorf("unable to get PrettyString for '%s.%s'", field.Parent.TypeSpec.Name.Name, field.Name())
}
return result, err
}

View File

@ -0,0 +1,56 @@
package analyze
import (
"fmt"
"go/ast"
"github.com/xaionaro-go/gosrc"
)
// Func exports the Func struct
type Func struct {
Parent *File
gosrc.Func
}
// Funcs exports the custom type []*Funs
type Funcs []*Func
// ReturnsFlagValue returns bool true if Function has a Flag value
func (fn Func) ReturnsFlagValue() bool {
if fn.Type.Params != nil && len(fn.Type.Params.List) != 0 {
return false
}
if fn.Type.Results == nil || len(fn.Type.Results.List) != 1 {
return false
}
ident, ok := fn.Type.Results.List[0].Type.(*ast.Ident)
if !ok {
return false
}
if ident.Name == "string" {
return false
}
return true
}
// ReturnsTypeName a string of the TypeName of the function
func (fn Func) ReturnsTypeName() string {
if fn.Type.Results == nil || len(fn.Type.Results.List) != 1 {
return ""
}
ident, ok := fn.Type.Results.List[0].Type.(*ast.Ident)
if !ok {
return ""
}
return ident.Name
}
// PrettyStringForResult returns a string corresponding with the documentation of the implemented function
func (fn Func) PrettyStringForResult(r interface{}) (string, error) {
result, err := getPrettyString(fn.Type, fn.FuncDecl.Doc, nil, fmt.Sprintf("PrettyString-%v:", r))
if err != nil {
err = fmt.Errorf("unable to get PrettyString for '%s'", fn.FuncDecl.Name.Name)
}
return result, err
}

View File

@ -0,0 +1,53 @@
package analyze
import (
"fmt"
)
// ManifestFieldType represents the custom type
type ManifestFieldType uint
const (
// ManifestFieldTypeUndefined indicates that the field is undefined
ManifestFieldTypeUndefined = ManifestFieldType(iota)
// ManifestFieldTypeElement indicates that the field is an element
ManifestFieldTypeElement
// ManifestFieldTypeElementList indicates that the field is a list of elements
ManifestFieldTypeElementList
// ManifestFieldTypeStructInfo indicates that the field is a StructInfo
ManifestFieldTypeStructInfo
// ManifestFieldTypeSubStruct indicates that the field is a underlaying struct
ManifestFieldTypeSubStruct
// ManifestFieldTypeEndValue indicates that the field is an EndValue
ManifestFieldTypeEndValue
// ManifestFieldTypeByteArrayDynamic indicates that the field is a´ dynamic byte array
ManifestFieldTypeByteArrayDynamic
// ManifestFieldTypeByteArrayStatic indicates that the field is a static byte array
ManifestFieldTypeByteArrayStatic
// ManifestFieldTypeList indicates that the field is a type list
ManifestFieldTypeList
)
func (ft ManifestFieldType) String() string {
switch ft {
case ManifestFieldTypeUndefined:
return "undefined"
case ManifestFieldTypeElement:
return "element"
case ManifestFieldTypeElementList:
return "elementList"
case ManifestFieldTypeStructInfo:
return "structInfo"
case ManifestFieldTypeSubStruct:
return "subStruct"
case ManifestFieldTypeEndValue:
return "endValue"
case ManifestFieldTypeByteArrayDynamic:
return "arrayDynamic"
case ManifestFieldTypeByteArrayStatic:
return "arrayStatic"
case ManifestFieldTypeList:
return "list"
}
return fmt.Sprintf("unexpected_%d", uint(ft))
}

View File

@ -0,0 +1,46 @@
package analyze
import (
"github.com/xaionaro-go/gosrc"
)
// File struct has four fields. gosrc.File holds the associated file.
// Parent represents the package as element containing the file.
// Structs holds a map of all structures of a file matching to their name in string representation.
// BasicNamedTypes holds
type File struct {
gosrc.File
Parent *Package
Structs map[string]*Struct
BasicNamedTypes map[string]*BasicNamedType
}
// Package contains zwo fields. gosrc.Package and an slice of file pointers.
type Package struct {
gosrc.Package
Files []*File
}
// StructByName returns Struct with the given name.
func (pkg *Package) StructByName(structName string) *Struct {
for _, file := range pkg.Files {
if _struct, ok := file.Structs[structName]; ok {
return _struct
}
}
return nil
}
// Structs returns a map matching StructNames to pointers of their struct instance.
func (pkg *Package) Structs() map[string]*Struct {
result := map[string]*Struct{}
for _, file := range pkg.Files {
for structName, _struct := range file.Structs {
result[structName] = _struct
}
}
return result
}

View File

@ -0,0 +1,38 @@
package analyze
import (
"fmt"
"go/ast"
"strings"
"github.com/fatih/camelcase"
)
func getPrettyString(typ ast.Expr, doc, comment *ast.CommentGroup, docPrefix string) (string, error) {
if doc == nil {
doc = comment
}
if doc != nil {
for _, docItem := range doc.List {
text := strings.TrimSpace(strings.TrimLeft(docItem.Text, "/"))
if !strings.HasPrefix(text, docPrefix) {
continue
}
return strings.TrimSpace(text[len(docPrefix):]), nil
}
}
if ident, ok := typ.(*ast.Ident); ok {
return strings.Join(camelcase.Split(ident.Name), " "), nil
}
if doc != nil {
if len(doc.List) > 0 {
return strings.TrimSpace(strings.TrimLeft(doc.List[0].Text, "/")), nil
}
}
return "", fmt.Errorf("comment with prefix '%s' is not found", docPrefix)
}

View File

@ -0,0 +1,105 @@
package analyze
import (
"fmt"
"go/ast"
"go/build"
"path/filepath"
"strings"
"github.com/xaionaro-go/gosrc"
)
// Scan holds information about a scan process used to scan a path.
func Scan(
path string,
goPaths []string,
) (*Package, error) {
// Scan the path "path".
pkgRaw, err := getRawPkg(path, goPaths)
if err != nil {
return nil, fmt.Errorf("unable to get the package from directory '%s': %w", path, err)
}
// Basically just converting received structures to our-own types,
// which we will used then in cmd/manifestcodegen/template_methods.go.
pkg, err := convertRawPkg(pkgRaw)
if err != nil {
return nil, fmt.Errorf("unable to get convert the package: %w", err)
}
return pkg, nil
}
func getRawPkg(
path string,
goPaths []string,
) (*gosrc.Package, error) {
buildCtx := build.Default
buildCtx.GOPATH = strings.Join(goPaths, string(filepath.ListSeparator))
buildCtx.BuildTags = append(buildCtx.BuildTags, `manifestcodegen`)
dirRaw, err := gosrc.OpenDirectoryByPkgPath(&buildCtx, path, false, false, false, nil)
if err != nil {
return nil, fmt.Errorf("unable to open go directory: %w", err)
}
if len(dirRaw.Packages) != 1 {
return nil, fmt.Errorf("expected one package in the directory, but received %d", len(dirRaw.Packages))
}
return dirRaw.Packages[0], nil
}
func convertRawPkg(
pkgRaw *gosrc.Package,
) (*Package, error) {
pkg := &Package{
Package: *pkgRaw,
}
for _, fileRaw := range pkgRaw.Files.FilterByGoGenerateTag("manifestcodegen") {
file := &File{
File: *fileRaw,
Parent: pkg,
Structs: map[string]*Struct{},
BasicNamedTypes: map[string]*BasicNamedType{},
}
for _, astTypeSpec := range fileRaw.AstTypeSpecs() {
switch astTypeSpec.TypeSpec.Type.(type) {
case *ast.StructType:
structRaw := &gosrc.Struct{AstTypeSpec: *astTypeSpec}
fieldsRaw, err := structRaw.Fields()
if err != nil {
return nil, fmt.Errorf("unable to get fields of struct '%s': %w", structRaw, err)
}
_struct := &Struct{
Struct: *structRaw,
Parent: file,
}
for _, fieldRaw := range fieldsRaw {
_struct.Fields = append(_struct.Fields, &Field{
Field: *fieldRaw,
Parent: _struct,
})
}
if _struct := pkg.StructByName(_struct.Name()); _struct != nil {
return nil, fmt.Errorf("structure %s is defined twice (files: '%s' and '%s')", _struct.Name(), _struct.Parent.File, file.Path)
}
file.Structs[_struct.Name()] = _struct
case *ast.Ident:
file.BasicNamedTypes[astTypeSpec.TypeSpec.Name.Name] = &BasicNamedType{
Parent: file,
AstTypeSpec: *astTypeSpec,
}
}
}
pkg.Files = append(pkg.Files, file)
}
return pkg, nil
}

View File

@ -0,0 +1,92 @@
package analyze
import (
"fmt"
"github.com/xaionaro-go/gosrc"
)
type Struct struct {
gosrc.Struct
Parent *File
Fields []*Field
}
func (_struct Struct) ElementStructInfoField() *Field {
for _, field := range _struct.Fields {
if field.isElementStructInfo() {
return field
}
}
return nil
}
func (_struct Struct) ElementStructVersion() string {
field := _struct.ElementStructInfoField()
if field == nil {
return ""
}
id, _ := field.TagGet("version")
return id
}
func (_struct Struct) ElementStructID() string {
field := _struct.ElementStructInfoField()
if field == nil {
return ""
}
id, _ := field.TagGet("id")
return id
}
func (_struct Struct) IsElementsContainer() bool {
for _, field := range _struct.Fields {
if field.IsElement() {
return true
}
}
return false
}
func (_struct Struct) PrettyString() (string, error) {
result, err := getPrettyString(_struct.TypeSpec.Name, _struct.TypeSpec.Doc, _struct.TypeSpec.Comment, "PrettyString:")
if err != nil {
err = fmt.Errorf("unable to get PrettyString for '%s'", _struct.TypeSpec.Name.Name)
}
return result, err
}
func (_struct Struct) Methods() Funcs {
var result Funcs
for _, method := range _struct.AstTypeSpec.Methods() {
result = append(result, &Func{Func: *method})
}
return result
}
func (_struct Struct) HasOnRehash() bool {
return _struct.MethodByName("onRehash") != nil
}
func (_struct Struct) ElementStructInfoVar0() string {
f := _struct.ElementStructInfoField()
if f == nil {
return ""
}
r, _ := f.TagGet("var0")
return r
}
func (_struct Struct) ElementStructInfoVar1() string {
f := _struct.ElementStructInfoField()
if f == nil {
return ""
}
r, _ := f.TagGet("var1")
return r
}

View File

@ -0,0 +1,91 @@
package pretty
import (
"fmt"
"reflect"
"strings"
"github.com/dustin/go-humanize"
)
func Header(depth uint, description string, obj interface{}) string {
if description == "" {
description = fmt.Sprintf("%T", obj)
}
switch depth {
case 0:
description = `----` + description + "----\n"
case 1:
description = `--` + description + `--`
default:
description += `:`
}
description = strings.Repeat(" ", int(depth)) + description
return description
}
func SubValue(depth uint, fieldName, valueDescription string, value interface{}) string {
if valueDescription == "" {
valueDescription = getDescriptionForValue(depth, value)
}
return fmt.Sprintf("%s %s", Header(depth, fieldName, nil), valueDescription)
}
func getDescriptionForValue(depth uint, value interface{}) string {
v := reflect.ValueOf(value)
if v.Kind() == reflect.Ptr && v.IsNil() {
return "is not set (nil)"
}
switch value := value.(type) {
case interface {
PrettyString(depth uint, withHeader bool) string
}:
description := value.PrettyString(depth, false)
if len(strings.Split(description, "\n")) > 1 {
return "\n" + description
} else {
return strings.TrimSpace(description)
}
case fmt.GoStringer:
return value.GoString()
case fmt.Stringer:
return value.String()
}
v = reflect.Indirect(v)
switch v.Kind() {
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
i := v.Uint()
var hexFmt string
switch v.Type().Size() {
case 1:
hexFmt = "0x%02X"
case 2:
hexFmt = "0x%04X"
case 4:
hexFmt = "0x%08X"
case 8:
hexFmt = "0x%16X"
}
switch {
case i < 10:
return fmt.Sprintf(hexFmt, i)
case i < 65536:
return fmt.Sprintf(hexFmt+" (%d)", i, i)
default:
return fmt.Sprintf(hexFmt+" (%d: %s)", i, i, humanize.IBytes(i))
}
case reflect.Array:
return fmt.Sprintf("0x%X", v.Interface())
case reflect.Slice:
if v.Len() == 0 {
return "empty (len: 0)"
}
return fmt.Sprintf("0x%X (len: %d)", v.Interface(), v.Len())
}
return fmt.Sprintf("%#+v (%T)", value, value)
}

View File

@ -0,0 +1,47 @@
package tracedbinary
import (
"encoding/binary"
"fmt"
"io"
"path/filepath"
"reflect"
"runtime"
)
type ByteOrder = binary.ByteOrder
var (
LittleEndian = binary.LittleEndian
)
func Read(r io.Reader, order ByteOrder, data interface{}) error {
err := binary.Read(r, order, data)
v := reflect.Indirect(reflect.ValueOf(data))
switch {
case v.Kind() != reflect.Slice || v.Len() < 16:
fmt.Printf("%s: binary.Read(%T, %s, %T) -> %v; data == %v\n", caller(), r, order, data, err, v.Interface())
case v.Kind() == reflect.Slice:
fmt.Printf("%s: binary.Read(%T, %s, %T) -> %v; len(data) == %v\n", caller(), r, order, data, err, v.Len())
default:
fmt.Printf("%s: binary.Read(%T, %s, %T) -> %v\n", caller(), r, order, data, err)
}
return err
}
func Write(w io.Writer, order ByteOrder, data interface{}) error {
err := binary.Write(w, order, data)
fmt.Printf("%s: binary.Read(%T, %s, %T) -> %v\n", caller(), w, order, data, err)
return err
}
func Size(v interface{}) int {
r := binary.Size(v)
fmt.Printf("%s: binary.Size(%T) -> %v\n", caller(), v, r)
return r
}
func caller() string {
_, file, line, _ := runtime.Caller(2)
return fmt.Sprintf("%s:%d", filepath.Base(file), line)
}

View File

@ -0,0 +1,32 @@
package unittest
import (
"bytes"
"io/ioutil"
"testing"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/stretchr/testify/require"
)
func ManifestReadWrite(t *testing.T, m manifest.Manifest, testDataFilePath string) {
testData, err := ioutil.ReadFile(testDataFilePath)
require.NoError(t, err)
nR, err := m.ReadFrom(bytes.NewReader(append(testData, []byte(`extra bytes`)...)))
require.NoError(t, err)
require.Equal(t, int64(len(testData)), nR)
require.Equal(t, nR, int64(m.TotalSize()))
prettyString := m.PrettyString(0, true)
var out bytes.Buffer
nW, err := m.WriteTo(&out)
require.NoError(t, err)
newPrettyString := m.PrettyString(0, true)
require.Equal(t, prettyString, newPrettyString, newPrettyString)
require.Equal(t, string(testData), out.String())
require.Equal(t, nW, nR)
require.Equal(t, nW, int64(out.Len()))
}

View File

@ -0,0 +1,152 @@
//go:generate manifestcodegen
package manifest
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/sha256"
"encoding/binary"
"fmt"
"math/big"
"github.com/google/go-tpm/tpm2"
)
// Key is a public key of an asymmetric crypto keypair.
type Key struct {
KeyAlg tpm2.Algorithm `json:"key_alg"`
Version uint8 `require:"0x10" json:"key_version"`
KeySize BitSize `json:"key_bitsize"`
Data []byte `countValue:"keyDataSize()" json:"key_data"`
}
// BitSize is a size in bits.
type BitSize uint16
// InBits returns the size in bits.
func (ks BitSize) InBits() uint16 {
return uint16(ks)
}
// InBytes returns the size in bytes.
func (ks BitSize) InBytes() uint16 {
return uint16(ks >> 3)
}
// SetInBits sets the size in bits.
func (ks *BitSize) SetInBits(amountOfBits uint16) {
*ks = BitSize(amountOfBits)
}
// SetInBytes sets the size in bytes.
func (ks *BitSize) SetInBytes(amountOfBytes uint16) {
*ks = BitSize(amountOfBytes << 3)
}
// keyDataSize returns the expected length of Data for specified
// KeyAlg and KeySize.
func (k Key) keyDataSize() int64 {
switch k.KeyAlg {
case tpm2.AlgRSA:
return int64(k.KeySize.InBytes()) + 4
case tpm2.AlgECC:
return int64(k.KeySize.InBytes()) * 2
}
return -1
}
// PubKey parses Data into crypto.PublicKey.
func (k Key) PubKey() (crypto.PublicKey, error) {
expectedSize := int(k.keyDataSize())
if expectedSize < 0 {
return nil, fmt.Errorf("unexpected algorithm: %s", k.KeyAlg)
}
if len(k.Data) != expectedSize {
return nil, fmt.Errorf("unexpected size: expected:%d, received %d", expectedSize, len(k.Data))
}
switch k.KeyAlg {
case tpm2.AlgRSA:
result := &rsa.PublicKey{
N: new(big.Int).SetBytes(reverseBytes(k.Data[4:])),
E: int(binaryOrder.Uint32(k.Data)),
}
return result, nil
case tpm2.AlgECC:
keySize := k.KeySize.InBytes()
x := new(big.Int).SetBytes(reverseBytes(k.Data[:keySize]))
y := new(big.Int).SetBytes(reverseBytes(k.Data[keySize:]))
return ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}, nil
}
return nil, fmt.Errorf("unexpected TPM algorithm: %s", k.KeyAlg)
}
func reverseBytes(b []byte) []byte {
r := make([]byte, len(b))
for idx := range b {
r[idx] = b[len(b)-idx-1]
}
return r
}
// SetPubKey sets Data the value corresponding to passed `key`.
func (k *Key) SetPubKey(key crypto.PublicKey) error {
k.Version = 0x10
switch key := key.(type) {
case *rsa.PublicKey:
k.KeyAlg = tpm2.AlgRSA
n := key.N.Bytes()
k.KeySize.SetInBytes(uint16(len(n)))
k.Data = make([]byte, 4+len(n))
binaryOrder.PutUint32(k.Data, uint32(key.E))
copy(k.Data[4:], reverseBytes(n))
return nil
case *ecdsa.PublicKey:
var x, y *big.Int
k.KeyAlg = tpm2.AlgRSA
x, y = key.X, key.Y
if x == nil || y == nil {
return fmt.Errorf("the pubkey '%#+v' is invalid: x == nil || y == nil", key)
}
k.KeySize.SetInBits(256)
xB, yB := x.Bytes(), y.Bytes()
if len(xB) != int(k.KeySize.InBytes()) || len(yB) != int(k.KeySize.InBytes()) {
return fmt.Errorf("the pubkey '%#+v' is invalid: len(x)<%d> != %d || len(y)<%d> == %d",
key, len(xB), int(k.KeySize.InBytes()), len(yB), int(k.KeySize.InBytes()))
}
k.Data = make([]byte, 2*k.KeySize.InBytes())
copy(k.Data[:], reverseBytes(xB))
copy(k.Data[len(xB):], reverseBytes(yB))
return nil
}
return fmt.Errorf("unexpected key type: %T", key)
}
//PrintMEKey prints the KM public signing key hash to fuse into the Intel ME
func (k *Key) PrintMEKey() error {
buf := new(bytes.Buffer)
if len(k.Data) > 1 {
if err := binary.Write(buf, binary.LittleEndian, k.Data[4:]); err != nil {
return nil
}
if err := binary.Write(buf, binary.LittleEndian, k.Data[:4]); err != nil {
return nil
}
h := sha256.New()
h.Write(buf.Bytes())
fmt.Printf(" Key Manifest Pubkey Hash: 0x%x\n", h.Sum(nil))
} else {
fmt.Printf(" Key Manifest Pubkey Hash: No km public key set in KM\n")
}
return nil
}

View File

@ -0,0 +1,90 @@
//go:generate manifestcodegen
package key
import (
"fmt"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
)
// Hash is "KM hash Structure" defined in document #575623.
type Hash struct {
// Usage is the digest usage bitmask.
//
// More than one bit can be set to indicate shared digest usage.
// Usage of bit 0 is normative; other usages are informative.
Usage Usage `json:"hash_Usage"`
// Digest is the actual digest.
Digest manifest.HashStructure `json:"hash_Struct"`
}
// Usage is the digest usage bitmask.
//
// More than one bit can be set to indicate shared digest usage.
// Usage of bit 0 is normative; other usages are informative.
type Usage uint64
const (
// UsageBPMSigningPKD is the bit meaning the digest could be used as
// Boot Policy Manifest signing pubkey digest.
UsageBPMSigningPKD = Usage(1 << iota)
// UsageBPMSigningPKD is the bit meaning the digest could be used as
// FIT Patch Manifest signing pubkey digest.
UsageFITPatchManifestSigningPKD
// UsageBPMSigningPKD is the bit meaning the digest could be used as
// ACM Manifest signing pubkey digest.
UsageACMManifestSigningPKD
// UsageBPMSigningPKD is the bit meaning the digest could be used as
// SDEV signing pubkey digest.
UsageSDEVSigningPKD
)
// String implements fmt.Stringer.
func (u Usage) String() string {
var result []string
for i := uint(0); i < 64; i++ {
f := Usage(0 << i)
if !u.IsSet(f) {
continue
}
var descr string
switch f {
case UsageBPMSigningPKD:
descr = "BPM_signing_pubkey_digest"
case UsageFITPatchManifestSigningPKD:
descr = "FIT_patch_manifest_signing_pubkey_digest"
case UsageACMManifestSigningPKD:
descr = "ACM_manifest_signing_pubkey_digest"
case UsageSDEVSigningPKD:
descr = "SDEV_signing_pubkey_digest"
default:
descr = fmt.Sprintf("unexpected_bit_%d", i)
}
result = append(result, descr)
}
return strings.Join(result, ",")
}
// IsSet returns true if bits `f` are set in bitmask `u`.
func (u Usage) IsSet(f Usage) bool {
return u&f != 0
}
// Set sets/unsets the bits of `f` in bitmask `u`.
//
// To set the bits `v` should be true, to unset -- false.
func (u *Usage) Set(f Usage, v bool) {
if v {
*u |= f
} else {
*u &= ^f
}
}

View File

@ -0,0 +1,165 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/key
package key
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewHash returns a new instance of Hash with
// all default values set.
func NewHash() *Hash {
s := &Hash{}
// Recursively initializing a child structure:
s.Digest = *manifest.NewHashStructure()
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *Hash) Validate() error {
// Recursively validating a child structure:
if err := s.Digest.Validate(); err != nil {
return fmt.Errorf("error on field 'Digest': %w", err)
}
return nil
}
// ReadFrom reads the Hash from 'r' in format defined in the document #575623.
func (s *Hash) ReadFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// Usage (ManifestFieldType: endValue)
{
n, err := 8, binary.Read(r, binary.LittleEndian, &s.Usage)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Usage': %w", err)
}
totalN += int64(n)
}
// Digest (ManifestFieldType: subStruct)
{
n, err := s.Digest.ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Digest': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *Hash) RehashRecursive() {
s.Digest.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *Hash) Rehash() {
}
// WriteTo writes the Hash into 'w' in format defined in
// the document #575623.
func (s *Hash) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// Usage (ManifestFieldType: endValue)
{
n, err := 8, binary.Write(w, binary.LittleEndian, &s.Usage)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Usage': %w", err)
}
totalN += int64(n)
}
// Digest (ManifestFieldType: subStruct)
{
n, err := s.Digest.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Digest': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// UsageSize returns the size in bytes of the value of field Usage
func (s *Hash) UsageTotalSize() uint64 {
return 8
}
// DigestSize returns the size in bytes of the value of field Digest
func (s *Hash) DigestTotalSize() uint64 {
return s.Digest.TotalSize()
}
// UsageOffset returns the offset in bytes of field Usage
func (s *Hash) UsageOffset() uint64 {
return 0
}
// DigestOffset returns the offset in bytes of field Digest
func (s *Hash) DigestOffset() uint64 {
return s.UsageOffset() + s.UsageTotalSize()
}
// Size returns the total size of the Hash.
func (s *Hash) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.UsageTotalSize()
size += s.DigestTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *Hash) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Hash", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Usage", "", &s.Usage))
// ManifestFieldType is subStruct
lines = append(lines, pretty.SubValue(depth+1, "Digest", "", &s.Digest))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags Usage) PrettyString(depth uint, withHeader bool) string {
return flags.String()
}

View File

@ -0,0 +1,60 @@
//go:generate manifestcodegen
package key
import (
"crypto"
"fmt"
"github.com/google/go-tpm/tpm2"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
)
// PrettyString: Key Manifest
type Manifest struct {
manifest.StructInfo `id:"__KEYM__" version:"0x21" var0:"0" var1:"0"`
// KeyManifestSignatureOffset is Key Manifest KeySignature offset.
//
// The original name is "KeySignatureOffset" (in #575623).
KeyManifestSignatureOffset uint16 `rehashValue:"KeyAndSignatureOffset()" json:"km_SigOffset,omitempty"`
// Reserved2 is an alignment.
Reserved2 [3]byte `json:"km_Reserved2,omitempty"`
// Revision is the revision of the Key Manifest defined by the Platform
// Manufacturer.
Revision uint8 `json:"km_Revision"`
// KMSVN is the Key Manifest Security Version Number.
KMSVN manifest.SVN `json:"km_SVN"`
// KMID is the Key Manifest Identifier.
KMID uint8 `json:"km_ID"`
// PubKeyHashAlg is the hash algorithm of OEM public key digest programmed
// into the FPF.
PubKeyHashAlg tpm2.Algorithm `json:"km_PubKeyHashAlg"`
// Hash is the slice of KMHASH_STRUCT (KHS) structures (see table 5-3
// of the document #575623). Describes BPM pubkey digest (among other).
Hash []Hash `json:"km_hash"`
// KeyAndSignature is the Key Manifest signature.
KeyAndSignature manifest.KeySignature `json:"km_KeySignature"`
}
func (m *Manifest) SetSignature(
algo tpm2.Algorithm,
privKey crypto.Signer,
signedData []byte,
) error {
err := m.KeyAndSignature.SetSignature(algo, privKey, signedData)
if err != nil {
return fmt.Errorf("unable to set the signature: %w", err)
}
m.PubKeyHashAlg = m.KeyAndSignature.Signature.HashAlg
return nil
}

View File

@ -0,0 +1,455 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/key
package key
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
_ = manifest.StructInfo{}
)
// NewManifest returns a new instance of Manifest with
// all default values set.
func NewManifest() *Manifest {
s := &Manifest{}
copy(s.StructInfo.ID[:], []byte(StructureIDManifest))
s.StructInfo.Version = 0x21
// Recursively initializing a child structure:
s.KeyAndSignature = *manifest.NewKeySignature()
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *Manifest) Validate() error {
// See tag "rehashValue"
{
expectedValue := uint16(s.KeyAndSignatureOffset())
if s.KeyManifestSignatureOffset != expectedValue {
return fmt.Errorf("field 'KeyManifestSignatureOffset' expects write-value '%v', but has %v", expectedValue, s.KeyManifestSignatureOffset)
}
}
// Recursively validating a child structure:
if err := s.KeyAndSignature.Validate(); err != nil {
return fmt.Errorf("error on field 'KeyAndSignature': %w", err)
}
return nil
}
// StructureIDManifest is the StructureID (in terms of
// the document #575623) of element 'Manifest'.
const StructureIDManifest = "__KEYM__"
// GetStructInfo returns current value of StructInfo of the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *Manifest) GetStructInfo() manifest.StructInfo {
return s.StructInfo
}
// SetStructInfo sets new value of StructInfo to the structure.
//
// StructInfo is a set of standard fields with presented in any element
// ("element" in terms of document #575623).
func (s *Manifest) SetStructInfo(newStructInfo manifest.StructInfo) {
s.StructInfo = newStructInfo
}
// ReadFrom reads the Manifest from 'r' in format defined in the document #575623.
func (s *Manifest) ReadFrom(r io.Reader) (int64, error) {
var totalN int64
err := binary.Read(r, binary.LittleEndian, &s.StructInfo)
if err != nil {
return totalN, fmt.Errorf("unable to read structure info at %d: %w", totalN, err)
}
totalN += int64(binary.Size(s.StructInfo))
n, err := s.ReadDataFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read data: %w", err)
}
totalN += n
return totalN, nil
}
// ReadDataFrom reads the Manifest from 'r' excluding StructInfo,
// in format defined in the document #575623.
func (s *Manifest) ReadDataFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// StructInfo (ManifestFieldType: structInfo)
{
// ReadDataFrom does not read Struct, use ReadFrom for that.
}
// KeyManifestSignatureOffset (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeyManifestSignatureOffset)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KeyManifestSignatureOffset': %w", err)
}
totalN += int64(n)
}
// Reserved2 (ManifestFieldType: arrayStatic)
{
n, err := 3, binary.Read(r, binary.LittleEndian, s.Reserved2[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Reserved2': %w", err)
}
totalN += int64(n)
}
// Revision (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.Revision)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Revision': %w", err)
}
totalN += int64(n)
}
// KMSVN (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.KMSVN)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KMSVN': %w", err)
}
totalN += int64(n)
}
// KMID (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.KMID)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KMID': %w", err)
}
totalN += int64(n)
}
// PubKeyHashAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.PubKeyHashAlg)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'PubKeyHashAlg': %w", err)
}
totalN += int64(n)
}
// Hash (ManifestFieldType: list)
{
var count uint16
err := binary.Read(r, binary.LittleEndian, &count)
if err != nil {
return totalN, fmt.Errorf("unable to read the count for field 'Hash': %w", err)
}
totalN += int64(binary.Size(count))
s.Hash = make([]Hash, count)
for idx := range s.Hash {
n, err := s.Hash[idx].ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Hash[%d]': %w", idx, err)
}
totalN += int64(n)
}
}
// KeyAndSignature (ManifestFieldType: subStruct)
{
n, err := s.KeyAndSignature.ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KeyAndSignature': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *Manifest) RehashRecursive() {
s.StructInfo.Rehash()
s.KeyAndSignature.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *Manifest) Rehash() {
s.Variable0 = 0
s.ElementSize = 0
s.KeyManifestSignatureOffset = uint16(s.KeyAndSignatureOffset())
}
// WriteTo writes the Manifest into 'w' in format defined in
// the document #575623.
func (s *Manifest) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// StructInfo (ManifestFieldType: structInfo)
{
n, err := s.StructInfo.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'StructInfo': %w", err)
}
totalN += int64(n)
}
// KeyManifestSignatureOffset (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeyManifestSignatureOffset)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KeyManifestSignatureOffset': %w", err)
}
totalN += int64(n)
}
// Reserved2 (ManifestFieldType: arrayStatic)
{
n, err := 3, binary.Write(w, binary.LittleEndian, s.Reserved2[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Reserved2': %w", err)
}
totalN += int64(n)
}
// Revision (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.Revision)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Revision': %w", err)
}
totalN += int64(n)
}
// KMSVN (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.KMSVN)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KMSVN': %w", err)
}
totalN += int64(n)
}
// KMID (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.KMID)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KMID': %w", err)
}
totalN += int64(n)
}
// PubKeyHashAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.PubKeyHashAlg)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'PubKeyHashAlg': %w", err)
}
totalN += int64(n)
}
// Hash (ManifestFieldType: list)
{
count := uint16(len(s.Hash))
err := binary.Write(w, binary.LittleEndian, &count)
if err != nil {
return totalN, fmt.Errorf("unable to write the count for field 'Hash': %w", err)
}
totalN += int64(binary.Size(count))
for idx := range s.Hash {
n, err := s.Hash[idx].WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Hash[%d]': %w", idx, err)
}
totalN += int64(n)
}
}
// KeyAndSignature (ManifestFieldType: subStruct)
{
n, err := s.KeyAndSignature.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KeyAndSignature': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// StructInfoSize returns the size in bytes of the value of field StructInfo
func (s *Manifest) StructInfoTotalSize() uint64 {
return s.StructInfo.TotalSize()
}
// KeyManifestSignatureOffsetSize returns the size in bytes of the value of field KeyManifestSignatureOffset
func (s *Manifest) KeyManifestSignatureOffsetTotalSize() uint64 {
return 2
}
// Reserved2Size returns the size in bytes of the value of field Reserved2
func (s *Manifest) Reserved2TotalSize() uint64 {
return 3
}
// RevisionSize returns the size in bytes of the value of field Revision
func (s *Manifest) RevisionTotalSize() uint64 {
return 1
}
// KMSVNSize returns the size in bytes of the value of field KMSVN
func (s *Manifest) KMSVNTotalSize() uint64 {
return 1
}
// KMIDSize returns the size in bytes of the value of field KMID
func (s *Manifest) KMIDTotalSize() uint64 {
return 1
}
// PubKeyHashAlgSize returns the size in bytes of the value of field PubKeyHashAlg
func (s *Manifest) PubKeyHashAlgTotalSize() uint64 {
return 2
}
// HashSize returns the size in bytes of the value of field Hash
func (s *Manifest) HashTotalSize() uint64 {
var size uint64
size += uint64(binary.Size(uint16(0)))
for idx := range s.Hash {
size += s.Hash[idx].TotalSize()
}
return size
}
// KeyAndSignatureSize returns the size in bytes of the value of field KeyAndSignature
func (s *Manifest) KeyAndSignatureTotalSize() uint64 {
return s.KeyAndSignature.TotalSize()
}
// StructInfoOffset returns the offset in bytes of field StructInfo
func (s *Manifest) StructInfoOffset() uint64 {
return 0
}
// KeyManifestSignatureOffsetOffset returns the offset in bytes of field KeyManifestSignatureOffset
func (s *Manifest) KeyManifestSignatureOffsetOffset() uint64 {
return s.StructInfoOffset() + s.StructInfoTotalSize()
}
// Reserved2Offset returns the offset in bytes of field Reserved2
func (s *Manifest) Reserved2Offset() uint64 {
return s.KeyManifestSignatureOffsetOffset() + s.KeyManifestSignatureOffsetTotalSize()
}
// RevisionOffset returns the offset in bytes of field Revision
func (s *Manifest) RevisionOffset() uint64 {
return s.Reserved2Offset() + s.Reserved2TotalSize()
}
// KMSVNOffset returns the offset in bytes of field KMSVN
func (s *Manifest) KMSVNOffset() uint64 {
return s.RevisionOffset() + s.RevisionTotalSize()
}
// KMIDOffset returns the offset in bytes of field KMID
func (s *Manifest) KMIDOffset() uint64 {
return s.KMSVNOffset() + s.KMSVNTotalSize()
}
// PubKeyHashAlgOffset returns the offset in bytes of field PubKeyHashAlg
func (s *Manifest) PubKeyHashAlgOffset() uint64 {
return s.KMIDOffset() + s.KMIDTotalSize()
}
// HashOffset returns the offset in bytes of field Hash
func (s *Manifest) HashOffset() uint64 {
return s.PubKeyHashAlgOffset() + s.PubKeyHashAlgTotalSize()
}
// KeyAndSignatureOffset returns the offset in bytes of field KeyAndSignature
func (s *Manifest) KeyAndSignatureOffset() uint64 {
return s.HashOffset() + s.HashTotalSize()
}
// Size returns the total size of the Manifest.
func (s *Manifest) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.StructInfoTotalSize()
size += s.KeyManifestSignatureOffsetTotalSize()
size += s.Reserved2TotalSize()
size += s.RevisionTotalSize()
size += s.KMSVNTotalSize()
size += s.KMIDTotalSize()
size += s.PubKeyHashAlgTotalSize()
size += s.HashTotalSize()
size += s.KeyAndSignatureTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *Manifest) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Key Manifest", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is structInfo
lines = append(lines, pretty.SubValue(depth+1, "Struct Info", "", &s.StructInfo))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Key Manifest Signature Offset", "", &s.KeyManifestSignatureOffset))
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "Reserved 2", "", &s.Reserved2))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Revision", "", &s.Revision))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "KMSVN", "", &s.KMSVN))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "KMID", "", &s.KMID))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Pub Key Hash Alg", "", &s.PubKeyHashAlg))
// ManifestFieldType is list
lines = append(lines, pretty.Header(depth+1, fmt.Sprintf("Hash: Array of \"Hash\" of length %d", len(s.Hash)), s.Hash))
for i := 0; i < len(s.Hash); i++ {
lines = append(lines, fmt.Sprintf("%sitem #%d: ", strings.Repeat(" ", int(depth+2)), i)+strings.TrimSpace(s.Hash[i].PrettyString(depth+2, true)))
}
if depth < 1 {
lines = append(lines, "")
}
// ManifestFieldType is subStruct
lines = append(lines, pretty.SubValue(depth+1, "Key And Signature", "", &s.KeyAndSignature))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,32 @@
// +build !manifestcodegen
//
// To avoid error "m.StructInfo.PrettyString undefined" we place this
// function to a file with a build tag "!manifestcodegen"
package key
import (
"fmt"
)
// Print prints the Key Manifest.
func (m *Manifest) Print() {
fmt.Printf(" --Key Manifest--\n")
fmt.Printf("\t%v\n", m.StructInfo.PrettyString(1, true))
fmt.Printf("\tKeyManifestSignatureOffset: %v\n", m.KeyManifestSignatureOffset)
fmt.Printf("\tKMID: %v\n", m.KMID)
fmt.Printf("\tPubKeyHashAlg: %v\n", m.PubKeyHashAlg)
for _, i := range m.Hash {
fmt.Printf("%v\n", i.PrettyString(2, true))
}
fmt.Printf("\n")
if m.KeyAndSignature.Signature.DataTotalSize() < 1 {
fmt.Printf(" --Key and Signature--\n")
if m.KeyAndSignature.Key.DataTotalSize() > 0 {
fmt.Printf("\t%v\n", m.KeyAndSignature.Key.PrettyString(2, true))
}
fmt.Printf("\n\n\tKey Manifest not signed!\n\n")
} else {
fmt.Printf("%v \n\n", m.KeyAndSignature.PrettyString(2, true))
}
}

View File

@ -0,0 +1,11 @@
package key
import (
"testing"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/unittest"
)
func TestReadWrite(t *testing.T) {
unittest.ManifestReadWrite(t, &Manifest{}, "testdata/km.bin")
}

Binary file not shown.

View File

@ -0,0 +1,232 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest
package manifest
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
)
// NewKey returns a new instance of Key with
// all default values set.
func NewKey() *Key {
s := &Key{}
// Set through tag "required":
s.Version = 0x10
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *Key) Validate() error {
// See tag "require"
if s.Version != 0x10 {
return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version)
}
return nil
}
// ReadFrom reads the Key from 'r' in format defined in the document #575623.
func (s *Key) ReadFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// KeyAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeyAlg)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KeyAlg': %w", err)
}
totalN += int64(n)
}
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Version': %w", err)
}
totalN += int64(n)
}
// KeySize (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySize)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KeySize': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
size := uint16(s.keyDataSize())
s.Data = make([]byte, size)
n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *Key) RehashRecursive() {
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *Key) Rehash() {
}
// WriteTo writes the Key into 'w' in format defined in
// the document #575623.
func (s *Key) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// KeyAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeyAlg)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KeyAlg': %w", err)
}
totalN += int64(n)
}
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Version': %w", err)
}
totalN += int64(n)
}
// KeySize (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySize)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KeySize': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// KeyAlgSize returns the size in bytes of the value of field KeyAlg
func (s *Key) KeyAlgTotalSize() uint64 {
return 2
}
// VersionSize returns the size in bytes of the value of field Version
func (s *Key) VersionTotalSize() uint64 {
return 1
}
// KeySizeSize returns the size in bytes of the value of field KeySize
func (s *Key) KeySizeTotalSize() uint64 {
return 2
}
// DataSize returns the size in bytes of the value of field Data
func (s *Key) DataTotalSize() uint64 {
return uint64(len(s.Data))
}
// KeyAlgOffset returns the offset in bytes of field KeyAlg
func (s *Key) KeyAlgOffset() uint64 {
return 0
}
// VersionOffset returns the offset in bytes of field Version
func (s *Key) VersionOffset() uint64 {
return s.KeyAlgOffset() + s.KeyAlgTotalSize()
}
// KeySizeOffset returns the offset in bytes of field KeySize
func (s *Key) KeySizeOffset() uint64 {
return s.VersionOffset() + s.VersionTotalSize()
}
// DataOffset returns the offset in bytes of field Data
func (s *Key) DataOffset() uint64 {
return s.KeySizeOffset() + s.KeySizeTotalSize()
}
// Size returns the total size of the Key.
func (s *Key) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.KeyAlgTotalSize()
size += s.VersionTotalSize()
size += s.KeySizeTotalSize()
size += s.DataTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *Key) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Key", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Key Alg", "", &s.KeyAlg))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Key Size", "", &s.KeySize))
// ManifestFieldType is arrayDynamic
lines = append(lines, pretty.SubValue(depth+1, "Data", "", &s.Data))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}
// PrettyString returns the bits of the flags in an easy-to-read format.
func (flags BitSize) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Bit Size", flags))
}
lines = append(lines, pretty.SubValue(depth+1, "In Bits", "", flags.InBits()))
lines = append(lines, pretty.SubValue(depth+1, "In Bytes", "", flags.InBytes()))
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,62 @@
//go:generate manifestcodegen
package manifest
import (
"crypto"
"fmt"
"github.com/google/go-tpm/tpm2"
)
// KeySignature
type KeySignature struct {
Version uint8 `require:"0x10" json:"ks_Version,omitempty"`
Key Key `json:"ks_key"`
Signature Signature `json:"ks_Signature"`
}
// Verify verifies the builtin signature with the builtin public key.
func (m *KeySignature) Verify(signedData []byte) error {
sig, err := m.Signature.SignatureData()
if err != nil {
return fmt.Errorf("invalid signature: %w", err)
}
pk, err := m.Key.PubKey()
if err != nil {
return fmt.Errorf("invalid public key: %w", err)
}
err = sig.Verify(pk, signedData)
if err != nil {
return fmt.Errorf("verification failed: %w", err)
}
return nil
}
// SetSignature generates a signature and sets all the values of KeyManifest,
// accordingly to arguments signAlgo, privKey and signedData.
//
// if signAlgo is zero then it is detected automatically, based on the type
// of the provided private key.
func (ks *KeySignature) SetSignature(signAlgo tpm2.Algorithm, privKey crypto.Signer, signedData []byte) error {
err := ks.Key.SetPubKey(privKey.Public())
if err != nil {
return fmt.Errorf("unable to set public key: %w", err)
}
return ks.Signature.SetSignature(signAlgo, privKey, signedData)
}
// SetSignatureAuto generates a signature and sets all the values of KeyManifest,
// accordingly to arguments privKey and signedData.
//
// Signing algorithm will be detected automatically based on the type of the
// provided private key.
func (ks *KeySignature) SetSignatureAuto(privKey crypto.Signer, signedData []byte) error {
err := ks.Key.SetPubKey(privKey.Public())
if err != nil {
return fmt.Errorf("unable to set public key: %w", err)
}
return ks.SetSignature(0, privKey, signedData)
}

View File

@ -0,0 +1,202 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest
package manifest
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
)
// NewKeySignature returns a new instance of KeySignature with
// all default values set.
func NewKeySignature() *KeySignature {
s := &KeySignature{}
// Set through tag "required":
s.Version = 0x10
// Recursively initializing a child structure:
s.Key = *NewKey()
// Recursively initializing a child structure:
s.Signature = *NewSignature()
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *KeySignature) Validate() error {
// See tag "require"
if s.Version != 0x10 {
return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version)
}
// Recursively validating a child structure:
if err := s.Key.Validate(); err != nil {
return fmt.Errorf("error on field 'Key': %w", err)
}
// Recursively validating a child structure:
if err := s.Signature.Validate(); err != nil {
return fmt.Errorf("error on field 'Signature': %w", err)
}
return nil
}
// ReadFrom reads the KeySignature from 'r' in format defined in the document #575623.
func (s *KeySignature) ReadFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Version': %w", err)
}
totalN += int64(n)
}
// Key (ManifestFieldType: subStruct)
{
n, err := s.Key.ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Key': %w", err)
}
totalN += int64(n)
}
// Signature (ManifestFieldType: subStruct)
{
n, err := s.Signature.ReadFrom(r)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Signature': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *KeySignature) RehashRecursive() {
s.Key.Rehash()
s.Signature.Rehash()
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *KeySignature) Rehash() {
}
// WriteTo writes the KeySignature into 'w' in format defined in
// the document #575623.
func (s *KeySignature) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Version': %w", err)
}
totalN += int64(n)
}
// Key (ManifestFieldType: subStruct)
{
n, err := s.Key.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Key': %w", err)
}
totalN += int64(n)
}
// Signature (ManifestFieldType: subStruct)
{
n, err := s.Signature.WriteTo(w)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Signature': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// VersionSize returns the size in bytes of the value of field Version
func (s *KeySignature) VersionTotalSize() uint64 {
return 1
}
// KeySize returns the size in bytes of the value of field Key
func (s *KeySignature) KeyTotalSize() uint64 {
return s.Key.TotalSize()
}
// SignatureSize returns the size in bytes of the value of field Signature
func (s *KeySignature) SignatureTotalSize() uint64 {
return s.Signature.TotalSize()
}
// VersionOffset returns the offset in bytes of field Version
func (s *KeySignature) VersionOffset() uint64 {
return 0
}
// KeyOffset returns the offset in bytes of field Key
func (s *KeySignature) KeyOffset() uint64 {
return s.VersionOffset() + s.VersionTotalSize()
}
// SignatureOffset returns the offset in bytes of field Signature
func (s *KeySignature) SignatureOffset() uint64 {
return s.KeyOffset() + s.KeyTotalSize()
}
// Size returns the total size of the KeySignature.
func (s *KeySignature) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.VersionTotalSize()
size += s.KeyTotalSize()
size += s.SignatureTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *KeySignature) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Key Signature", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version))
// ManifestFieldType is subStruct
lines = append(lines, pretty.SubValue(depth+1, "Key", "", &s.Key))
// ManifestFieldType is subStruct
lines = append(lines, pretty.SubValue(depth+1, "Signature", "", &s.Signature))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,129 @@
//go:generate manifestcodegen
package manifest
import (
"crypto"
"crypto/rand"
"fmt"
"math/big"
"github.com/google/go-tpm/tpm2"
)
var (
// RandReader exports the rand.Reader
RandReader = rand.Reader
)
// Signature exports the Signature structure
type Signature struct {
SigScheme tpm2.Algorithm `json:"sig_scheme"`
Version uint8 `require:"0x10" json:"sig_version,omitempty"`
KeySize BitSize `json:"sig_keysize,omitempty"`
HashAlg tpm2.Algorithm `json:"sig_hashAlg"`
Data []byte `countValue:"KeySize.InBytes()" prettyValue:"dataPrettyValue()" json:"sig_data"`
}
func (m Signature) dataPrettyValue() interface{} {
r, _ := m.SignatureData()
return r
}
// SignatureData parses field Data and returns the signature as one of these types:
// * SignatureRSAASA
// * SignatureECDSA
// * SignatureSM2
func (m Signature) SignatureData() (SignatureDataInterface, error) {
switch m.SigScheme {
case tpm2.AlgRSASSA:
return SignatureRSAASA(m.Data), nil
case tpm2.AlgECDSA:
if len(m.Data) != 64 && len(m.Data) != 96 {
return nil, fmt.Errorf("invalid length of the signature data: %d (expected 64 or 96)", len(m.Data))
}
return SignatureECDSA{
R: new(big.Int).SetBytes(reverseBytes(m.Data[:len(m.Data)/2])),
S: new(big.Int).SetBytes(reverseBytes(m.Data[len(m.Data)/2:])),
}, nil
}
return nil, fmt.Errorf("unexpected signature scheme: %s", m.SigScheme)
}
// SetSignatureByData sets all the fields of the structure Signature by
// accepting one of these types as the input argument `sig`:
// * SignatureRSAASA
// * SignatureECDSA
// * SignatureSM2
func (m *Signature) SetSignatureByData(sig SignatureDataInterface) error {
err := m.SetSignatureData(sig)
if err != nil {
return err
}
switch sig := sig.(type) {
case SignatureRSAASA:
m.SigScheme = tpm2.AlgRSASSA
m.HashAlg = tpm2.AlgSHA256
m.KeySize.SetInBytes(uint16(len(m.Data)))
case SignatureECDSA:
m.SigScheme = tpm2.AlgECDSA
m.HashAlg = tpm2.AlgSHA256
m.KeySize.SetInBits(uint16(sig.R.BitLen()))
default:
return fmt.Errorf("unexpected signature type: %T", sig)
}
return nil
}
// SetSignatureData sets the value of the field Data by accepting one of these
// types as the input argument `sig`:
// * SignatureRSAASA
// * SignatureECDSA
// * SignatureSM2
func (m *Signature) SetSignatureData(sig SignatureDataInterface) error {
switch sig := sig.(type) {
case SignatureRSAASA:
m.Data = sig
case SignatureECDSA, SignatureSM2:
var r, s *big.Int
switch sig := sig.(type) {
case SignatureECDSA:
r, s = sig.R, sig.S
default:
return fmt.Errorf("internal error")
}
if r.BitLen() != s.BitLen() {
return fmt.Errorf("the length of component R (%d) is not equal to the length of component S (%d)", r.BitLen(), s.BitLen())
}
if r.BitLen() != 256 && r.BitLen() != 384 {
return fmt.Errorf("component R (or S) size should be 256 or 384 bites (not %d)", r.BitLen())
}
m.Data = make([]byte, r.BitLen()/8+s.BitLen()/8)
copy(m.Data[:], reverseBytes(r.Bytes()))
copy(m.Data[r.BitLen()/8:], reverseBytes(s.Bytes()))
default:
return fmt.Errorf("unexpected signature type: %T", sig)
}
return nil
}
// SetSignature calculates the signature accordingly to arguments signAlgo,
// privKey and signedData; and sets all the fields of the structure Signature.
//
// if signAlgo is zero then it is detected automatically, based on the type
// of the provided private key.
func (m *Signature) SetSignature(signAlgo tpm2.Algorithm, privKey crypto.Signer, signedData []byte) error {
signData, err := NewSignatureData(signAlgo, privKey, signedData)
if err != nil {
return fmt.Errorf("unable to construct the signature data: %w", err)
}
err = m.SetSignatureByData(signData)
if err != nil {
return fmt.Errorf("unable to set the signature: %w", err)
}
return nil
}

View File

@ -0,0 +1,252 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest
package manifest
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
)
// NewSignature returns a new instance of Signature with
// all default values set.
func NewSignature() *Signature {
s := &Signature{}
// Set through tag "required":
s.Version = 0x10
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *Signature) Validate() error {
// See tag "require"
if s.Version != 0x10 {
return fmt.Errorf("field 'Version' expects value '0x10', but has %v", s.Version)
}
return nil
}
// ReadFrom reads the Signature from 'r' in format defined in the document #575623.
func (s *Signature) ReadFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// SigScheme (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.SigScheme)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'SigScheme': %w", err)
}
totalN += int64(n)
}
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Version': %w", err)
}
totalN += int64(n)
}
// KeySize (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.KeySize)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'KeySize': %w", err)
}
totalN += int64(n)
}
// HashAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.HashAlg)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'HashAlg': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
size := uint16(s.KeySize.InBytes())
s.Data = make([]byte, size)
n, err := len(s.Data), binary.Read(r, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *Signature) RehashRecursive() {
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *Signature) Rehash() {
}
// WriteTo writes the Signature into 'w' in format defined in
// the document #575623.
func (s *Signature) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// SigScheme (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.SigScheme)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'SigScheme': %w", err)
}
totalN += int64(n)
}
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Version': %w", err)
}
totalN += int64(n)
}
// KeySize (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.KeySize)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'KeySize': %w", err)
}
totalN += int64(n)
}
// HashAlg (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.HashAlg)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'HashAlg': %w", err)
}
totalN += int64(n)
}
// Data (ManifestFieldType: arrayDynamic)
{
n, err := len(s.Data), binary.Write(w, binary.LittleEndian, s.Data)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Data': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// SigSchemeSize returns the size in bytes of the value of field SigScheme
func (s *Signature) SigSchemeTotalSize() uint64 {
return 2
}
// VersionSize returns the size in bytes of the value of field Version
func (s *Signature) VersionTotalSize() uint64 {
return 1
}
// KeySizeSize returns the size in bytes of the value of field KeySize
func (s *Signature) KeySizeTotalSize() uint64 {
return 2
}
// HashAlgSize returns the size in bytes of the value of field HashAlg
func (s *Signature) HashAlgTotalSize() uint64 {
return 2
}
// DataSize returns the size in bytes of the value of field Data
func (s *Signature) DataTotalSize() uint64 {
return uint64(len(s.Data))
}
// SigSchemeOffset returns the offset in bytes of field SigScheme
func (s *Signature) SigSchemeOffset() uint64 {
return 0
}
// VersionOffset returns the offset in bytes of field Version
func (s *Signature) VersionOffset() uint64 {
return s.SigSchemeOffset() + s.SigSchemeTotalSize()
}
// KeySizeOffset returns the offset in bytes of field KeySize
func (s *Signature) KeySizeOffset() uint64 {
return s.VersionOffset() + s.VersionTotalSize()
}
// HashAlgOffset returns the offset in bytes of field HashAlg
func (s *Signature) HashAlgOffset() uint64 {
return s.KeySizeOffset() + s.KeySizeTotalSize()
}
// DataOffset returns the offset in bytes of field Data
func (s *Signature) DataOffset() uint64 {
return s.HashAlgOffset() + s.HashAlgTotalSize()
}
// Size returns the total size of the Signature.
func (s *Signature) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.SigSchemeTotalSize()
size += s.VersionTotalSize()
size += s.KeySizeTotalSize()
size += s.HashAlgTotalSize()
size += s.DataTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *Signature) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Signature", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Sig Scheme", "", &s.SigScheme))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Key Size", "", &s.KeySize))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Hash Alg", "", &s.HashAlg))
// ManifestFieldType is arrayDynamic
lines = append(lines, pretty.SubValue(depth+1, "Data", "", s.dataPrettyValue()))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,138 @@
package manifest
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha256"
"fmt"
"math/big"
"github.com/google/go-tpm/tpm2"
)
// NewSignatureData returns an implementation of SignatureDataInterface,
// accordingly to signAlgo, privKey and signedData.
//
// if signAlgo is zero then it is detected automatically, based on the type
// of the provided private key.
func NewSignatureData(
signAlgo tpm2.Algorithm,
privKey crypto.Signer,
signedData []byte,
) (SignatureDataInterface, error) {
if signAlgo == 0 {
// auto-detect the sign algorithm, based on the provided signing key
switch privKey.(type) {
case *rsa.PrivateKey:
signAlgo = tpm2.AlgRSASSA
case *ecdsa.PrivateKey:
signAlgo = tpm2.AlgECDSA
}
}
switch signAlgo {
case tpm2.AlgRSASSA:
rsaPrivateKey, ok := privKey.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("expected private RSA key (type %T), but received %T", rsaPrivateKey, privKey)
}
h := sha256.New()
_, _ = h.Write(signedData)
bpmHash := h.Sum(nil)
data, err := rsa.SignPKCS1v15(RandReader, rsaPrivateKey, crypto.SHA256, bpmHash)
if err != nil {
return nil, fmt.Errorf("unable to sign with RSASSA the data: %w", err)
}
return SignatureRSAASA(data), nil
case tpm2.AlgECDSA:
eccPrivateKey, ok := privKey.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("expected private ECDSA key (type %T), but received %T", eccPrivateKey, privKey)
}
var data SignatureECDSA
var err error
data.R, data.S, err = ecdsa.Sign(RandReader, eccPrivateKey, signedData)
if err != nil {
return nil, fmt.Errorf("unable to sign with ECDSA the data: %w", err)
}
return data, nil
}
return nil, fmt.Errorf("signing algorithm '%s' is not implemented in this library", signAlgo)
}
// SignatureDataInterface is the interface which abstracts all the signature data types.
type SignatureDataInterface interface {
fmt.Stringer
// Verify returns nil if signedData was indeed signed by key pk, and
// returns an appropriate error otherwise.
Verify(pk crypto.PublicKey, signedData []byte) error
}
// SignatureRSAASA is RSAASA signature bytes.
type SignatureRSAASA []byte
// String implements fmt.Stringer
func (s SignatureRSAASA) String() string {
return fmt.Sprintf("0x%X", []byte(s))
}
// Verify implements SignatureDataInterface.
func (s SignatureRSAASA) Verify(pkIface crypto.PublicKey, signedData []byte) error {
pk, ok := pkIface.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("expected public key of type %T, but received %T", pk, pkIface)
}
h := sha256.New()
h.Write(signedData)
hash := h.Sum(nil)
err := rsa.VerifyPKCS1v15(pk, crypto.SHA256, hash, s)
if err != nil {
return fmt.Errorf("data was not signed by the key: %w", err)
}
return nil
}
// SignatureECDSA is a structure with components of an ECDSA signature.
type SignatureECDSA struct {
// R is the R component of the signature.
R *big.Int
// S is the S component of the signature.
S *big.Int
}
// String implements fmt.Stringer
func (s SignatureECDSA) String() string {
return fmt.Sprintf("{R: 0x%X, S: 0x%X}", s.R, s.S)
}
// Verify implements SignatureDataInterface.
func (s SignatureECDSA) Verify(pkIface crypto.PublicKey, signedData []byte) error {
return fmt.Errorf("support of ECDSA signatures is not implemented, yet")
}
// SignatureSM2 is a structure with components of an SM2 signature.
type SignatureSM2 struct {
// R is the R component of the signature.
R *big.Int
// S is the S component of the signature.
S *big.Int
}
// String implements fmt.Stringer
func (s SignatureSM2) String() string {
return fmt.Sprintf("{R: 0x%X, S: 0x%X}", s.R, s.S)
}
// Verify implements SignatureDataInterface.
func (s SignatureSM2) Verify(pkIface crypto.PublicKey, signedData []byte) error {
return fmt.Errorf("support of SM2 signatures is not implemented, yet")
}

View File

@ -0,0 +1,55 @@
//go:generate manifestcodegen
package manifest
import (
"encoding/binary"
"io"
)
var (
binaryOrder = binary.LittleEndian
)
type StructInfo struct {
ID StructureID
Version uint8
Variable0 uint8
ElementSize uint16
}
func (s StructInfo) StructInfo() StructInfo {
return s
}
type StructureID [8]byte
// String returns the ID as a string.
func (s StructureID) String() string {
return string(s[:])
}
type Structure interface {
io.ReaderFrom
io.WriterTo
TotalSize() uint64
// PrettyString returns the whole object as a structured string.
PrettyString(depth uint, withHeader bool) string
}
type Element interface {
Structure
ReadDataFrom(r io.Reader) (int64, error)
GetStructInfo() StructInfo
SetStructInfo(StructInfo)
}
type ElementsContainer interface {
Structure
GetFieldByStructID(structID string) interface{}
}
// Manifest is an abstract manifest.
type Manifest interface {
Structure
}

View File

@ -0,0 +1,213 @@
// +build !manifestcodegen
// Code generated by "menifestcodegen". DO NOT EDIT.
// To reproduce: go run github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/manifestcodegen/cmd/manifestcodegen github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest
package manifest
import (
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/internal/pretty"
)
var (
// Just to avoid errors in "import" above in case if it wasn't used below
_ = binary.LittleEndian
_ = (fmt.Stringer)(nil)
_ = (io.Reader)(nil)
_ = pretty.Header
_ = strings.Join
)
// NewStructInfo returns a new instance of StructInfo with
// all default values set.
func NewStructInfo() *StructInfo {
s := &StructInfo{}
s.Rehash()
return s
}
// Validate (recursively) checks the structure if there are any unexpected
// values. It returns an error if so.
func (s *StructInfo) Validate() error {
return nil
}
// ReadFrom reads the StructInfo from 'r' in format defined in the document #575623.
func (s *StructInfo) ReadFrom(r io.Reader) (int64, error) {
totalN := int64(0)
// ID (ManifestFieldType: arrayStatic)
{
n, err := 8, binary.Read(r, binary.LittleEndian, s.ID[:])
if err != nil {
return totalN, fmt.Errorf("unable to read field 'ID': %w", err)
}
totalN += int64(n)
}
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Version': %w", err)
}
totalN += int64(n)
}
// Variable0 (ManifestFieldType: endValue)
{
n, err := 1, binary.Read(r, binary.LittleEndian, &s.Variable0)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'Variable0': %w", err)
}
totalN += int64(n)
}
// ElementSize (ManifestFieldType: endValue)
{
n, err := 2, binary.Read(r, binary.LittleEndian, &s.ElementSize)
if err != nil {
return totalN, fmt.Errorf("unable to read field 'ElementSize': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// RehashRecursive calls Rehash (see below) recursively.
func (s *StructInfo) RehashRecursive() {
s.Rehash()
}
// Rehash sets values which are calculated automatically depending on the rest
// data. It is usually about the total size field of an element.
func (s *StructInfo) Rehash() {
}
// WriteTo writes the StructInfo into 'w' in format defined in
// the document #575623.
func (s *StructInfo) WriteTo(w io.Writer) (int64, error) {
totalN := int64(0)
s.Rehash()
// ID (ManifestFieldType: arrayStatic)
{
n, err := 8, binary.Write(w, binary.LittleEndian, s.ID[:])
if err != nil {
return totalN, fmt.Errorf("unable to write field 'ID': %w", err)
}
totalN += int64(n)
}
// Version (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.Version)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Version': %w", err)
}
totalN += int64(n)
}
// Variable0 (ManifestFieldType: endValue)
{
n, err := 1, binary.Write(w, binary.LittleEndian, &s.Variable0)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'Variable0': %w", err)
}
totalN += int64(n)
}
// ElementSize (ManifestFieldType: endValue)
{
n, err := 2, binary.Write(w, binary.LittleEndian, &s.ElementSize)
if err != nil {
return totalN, fmt.Errorf("unable to write field 'ElementSize': %w", err)
}
totalN += int64(n)
}
return totalN, nil
}
// IDSize returns the size in bytes of the value of field ID
func (s *StructInfo) IDTotalSize() uint64 {
return 8
}
// VersionSize returns the size in bytes of the value of field Version
func (s *StructInfo) VersionTotalSize() uint64 {
return 1
}
// Variable0Size returns the size in bytes of the value of field Variable0
func (s *StructInfo) Variable0TotalSize() uint64 {
return 1
}
// ElementSizeSize returns the size in bytes of the value of field ElementSize
func (s *StructInfo) ElementSizeTotalSize() uint64 {
return 2
}
// IDOffset returns the offset in bytes of field ID
func (s *StructInfo) IDOffset() uint64 {
return 0
}
// VersionOffset returns the offset in bytes of field Version
func (s *StructInfo) VersionOffset() uint64 {
return s.IDOffset() + s.IDTotalSize()
}
// Variable0Offset returns the offset in bytes of field Variable0
func (s *StructInfo) Variable0Offset() uint64 {
return s.VersionOffset() + s.VersionTotalSize()
}
// ElementSizeOffset returns the offset in bytes of field ElementSize
func (s *StructInfo) ElementSizeOffset() uint64 {
return s.Variable0Offset() + s.Variable0TotalSize()
}
// Size returns the total size of the StructInfo.
func (s *StructInfo) TotalSize() uint64 {
if s == nil {
return 0
}
var size uint64
size += s.IDTotalSize()
size += s.VersionTotalSize()
size += s.Variable0TotalSize()
size += s.ElementSizeTotalSize()
return size
}
// PrettyString returns the content of the structure in an easy-to-read format.
func (s *StructInfo) PrettyString(depth uint, withHeader bool) string {
var lines []string
if withHeader {
lines = append(lines, pretty.Header(depth, "Struct Info", s))
}
if s == nil {
return strings.Join(lines, "\n")
}
// ManifestFieldType is arrayStatic
lines = append(lines, pretty.SubValue(depth+1, "ID", "", &s.ID))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Version", "", &s.Version))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Variable 0", "", &s.Variable0))
// ManifestFieldType is endValue
lines = append(lines, pretty.SubValue(depth+1, "Element Size", "", &s.ElementSize))
if depth < 2 {
lines = append(lines, "")
}
return strings.Join(lines, "\n")
}

View File

@ -0,0 +1,9 @@
package manifest
// SVN represents Security Version Number.
type SVN uint8
// SVN returns the Security Version Number of an SVN field
func (svn SVN) SVN() uint8 {
return uint8(svn) & 0x0f
}

View File

@ -0,0 +1,416 @@
package bg
import (
"bytes"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/google/go-tpm/tpm2"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/key"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
"github.com/creasty/defaults"
"github.com/tidwall/pretty"
)
// IbbSegments 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 tpm2.Algorithm `json:"algorithm"` //
}
// TODO: remove this structure, it could be replaced with something like:
// type BootGuardOptions struct {
// BPM *bootpolicy.Manifest
// KM *key.Manifest
// }
// It will also remove a lot of extra code.
// BootGuardOptions presents all available options for BootGuard configuarion file.
type BootGuardOptions struct {
BootPolicyManifest bootpolicy.Manifest
KeyManifest key.Manifest
}
// ParseConfig parses a boot guard option json file
func ParseConfig(filepath string) (*BootGuardOptions, error) {
var bgo BootGuardOptions
data, err := ioutil.ReadFile(filepath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(data, &bgo); err != nil {
return nil, err
}
return &bgo, nil
}
func setBPMHeader(bgo *BootGuardOptions, bpm *bootpolicy.Manifest) (*bootpolicy.BPMH, error) {
header := bootpolicy.NewBPMH()
if err := defaults.Set(header); err != nil {
return nil, err
}
header.BPMRevision = bgo.BootPolicyManifest.BPMRevision
header.BPMSVN = manifest.SVN(bgo.BootPolicyManifest.BPMH.BPMSVN)
header.ACMSVNAuth = manifest.SVN(bgo.BootPolicyManifest.BPMH.ACMSVNAuth)
header.NEMDataStack = bootpolicy.Size4K(bgo.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, err
}
_, err = reader.Seek(int64(addr), io.SeekStart)
if err != nil {
return nil, err
}
size := uint64(ibb.Size)
ibbSegments[idx] = make([]byte, size)
_, err = reader.Read(ibbSegments[idx])
if err != nil {
return nil, err
}
}
return ibbSegments, nil
}
func getIBBsDigest(ibbs []bootpolicy.IBBSegment, image []byte, algo tpm2.Algorithm) ([]byte, error) {
var hash []byte
switch algo {
case tpm2.AlgSHA1:
h := sha1.New()
segments, err := getIBBSegment(ibbs, image)
if err != nil {
return nil, err
}
for _, segment := range segments {
_, err = h.Write(segment)
if err != nil {
return nil, err
}
}
hash = h.Sum(nil)
case tpm2.AlgSHA256:
h := sha256.New()
segments, err := getIBBSegment(ibbs, image)
if err != nil {
return nil, err
}
for _, segment := range segments {
_, err = h.Write(segment)
if err != nil {
return nil, err
}
}
hash = h.Sum(nil)
case tpm2.AlgSHA384:
h := sha512.New384()
segments, err := getIBBSegment(ibbs, image)
if err != nil {
return nil, err
}
for _, segment := range segments {
_, err = h.Write(segment)
if err != nil {
return nil, err
}
}
hash = h.Sum(nil)
case tpm2.AlgSHA512:
h := sha512.New512_256()
segments, err := getIBBSegment(ibbs, image)
if err != nil {
return nil, err
}
for _, segment := range segments {
_, err = h.Write(segment)
if err != nil {
return nil, err
}
}
hash = h.Sum(nil)
case tpm2.AlgNull:
return nil, nil
default:
return nil, fmt.Errorf("couldn't match requested hash algorithm: 0x%x", algo)
}
return hash, nil
}
func setIBBSegment(bgo *BootGuardOptions, image []byte) (*bootpolicy.SE, error) {
for iterator, item := range bgo.BootPolicyManifest.SE[0].DigestList.List {
d, err := getIBBsDigest(bgo.BootPolicyManifest.SE[0].IBBSegments, image, item.HashAlg)
if err != nil {
return nil, err
}
bgo.BootPolicyManifest.SE[0].DigestList.List[iterator].HashBuffer = make([]byte, len(d))
copy(bgo.BootPolicyManifest.SE[0].DigestList.List[iterator].HashBuffer, d)
}
return &bgo.BootPolicyManifest.SE[0], nil
}
func setTXTElement(bgo *BootGuardOptions) (*bootpolicy.TXT, error) {
txte := bootpolicy.NewTXT()
txte = bgo.BootPolicyManifest.TXTE
return txte, nil
}
func setPCDElement(bgo *BootGuardOptions) (*bootpolicy.PCD, error) {
pcde := bootpolicy.NewPCD()
if bgo.BootPolicyManifest.PCDE == nil {
return nil, nil
}
pcde.Data = bgo.BootPolicyManifest.PCDE.Data
return pcde, nil
}
func setPMElement(bgo *BootGuardOptions) (*bootpolicy.PM, error) {
pme := bootpolicy.NewPM()
if bgo.BootPolicyManifest.PME == nil {
return nil, nil
}
pme.Data = bgo.BootPolicyManifest.PME.Data
return pme, nil
}
func setPMSElement(bgo *BootGuardOptions, bpm *bootpolicy.Manifest) (*bootpolicy.Signature, error) {
psme := bootpolicy.NewSignature()
return psme, nil
}
// SetKM takes BootGuardOptiones struct and initializes a new KM with the given configuration.
func SetKM(bgo *BootGuardOptions) (*key.Manifest, error) {
km := key.NewManifest()
km = &bgo.KeyManifest
return km, nil
}
// GenerateBPM generates a Boot Policy Manifest with the given config and firmware image
func GenerateBPM(bgo *BootGuardOptions, biosFilepath string) (*bootpolicy.Manifest, error) {
bpm := bootpolicy.NewManifest()
data, err := ioutil.ReadFile(biosFilepath)
if err != nil {
return nil, err
}
se, err := setIBBSegment(bgo, data)
if err != nil {
return nil, err
}
bpm.SE = append(bpm.SE, *se)
bpm.TXTE, err = setTXTElement(bgo)
if err != nil {
return nil, err
}
bpm.PCDE, err = setPCDElement(bgo)
if err != nil {
return nil, err
}
bpm.PME, err = setPMElement(bgo)
if err != nil {
return nil, err
}
bpmh, err := setBPMHeader(bgo, bpm)
if err != nil {
return nil, err
}
bpm.BPMH = *bpmh
pmse, err := setPMSElement(bgo, bpm)
if err != nil {
return nil, err
}
bpm.PMSE = *pmse
return bpm, nil
}
// CreateManifests takes a boot guard options configuration file in json format and a firmware image and extracts km and bpm
func CreateManifests(configFilepath, biosFilepath string) (*bootpolicy.Manifest, *key.Manifest, error) {
bpm := bootpolicy.NewManifest()
bgo, err := ParseConfig(configFilepath)
if err != nil {
return nil, nil, err
}
data, err := ioutil.ReadFile(biosFilepath)
if err != nil {
return nil, nil, err
}
_, _, acmBuf, err := ParseFITEntries(data)
if err != nil {
return nil, nil, err
}
acm, _, _, _, err, err2 := tools.ParseACM(acmBuf)
if err != nil {
return nil, nil, err
}
if err2 != nil {
return nil, nil, err
}
se, err := setIBBSegment(bgo, data)
if err != nil {
return nil, nil, err
}
bpm.SE = append(bpm.SE, *se)
bpm.TXTE, err = setTXTElement(bgo)
if err != nil {
return nil, nil, err
}
bpm.PCDE, err = setPCDElement(bgo)
if err != nil {
return nil, nil, err
}
bpm.PME, err = setPMElement(bgo)
if err != nil {
return nil, nil, err
}
bpmh, err := setBPMHeader(bgo, bpm)
if err != nil {
return nil, nil, err
}
bpm.BPMH = *bpmh
pmse, err := setPMSElement(bgo, bpm)
if err != nil {
return nil, nil, err
}
bpm.PMSE = *pmse
km, err := SetKM(bgo)
if err != nil {
return nil, nil, err
}
if bgo.BootPolicyManifest.NEMDataStack <= 0 {
bpm.BPMH.NEMDataStack, err = CalculateNEMSize(data, bpm, km, acm)
if err != nil {
return nil, nil, err
}
}
bpm.RehashRecursive()
km.RehashRecursive()
return bpm, km, nil
}
// WriteConfig writes a BootGuard config file to the given path with given options.
func WriteConfig(f *os.File, bgo *BootGuardOptions) error {
cfg, err := json.Marshal(bgo)
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) (*BootGuardOptions, error) {
var bgo BootGuardOptions
var bpm *bootpolicy.Manifest
var km *key.Manifest
bios, err := ioutil.ReadFile(biosFilepath)
if err != nil {
return nil, err
}
bpmBuf, kmBuf, _, err := ParseFITEntries(bios)
if err != nil {
return nil, err
}
if len(bpmBuf) == 0 {
return nil, fmt.Errorf("ReadConfigurationFromBIOSImage: No BPM found to read config from")
}
reader := bytes.NewReader(bpmBuf)
bpm, err = ParseBPM(reader)
if err != nil {
return nil, err
}
if len(kmBuf) == 0 {
return nil, fmt.Errorf("ReadConfigurationFromBIOSImage: No KM found to read config from")
}
reader = bytes.NewReader(kmBuf)
km, err = ParseKM(reader)
if err != nil {
return nil, err
}
/* Boot Policy Manifest */
// BPMH
bgo.BootPolicyManifest = *bpm
/* Key Manifest */
bgo.KeyManifest = *km
data, err := json.Marshal(bgo)
if err != nil {
return nil, err
}
json := pretty.Pretty(data)
if _, err = configFilepath.Write(json); err != nil {
return nil, err
}
return &bgo, 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 tpm2.Algorithm) ([]key.Hash, error) {
var data []byte
pubkey, err := ReadPubKey(path)
if err != nil {
return nil, err
}
alg, err := hashAlg.Hash()
if err != nil {
return nil, err
}
hash := alg.New()
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: tpm2.Algorithm(hashAlg),
}
copy(hStruc.HashBuffer, data)
kH := key.Hash{
Usage: key.UsageBPMSigningPKD,
Digest: *hStruc,
}
keyHashes = append(keyHashes, kH)
return keyHashes, nil
}

View File

@ -0,0 +1,59 @@
package bg
import "testing"
func TestParseConfigValid(T *testing.T) {
}
func TestParseConfigInvalid(T *testing.T) {
}
func TestSetBPMHeaderValid(T *testing.T) {
}
func TestSetBPMHeaderInvalidBadBGO(T *testing.T) {
}
func TestSetBPMHeaderInvalidBadManifest(T *testing.T) {
}
func TestSetIBBSegmentValid(T *testing.T) {
}
func TestSetIBBSegmentInvalidBGO(T *testing.T) {
}
func TestSetIBBSegmentInvalidImage(T *testing.T) {
}
func TestTXTElementValid(T *testing.T) {
}
func TestTXTElementInvalidBadBGO(T *testing.T) {
}
func TestSetPCDElementValid(T *testing.T) {
}
func TestSetPCDElementInvalidBGO(T *testing.T) {
}
func TestPMElementValid(T *testing.T) {
}
func TestPMElementInvalidBGO(T *testing.T) {
}

View File

@ -0,0 +1,8 @@
package bg
const (
keySignatureElementMaxSize = 3072 // how this value was calculated?
defaultStackAndDataSize = 4096
defaultLLCSize = 0x800000
additionalNEMSize = 0x80000
)

View File

@ -0,0 +1,230 @@
package bg
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"strings"
)
const (
//Supported RSA bit length of Intel TXT/CBnT technology
rsaLen2048 = int(2048)
rsaLen3072 = int(3072)
)
// GenRSAKey takes the required keylength, two boolean to decide for KM and BPM key and a path
// to create a RSA key pair and writes its public and private keys to files.
func GenRSAKey(len int, password string, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile *os.File) error {
if len == rsaLen2048 || len == rsaLen3072 {
key, err := rsa.GenerateKey(rand.Reader, len)
if err != nil {
return err
}
if err := writePrivKeyToFile(key, kmPrivFile, password); err != nil {
return err
}
if err := writePubKeyToFile(key.Public(), kmPubFile); err != nil {
return err
}
key, err = rsa.GenerateKey(rand.Reader, len)
if err != nil {
return err
}
if err := writePrivKeyToFile(key, bpmPrivFile, password); err != nil {
return err
}
if err := writePubKeyToFile(key.Public(), bpmPubFile); err != nil {
return err
}
return nil
}
return fmt.Errorf("RSA key length must be 2048 or 3084 Bits, but length is: %d", len)
}
// GenECCKey takes the required curve, two boolean to decide for KM and BPM key and a path
// to create a ECDSA key pair and writes its public and private keys to files.
func GenECCKey(curve int, password string, kmPubFile, kmPrivFile, bpmPubFile, bpmPrivFile *os.File) error {
var ellCurve elliptic.Curve
switch curve {
case 224:
ellCurve = elliptic.P224()
case 256:
ellCurve = elliptic.P256()
default:
return fmt.Errorf("Selected ECC algorithm not supported")
}
key, err := ecdsa.GenerateKey(ellCurve, rand.Reader)
if err != nil {
return err
}
if err := writePrivKeyToFile(key, kmPrivFile, password); err != nil {
return err
}
if err := writePubKeyToFile(key.Public(), kmPubFile); err != nil {
return err
}
key, err = ecdsa.GenerateKey(ellCurve, rand.Reader)
if err != nil {
return err
}
if err := writePrivKeyToFile(key, bpmPrivFile, password); err != nil {
return err
}
if err := writePubKeyToFile(key.Public(), bpmPubFile); err != nil {
return err
}
return nil
}
func writePrivKeyToFile(k crypto.PrivateKey, f *os.File, password string) error {
var key *[]byte
b, err := x509.MarshalPKCS8PrivateKey(k)
bpemBlock := &pem.Block{
Bytes: b,
}
bpem := pem.EncodeToMemory(bpemBlock)
if password != "" {
encKey, err := encryptPrivFile(&bpem, password)
if err != nil {
return err
}
key = encKey
} else {
key = &bpem
}
_, err = f.Write(*key)
if err != nil {
return err
}
return nil
}
func writePubKeyToFile(k crypto.PublicKey, f *os.File) error {
b, err := x509.MarshalPKIXPublicKey(k)
if err != nil {
return err
}
bpemBlock := &pem.Block{
Bytes: b,
}
bpem := pem.EncodeToMemory(bpemBlock)
_, err = f.Write(bpem)
if err != nil {
return err
}
return nil
}
func encryptPrivFile(data *[]byte, password string) (*[]byte, error) {
// Hash key to select aes-256 -> using SHA256
hash := crypto.SHA256.New()
hash.Write([]byte(password))
hashPW := hash.Sum(nil)
bc, err := aes.NewCipher(hashPW)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(bc)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
ct := gcm.Seal(nonce, nonce, *data, nil)
return &ct, nil
}
// DecryptPrivKey takes the encrypted Key as byte slice and the passwort to decrypt the priveate key and returns it with it's type.
func DecryptPrivKey(data []byte, password string) (crypto.PrivateKey, error) {
var plain []byte
if password != "" {
// Set up the crypto stuff
hash := crypto.SHA256.New()
hash.Write([]byte(password))
hashPW := hash.Sum(nil)
aes, err := aes.NewCipher(hashPW)
if err != nil {
return nil, err
}
aesGCM, err := cipher.NewGCM(aes)
if err != nil {
return nil, err
}
nonceSize := aesGCM.NonceSize()
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plain, err = aesGCM.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
} else {
plain = data
}
key, err := parsePrivateKey(plain)
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
return key, nil
}
// ReadPubKey ready a pem encoded RSA/ECC public key file
func ReadPubKey(path string) (crypto.PublicKey, error) {
raw, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
for {
block, rest := pem.Decode(raw)
if block == nil {
break
}
if !strings.Contains(block.Type, "CERTIFICATE") {
if strings.Contains(block.Type, "RSA") {
key, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("Parsing error in x509.ParsePKCS1PublicKey: %v", err)
}
return key, nil
}
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err == nil {
if key, ok := key.(crypto.PublicKey); ok {
return key, nil
}
return nil, fmt.Errorf("found unknown public key type (%T) in PKIX wrapping", key)
}
return nil, err
}
raw = rest
}
return nil, fmt.Errorf("failed to parse public key")
}

View File

@ -0,0 +1,48 @@
package bg
import (
"bytes"
"crypto"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
"github.com/9elements/converged-security-suite/v2/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
}
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)
}
return nil, err
}
raw = rest
}
return nil, fmt.Errorf("failed to parse private key")
}

View File

@ -0,0 +1,51 @@
package bg
// CMOSIoAddress holds information about the location of on-demand power down requests in CMOS.
// The structure is a substructure used in PowerDownRequest structure.
type CMOSIoAddress struct {
MediaType uint8
IndexRegisterAddress uint16
DataRegisterAddress uint16
BitFieldWidth uint8
BitFieldPosition uint8
IndexOffset uint8
}
// TPMNvAddress holds information about the location of on-demand power down requests in TPM NVRAM.
// The structure is a substructure used in PowerDownRequest structure.
type TPMNvAddress struct {
MediaType uint8
NVIndex uint32
BitFieldWidth uint8
BitFieldPosition uint8
IndexOffset uint8
}
// PowerDownRequest holds information of the storage location for the on-demand power down variable.
// Field: PDReqMedia holds an union of 1 CMOSIoAddress or array of 1:3 TPMNvAddress
type PowerDownRequest struct {
ID uint64 `default:"0x5F5F504452535F5F"`
Version uint8 `default:"0x10"`
SizeOfData uint16 `default:"0"`
Reserved uint8 `default:"0"`
PDReqMedia []byte
}
// Pcr0Data represents the data hashed into PCR0 of the TPM by S-ACM
type Pcr0Data struct {
ACMPolicyStatus uint64
ACMSVN uint16
ACMSignature []byte
KMSignature []byte
BPMSignature []byte
BPMIBBDigest []byte
}
// Pcr7Data represents the data hashed into PCR7 of the TPM by S-ACM optionally
type Pcr7Data struct {
ACMPolicyStatus uint64
ACMSVN uint16
ACMKeyHash [32]byte
BPMKey [32]byte
BPMKeyHash []byte
}

View File

@ -0,0 +1 @@
package bg

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
-----BEGIN -----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuA0rrZM4ZWo+JEKy1Dxn
KERb9MijZYUezT5bFH3xF5aVbLk7/WXSzkmDIrgxrdvx+JG2OTa5r8qquxqesxTD
o2rwTt0eC0EgvoLHVwPJBRm53Ik9vEHO7X0fgeK4hrml9XpCpllhWvMK7re+CyCm
+8kxQajxnELV2rZhXFdiPvJEnp3bwYmbiHzsOcrLTveRPwAJDdJZKBLL2MZ1ONJa
zZ5/YOcRLpVpF8zxvUWN/vFKxYp7ClgkaZPQpIGuMgGyCbuAed31ca46ynoIskQ/
uo7WPfG3O+ocQ5pHNoXtjRushTvDOQ0JqkLTkeF79I6tL59cspjCvtaCeLgay4LV
rQIDAQAB
-----END -----

Binary file not shown.

View File

@ -0,0 +1,9 @@
-----BEGIN -----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtWYUau8w1RXhoZUPeIOp
7t/ZnjHWMZlt1U7730l6nFAU56PBz0xpYhcLpPw+p1+n64fdMFPvM8M8IcIWuUNm
FWbKAvl7M0Pl/UNfLKGeHN65VhVNysWnVTRvmyFb1cAmAZqjqkbE9pkK5AzDDiHE
z7VgTiPTRmiKg4Udm0ZZdz9rBU9+FOLlOoALUjsaMQDsEuhAaP+O3CtmtRpSFemP
WzEVJB/HYUh5sZxcDVUKIkkJhAbIreekJpCexp2KsW/EDnPC4LkVVJE7lzwue0xa
J6jCaB+fQdXWF9UOimdz5mjcqToub1jp4iG2VUS0YOENLcC3e2yWSSt1EwZiZPei
HwIDAQAB
-----END -----

View File

@ -0,0 +1,484 @@
package bg
import (
"bytes"
"crypto/sha1"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/key"
"github.com/9elements/converged-security-suite/v2/pkg/tools"
"github.com/google/go-tpm/tpm2"
)
// WriteBootGuardStructures takes a firmware image and extracts boot policy manifest, key manifest and acm into seperate files.
func WriteBootGuardStructures(image []byte, bpmFile, kmFile, acmFile *os.File) error {
bpmBuf, kmBuf, acmBuf, err := ParseFITEntries(image)
if err != nil {
return err
}
if bpmFile != nil && len(bpmBuf) > 0 {
if _, err = bpmFile.Write(bpmBuf); err != nil {
return err
}
}
if kmFile != nil && len(kmBuf) > 0 {
if _, err = kmFile.Write(kmBuf); err != nil {
return err
}
}
if acmFile != nil && len(acmBuf) > 0 {
if _, err = acmFile.Write(acmBuf); err != nil {
return err
}
}
return nil
}
// PrintBootGuardStructures takes a firmware image and prints boot policy manifest, key manifest, ACM, chipset, processor and tpm information if available.
func PrintBootGuardStructures(image []byte) error {
var km *key.Manifest
var bpm *bootpolicy.Manifest
var acm *tools.ACM
var chipsets *tools.Chipsets
var processors *tools.Processors
var tpms *tools.TPMs
var err, err2 error
bpmBuf, kmBuf, acmBuf, err := ParseFITEntries(image)
if err != nil {
return err
}
reader := bytes.NewReader(bpmBuf)
bpm, err = ParseBPM(reader)
if err != nil {
return err
}
reader = bytes.NewReader(kmBuf)
km, err = ParseKM(reader)
if err != nil {
return err
}
acm, chipsets, processors, tpms, err, err2 = tools.ParseACM(acmBuf)
if err != nil || err2 != nil {
return err
}
if bpm != nil {
fmt.Println(bpm.PrettyString(0, true))
}
if km != nil {
fmt.Println(km.PrettyString(0, true))
}
if acm != nil {
acm.PrettyPrint()
chipsets.PrettyPrint()
processors.PrettyPrint()
tpms.PrettyPrint()
}
return nil
}
// PrintFIT takes a firmware image and prints the Firmware Interface Table
func PrintFIT(image []byte) error {
fitEntries, err := tools.ExtractFit(image)
if err != nil {
return err
}
fmt.Println("----Firmware Interface Table----")
fmt.Println()
for idx, entry := range fitEntries {
fmt.Printf("Entry %d\n", idx)
entry.FancyPrint()
fmt.Println()
}
fmt.Println()
return nil
}
// ParseFITEntries takes a firmware image and extract Boot policy manifest, key manifest and acm information.
func ParseFITEntries(image []byte) ([]byte, []byte, []byte, error) {
fitEntries, err := tools.ExtractFit(image)
if err != nil {
return nil, nil, nil, err
}
var bpm, km, acm []byte
reader := bytes.NewReader(image)
for _, entry := range fitEntries {
if entry.Type() == tools.BootPolicyManifest {
if entry.Size() == 0 {
return nil, nil, nil, fmt.Errorf("FIT entry size is zero for BPM")
}
addr, err := tools.CalcImageOffset(image, entry.Address)
if err != nil {
return nil, nil, nil, err
}
reader.Seek(int64(addr), io.SeekStart)
bpm = make([]byte, entry.Size())
len, err := reader.Read(bpm)
if err != nil || uint32(len) != entry.Size() {
return nil, nil, nil, err
}
}
if entry.Type() == tools.KeyManifestRec {
if entry.Size() == 0 {
return nil, nil, nil, fmt.Errorf("FIT entry size is zero for KM")
}
addr, err := tools.CalcImageOffset(image, entry.Address)
if err != nil {
return nil, nil, nil, err
}
reader.Seek(int64(addr), io.SeekStart)
km = make([]byte, entry.Size())
len, err := reader.Read(km)
if err != nil || uint32(len) != entry.Size() {
return nil, nil, nil, err
}
}
if entry.Type() == tools.StartUpACMod {
addr, err := tools.CalcImageOffset(image, entry.Address)
if err != nil {
return nil, nil, nil, err
}
reader.Seek(int64(addr), io.SeekStart)
if entry.Size() == 0 {
buf := make([]byte, 32)
_, err := reader.Read(buf)
if err != nil {
return nil, nil, nil, err
}
reader.Seek(int64(addr), io.SeekStart)
size, err := tools.LookupACMSize(buf)
if err != nil {
return nil, nil, nil, err
}
acm = make([]byte, size)
len, err := reader.Read(acm)
if err != nil || int64(len) != size {
return nil, nil, nil, err
}
} else {
acm = make([]byte, entry.Size())
len, err := reader.Read(acm)
if err != nil || uint32(len) != entry.Size() {
return nil, nil, nil, err
}
}
}
}
if len(bpm) == 0 || len(km) == 0 || len(acm) == 0 {
return nil, nil, nil, fmt.Errorf("Image has no BPM, KM, ACM")
}
return bpm, km, acm, nil
}
func generatePCR0Content(status uint64, km *key.Manifest, bpm *bootpolicy.Manifest, acm *tools.ACM) (*Pcr0Data, []byte, error) {
var err error
var pcr0 Pcr0Data
buf := new(bytes.Buffer)
if err = binary.Write(buf, binary.BigEndian, status); err != nil {
return nil, nil, err
}
fmt.Printf("\nStatus: 0x%x\n ", status)
if err = binary.Write(buf, binary.LittleEndian, acm.Header.TxtSVN); err != nil {
return nil, nil, err
}
fmt.Printf("ACM SVN: 0x%x\n ", acm.Header.TxtSVN)
if err = binary.Write(buf, binary.LittleEndian, acm.Header.Signature); err != nil {
return nil, nil, err
}
fmt.Printf("ACM Sig: 0x%x\n ", acm.Header.Signature)
{
kmSignature, err := km.KeyAndSignature.Signature.SignatureData()
if err != nil {
return nil, nil, fmt.Errorf("unable to extract BPM signature: %w", err)
}
fmt.Printf("KM Sig: %s\n", kmSignature.String())
switch kmSignature := kmSignature.(type) {
case manifest.SignatureRSAASA:
if err = binary.Write(buf, binary.LittleEndian, kmSignature); err != nil {
return nil, nil, err
}
case manifest.SignatureECDSA:
if err = binary.Write(buf, binary.LittleEndian, kmSignature.R); err != nil {
return nil, nil, err
}
case manifest.SignatureSM2:
if err = binary.Write(buf, binary.LittleEndian, kmSignature.R); err != nil {
return nil, nil, err
}
default:
return nil, nil, fmt.Errorf("unknown KM sig type: %T", kmSignature)
}
}
{
bpmSignature, err := bpm.PMSE.KeySignature.Signature.SignatureData()
if err != nil {
return nil, nil, fmt.Errorf("unable to extract BPM signature: %w", err)
}
fmt.Printf("BPM Sig: %s\n", bpmSignature.String())
switch bpmSignature := bpmSignature.(type) {
case manifest.SignatureRSAASA:
if err = binary.Write(buf, binary.LittleEndian, bpmSignature); err != nil {
return nil, nil, err
}
case manifest.SignatureECDSA:
if err = binary.Write(buf, binary.LittleEndian, bpmSignature.R); err != nil {
return nil, nil, err
}
case manifest.SignatureSM2:
if err = binary.Write(buf, binary.LittleEndian, bpmSignature.R); err != nil {
return nil, nil, err
}
default:
return nil, nil, fmt.Errorf("unknown BPM sig type: %T", bpmSignature)
}
}
for _, se := range bpm.SE {
for i := 0; i < len(se.DigestList.List); i++ {
if se.DigestList.List[i].HashAlg == tpm2.AlgSHA1 {
if err = binary.Write(buf, binary.LittleEndian, se.DigestList.List[i].HashBuffer); err != nil {
return nil, nil, err
}
fmt.Printf("IBB Hash: 0x%x\n ", se.DigestList.List[i].HashBuffer)
}
}
}
h := sha1.New()
h.Write(buf.Bytes())
finalHash := h.Sum(nil)
fmt.Printf("PCR-0 pre hash: 0x%x\n", finalHash)
return &pcr0, finalHash, nil
}
// PrecalcPCR0 takes a firmware image and ACM Policy status and returns the Pcr0Data structure and its hash.
func PrecalcPCR0(data []byte, acmPolicySts uint64) (*Pcr0Data, []byte, error) {
fitEntries, err := tools.ExtractFit(data)
if err != nil {
return nil, nil, err
}
var km *key.Manifest
var bpm *bootpolicy.Manifest
var acm *tools.ACM
for _, entry := range fitEntries {
if entry.Type() == tools.BootPolicyManifest {
addr, err := tools.CalcImageOffset(data, entry.Address)
if err != nil {
return nil, nil, err
}
reader := bytes.NewReader(data)
reader.Seek(int64(addr), io.SeekStart)
bpm, err = ParseBPM(reader)
if err != nil {
return nil, nil, err
}
}
if entry.Type() == tools.KeyManifestRec {
addr, err := tools.CalcImageOffset(data, entry.Address)
if err != nil {
return nil, nil, err
}
reader := bytes.NewReader(data)
reader.Seek(int64(addr), io.SeekStart)
km, err = ParseKM(reader)
if err != nil {
return nil, nil, err
}
}
if entry.Type() == tools.StartUpACMod {
addr, err := tools.CalcImageOffset(data, entry.Address)
if err != nil {
return nil, nil, err
}
reader := bytes.NewReader(data)
reader.Seek(int64(addr), io.SeekStart)
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
var err2 error
acm, _, _, _, err, err2 = tools.ParseACM(buf.Bytes())
if err != nil || err2 != nil {
return nil, nil, err
}
}
}
if acmPolicySts == 0 {
txtAPI := hwapi.GetAPI()
regs, err := tools.FetchTXTRegs(txtAPI)
if err != nil {
return nil, nil, err
}
acmPolicySts, err = tools.ReadACMPolicyStatusRaw(regs)
if err != nil {
return nil, nil, err
}
}
return generatePCR0Content(acmPolicySts, km, bpm, acm)
}
// 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")
}
fit := bytes.NewReader(image)
hdr, err := tools.GetFitHeader(fit)
if err != nil {
return 0, err
}
totalSize += uint32(km.KeyManifestSignatureOffset)
totalSize += keySignatureElementMaxSize
totalSize += hdr.Size()
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 {
image, err := ioutil.ReadFile(biosFilename)
if err != nil {
return err
}
fitEntries, err := tools.ExtractFit(image)
if err != nil {
return err
}
file, err := os.OpenFile(biosFilename, os.O_RDWR, 0600)
if err != nil {
return err
}
defer file.Close()
for _, entry := range fitEntries {
if entry.Type() == tools.BootPolicyManifest {
if len(bpm) <= 0 {
continue
}
if entry.Size() == 0 {
return fmt.Errorf("FIT entry size is zero for BPM")
}
if len(bpm) > int(entry.Size()) {
return fmt.Errorf("new BPM bigger than older BPM")
}
addr, err := tools.CalcImageOffset(image, entry.Address)
if err != nil {
return err
}
_, err = file.Seek(0, 0)
if err != nil {
return err
}
size, err := file.WriteAt(bpm, int64(addr))
if err != nil {
return err
}
if size != len(bpm) {
return fmt.Errorf("couldn't write new BPM")
}
}
if entry.Type() == tools.KeyManifestRec {
if len(km) <= 0 {
continue
}
if entry.Size() == 0 {
return fmt.Errorf("FIT entry size is zero for KM")
}
if len(km) > int(entry.Size()) {
return fmt.Errorf("new KM bigger than older KM")
}
addr, err := tools.CalcImageOffset(image, entry.Address)
if err != nil {
return err
}
_, err = file.Seek(0, 0)
if err != nil {
return err
}
size, err := file.WriteAt(km, int64(addr))
if err != nil {
return err
}
if size != len(km) {
return fmt.Errorf("couldn't write new KM")
}
}
if entry.Type() == tools.StartUpACMod {
if len(acm) <= 0 {
continue
}
addr, err := tools.CalcImageOffset(image, entry.Address)
if err != nil {
return err
}
_, err = file.Seek(int64(addr), io.SeekStart)
if err != nil {
return err
}
acmHeader := make([]byte, 32)
_, err = file.Read(acmHeader)
if err != nil {
return err
}
acmLen, err := tools.LookupACMSize(acmHeader)
if err != nil {
return err
}
if acmLen == 0 {
return fmt.Errorf("ACM size is wrong")
}
if len(acm) != int(acmLen) {
return fmt.Errorf("new ACM size doesn't equal old ACM size")
}
_, err = file.Seek(0, 0)
if err != nil {
return err
}
size, err := file.WriteAt(acm, int64(addr))
if err != nil {
return err
}
if size != len(acm) {
return fmt.Errorf("couldn't write new ACM")
}
}
}
return nil
}

View File

@ -0,0 +1,29 @@
package bg
import (
"errors"
"io"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
"github.com/9elements/converged-security-suite/v2/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
}
// 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
}

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"fmt"
@ -14,7 +14,7 @@ func DefineAUXIndexTPM20(rw io.ReadWriter) error {
return fmt.Errorf("AUX index already defined in TPM 2.0 - Delete first")
}
authArea := tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession, Auth: []byte(tpm2.EmptyAuth)}
err = tpm2.NVDefineSpaceEx(rw, tpm2.HandlePlatform, authArea, "", tpm20AUXIndexDef)
err = tpm2.NVDefineSpaceEx(rw, tpm2.HandlePlatform, "", tpm20AUXIndexDef, authArea)
if err != nil {
return fmt.Errorf("NVDefineSpaceEx() failed: %v", err)
}

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"fmt"

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"fmt"

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"fmt"
@ -18,8 +18,8 @@ func DefinePSIndexTPM20(rw io.ReadWriter, passHash []byte) error {
return fmt.Errorf("getPSPolicyHash() failed: %v", err)
}
tpm2PSIndexDef.AuthPolicy = psPolicyHash
authArea := tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession, Auth: []byte(tpm2.EmptyAuth)}
err = tpm2.NVDefineSpaceEx(rw, tpm2.HandlePlatform, authArea, "", tpm2PSIndexDef)
authArea := tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession, Auth: tpm2.EmptyAuth}
err = tpm2.NVDefineSpaceEx(rw, tpm2.HandlePlatform, "", tpm2PSIndexDef, authArea)
if err != nil {
return fmt.Errorf("NVDefineSpaceEx() failed: %v", err)
}

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"fmt"
@ -44,8 +44,9 @@ func DeletePSIndexTPM20(rw io.ReadWriter, passHash []byte) error {
if err != nil {
return fmt.Errorf("PolicyOr2 failed with: %v", err)
}
err = tpm2.NVUndefineSpaceSpecial(rw, tpm2PSIndexDef.NVIndex, sessIndex, tpm2.EmptyAuth)
indexAuth := tpm2.AuthCommand{Session: sessIndex, Attributes: tpm2.AttrContinueSession, Auth: []byte(tpm2.EmptyAuth)}
platformAuth := tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession, Auth: []byte(tpm2.EmptyAuth)}
err = tpm2.NVUndefineSpaceSpecial(rw, tpm2PSIndexDef.NVIndex, indexAuth, platformAuth)
if err != nil {
return fmt.Errorf("NVUndefineSpaceSpecial() failed: %v", err)
}

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"bytes"

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"fmt"

View File

@ -1,4 +1,4 @@
package provisioning
package txt
import (
"crypto"

View File

@ -283,8 +283,8 @@ func HasFIT(txtAPI hwapi.APIInterfaces, config *tools.Configuration) (bool, erro
if err != nil {
return false, nil, err
}
hdr, err := tools.GetFitHeader(fithdr)
header := bytes.NewReader(fithdr)
hdr, err := tools.GetFitHeader(header)
if err != nil {
return false, nil, err
}

View File

@ -739,7 +739,7 @@ func ActiveIOMMU(txtAPI hwapi.APIInterfaces, config *tools.Configuration) (bool,
// ServerModeTXT checks if TXT runs in Servermode
func ServerModeTXT(txtAPI hwapi.APIInterfaces, config *tools.Configuration) (bool, error, error) {
// FIXME: Query GetSec[Parameters] ebx = 5
// FIXME: FindOverlapping GetSec[Parameters] ebx = 5
// Assume yes if dependencies are satisfied
val, err := txtAPI.HasSMRR()
if err != nil {

View File

@ -5,7 +5,8 @@ import (
"encoding/binary"
"fmt"
"io"
"log"
"github.com/google/go-tpm/tpm2"
)
const (
@ -49,25 +50,6 @@ const (
//ACMSizeOffset as defined in Document 315168-016 Chapter A.1 Table 8. Authenticated Code Module Format
ACMSizeOffset int64 = 24
//TPMAlgoSHA1 as defined in Document 315168-016 Chapter D.1.3 LCP_POLICY2
TPMAlgoSHA1 uint16 = 0x0004
//TPMAlgoSHA256 as defined in Document 315168-016 Chapter D.1.3 LCP_POLICY2
TPMAlgoSHA256 uint16 = 0x000b
//TPMAlgoSHA384 FIXME
TPMAlgoSHA384 uint16 = 0x000c
//TPMAlgoSHA512 FIXME
TPMAlgoSHA512 uint16 = 0x000d
//TPMAlgoNULL as defined in Document 315168-016 Chapter D.1.3 LCP_POLICY2
TPMAlgoNULL uint16 = 0x0010
//TPMAlgoSM3_256 as defined in Document 315168-016 Chapter D.1.3 LCP_POLICY2
TPMAlgoSM3_256 uint16 = 0x0012
//TPMAlgoRSASSA as defined in Document 315168-016 Chapter D.1.3 LCP_POLICY2
TPMAlgoRSASSA uint16 = 0x0014
//TPMAlgoECDSA as defined in Document 315168-016 Chapter D.1.3 LCP_POLICY2
TPMAlgoECDSA uint16 = 0x0018
//TPMAlgoSM2 as defined in Document 315168-016 Chapter D.1.3 LCP_POLICY2
TPMAlgoSM2 uint16 = 0x001B
//ACMheaderLen as defined in Document 315168-016 Chapter A.1 Table 8. Authenticated Code Module Format (Version 0.0)
ACMheaderLen uint32 = 161
@ -133,14 +115,14 @@ type Processors struct {
IDList []ProcessorID
}
//TPMs describes the required TPM capabilties and algorithm as found in the ACM header
// TPMs describes the required TPM capabilities and algorithm as found in the ACM header
type TPMs struct {
Capabilities uint32
Count uint16
AlgID []uint16
AlgID []tpm2.Algorithm
}
// ACMHeader exports the structure of ACM Header found in the firemware interface table
// ACMHeader exports the structure of ACM Header found in the firmware interface table
type ACMHeader struct {
ModuleType uint16
ModuleSubType uint16
@ -164,7 +146,7 @@ type ACMHeader struct {
ScratchSize uint32
PubKey [256]uint8
PubExp uint32
Signatur [256]uint8
Signature [256]uint8
}
// ACM exports the structure of Authenticated Code Modules found in the Firmware Interface Table(FIT)
@ -291,7 +273,7 @@ func ParseACM(data []byte) (*ACM, *Chipsets, *Processors, *TPMs, error, error) {
return nil, nil, nil, nil, nil, err
}
tpms.AlgID = make([]uint16, tpms.Count)
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 {
@ -303,8 +285,8 @@ func ParseACM(data []byte) (*ACM, *Chipsets, *Processors, *TPMs, error, error) {
return &acm, &chipsets, &processors, &tpms, nil, nil
}
//LookupSize returns the ACM size
func LookupSize(header []byte) (int64, error) {
//LookupACMSize returns the ACM size
func LookupACMSize(header []byte) (int64, error) {
var acmSize uint32
buf := bytes.NewReader(header[:32])
@ -328,45 +310,45 @@ func (a *ACMHeader) ParseACMFlags() *ACMFlags {
//PrettyPrint prints a human readable representation of the ACMHeader
func (a *ACMHeader) PrettyPrint() {
log.Println("Authenticated Code Module")
fmt.Println("----Authenticated Code Module----")
fmt.Println()
if a.ModuleVendor == ACMVendorIntel {
log.Println("Module Vendor: Intel")
fmt.Println(" Module Vendor: Intel")
} else {
log.Println("Module Vendor: Unknown")
fmt.Println(" Module Vendor: Unknown")
}
if a.ModuleType == ACMTypeChipset {
log.Println("Module Type: ACM_TYPE_CHIPSET")
fmt.Println(" Module Type: ACM_TYPE_CHIPSET")
} else {
log.Println("Module Type: UNKNOWN")
fmt.Println(" Module Type: UNKNOWN")
}
if a.ModuleSubType == ACMSubTypeReset {
log.Println("Module Subtype: Execute at Reset")
fmt.Println(" Module Subtype: Execute at Reset")
} else if a.ModuleSubType == 0 {
log.Println("Module Subtype: 0x0")
fmt.Println(" Module Subtype: 0x0")
} else {
log.Println("Module Subtype: Unknown")
fmt.Println(" Module Subtype: Unknown")
}
log.Printf("Module Date: 0x%02x\n", a.Date)
log.Printf("Module Size: 0x%x (%d)\n", a.Size*4, a.Size*4)
fmt.Printf(" Module Date: 0x%02x\n", a.Date)
fmt.Printf(" Module Size: 0x%x (%d)\n", a.Size*4, a.Size*4)
log.Printf("Header Length: 0x%x (%d)\n", a.HeaderLen, a.HeaderLen)
log.Printf("Header Version: %d\n", a.HeaderVersion)
log.Printf("Chipset ID: 0x%02x\n", a.ChipsetID)
log.Printf("Flags: 0x%02x\n", a.Flags)
log.Printf("TXT SVN: 0x%08x\n", a.TxtSVN)
log.Printf("SE SVN: 0x%08x\n", a.SeSVN)
log.Printf("Code Control: 0x%02x\n", a.CodeControl)
log.Printf("Entry Point: 0x%08x:%08x\n", a.SegSel, a.EntryPoint)
log.Printf("Scratch Size: 0x%x (%d)\n", a.ScratchSize, a.ScratchSize)
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()
log.Println("Info Table:")
fmt.Println(" --Info Table--")
uuidStr := fmt.Sprintf("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
a.Info.UUID.Field1,
@ -381,98 +363,68 @@ func (a *ACM) PrettyPrint() {
a.Info.UUID.Field5[5])
if uuidStr == ACMUUIDV3 {
log.Println("\tUUID: ACM_UUID_V3")
fmt.Println(" UUID: ACM_UUID_V3")
}
switch a.Info.ChipsetACMType {
case ACMChipsetTypeBios:
log.Println("\tChipset ACM: BIOS")
fmt.Println(" Chipset ACM: BIOS")
break
case ACMChipsetTypeBiosRevoc:
log.Println("\tChipset ACM: BIOS Revocation")
fmt.Println(" Chipset ACM: BIOS Revocation")
break
case ACMChipsetTypeSinit:
log.Println("\tChipset ACM: SINIT")
fmt.Println(" Chipset ACM: SINIT")
break
case ACMChipsetTypeSinitRevoc:
log.Println("\tChipset ACM: SINIT Revocation")
fmt.Println(" Chipset ACM: SINIT Revocation")
break
default:
log.Println("\tChipset ACM: Unknown")
fmt.Println(" Chipset ACM: Unknown")
}
log.Printf("\tVersion: %d\n", a.Info.Version)
log.Printf("\tLength: 0x%x (%d)\n", a.Info.Length, a.Info.Length)
log.Printf("\tChipset ID List: 0x%02x\n", a.Info.ChipsetIDList)
log.Printf("\tOS SINIT Data Version: 0x%02x\n", a.Info.OSSinitDataVersion)
log.Printf("\tMin. MLE Header Version: 0x%08x\n", a.Info.MinMleHeaderVersion)
log.Printf("\tCapabilities: 0x%08x\n", a.Info.TxtCaps)
log.Printf("\tACM Version: %d\n", a.Info.ACMVersion)
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)
}
//PrettyPrint prints a human readable representation of the Chipsets
func (c *Chipsets) PrettyPrint() {
log.Println("Chipset List:")
log.Printf("\tEntries: %d\n", c.Count)
fmt.Println(" --Chipset List--")
fmt.Printf(" Entries: %d\n", c.Count)
for idx, chipset := range c.IDList {
log.Printf("\tEntry %d:\n", idx)
log.Printf("\t\tFlags: 0x%02x\n", chipset.Flags)
log.Printf("\t\tVendor: 0x%02x\n", chipset.VendorID)
log.Printf("\t\tDevice: 0x%02x\n", chipset.DeviceID)
log.Printf("\t\tRevision: 0x%02x\n", chipset.RevisionID)
fmt.Printf(" Entry %d:\n", idx)
fmt.Printf(" Flags: 0x%02x\n", chipset.Flags)
fmt.Printf(" Vendor: 0x%02x\n", chipset.VendorID)
fmt.Printf(" Device: 0x%02x\n", chipset.DeviceID)
fmt.Printf(" Revision: 0x%02x\n", chipset.RevisionID)
}
}
//PrettyPrint prints a human readable representation of the Processors
func (p *Processors) PrettyPrint() {
log.Println("Processor List:")
log.Printf("\tEntries: %d\n", p.Count)
fmt.Println(" --Processor List--")
fmt.Printf(" Entries: %d\n", p.Count)
for idx, processor := range p.IDList {
log.Printf("\tEntry %d:\n", idx)
log.Printf("\t\tFMS: 0x%02x\n", processor.FMS)
log.Printf("\t\tFMS Maks: 0x%02x\n", processor.FMSMask)
log.Printf("\t\tPlatform ID: 0x%02x\n", processor.PlatformID)
log.Printf("\t\tPlatform Mask: 0x%02x\n", processor.PlatformMask)
fmt.Printf(" Entry %d:\n", idx)
fmt.Printf(" FMS: 0x%02x\n", processor.FMS)
fmt.Printf(" FMS Maks: 0x%02x\n", processor.FMSMask)
fmt.Printf(" Platform ID: 0x%02x\n", processor.PlatformID)
fmt.Printf(" Platform Mask: 0x%02x\n", processor.PlatformMask)
}
}
//PrettyPrint prints a human readable representation of the TPMs
func (t *TPMs) PrettyPrint() {
log.Println("TPM Info List:")
log.Println("\tCapabilities:")
log.Printf("\t\tExternal Policy: %02x\n", t.Capabilities)
log.Printf("\tAlgorithms: %d\n", t.Count)
fmt.Println(" --TPM Info List--")
fmt.Println(" Capabilities:")
fmt.Printf(" External Policy: %02x\n", t.Capabilities)
fmt.Printf(" Algorithms: %d\n", t.Count)
for _, algo := range t.AlgID {
switch algo {
case TPMAlgoNULL:
log.Println("\t\tNULL")
break
case TPMAlgoSHA1:
log.Println("\t\tSHA-1")
break
case TPMAlgoSHA256:
log.Println("\t\tSHA-256")
break
case TPMAlgoSHA384:
log.Println("\t\tSHA-384")
break
case TPMAlgoSHA512:
log.Println("\t\tSHA-512")
break
case TPMAlgoSM3_256:
log.Println("\t\tSM3-256")
break
case TPMAlgoRSASSA:
log.Println("\t\tRSA-SSA")
break
case TPMAlgoECDSA:
log.Println("\t\tEC-DSA")
break
case TPMAlgoSM2:
log.Println("\t\tSM2")
break
default:
log.Println("\t\tUnknown")
}
fmt.Printf(" %v\n", algo.String())
}
}

View File

@ -54,7 +54,7 @@ func TestACMSize(t *testing.T) {
t.Errorf("Failed to read file: %v", err)
}
size, err := LookupSize(file)
size, err := LookupACMSize(file)
if err != nil {
t.Errorf("ACMSize() failed: %v", err)
}
@ -94,7 +94,7 @@ func TestACMSize2(t *testing.T) {
t.Errorf("ACMSize() failed: %v", err)
}
size, err := LookupSize(file)
size, err := LookupACMSize(file)
if err != nil {
t.Errorf("ACMSize() failed: %v", err)
}

View File

@ -3,15 +3,17 @@ package tools
import (
"encoding/json"
"fmt"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"io/ioutil"
"github.com/9elements/converged-security-suite/v2/pkg/hwapi"
"github.com/google/go-tpm/tpm2"
)
// Configuration input
type Configuration struct {
TPM hwapi.TPMVersion
TXTMode TXTMode
LCPHash LCPPol2Hash
LCPHash tpm2.Algorithm
}
// Configuration input
@ -48,15 +50,16 @@ func ParseConfig(filepath string) (*Configuration, error) {
return nil, fmt.Errorf("Couldn't parse TXT mode option: %s", jConfig.TXTMode)
}
if jConfig.LCP2Hash == "SHA1" {
config.LCPHash = LCPPol2HAlgSHA1
config.LCPHash = tpm2.AlgSHA1
} else if jConfig.LCP2Hash == "SHA256" {
config.LCPHash = LCPPol2HAlgSHA256
config.LCPHash = tpm2.AlgSHA256
} else if jConfig.LCP2Hash == "SHA384" {
config.LCPHash = LCPPol2HAlgSHA384
config.LCPHash = tpm2.AlgSHA384
} else if jConfig.LCP2Hash == "SM3" {
config.LCPHash = LCPPol2HAlgSM3
// SM3 is not implemented
//config.LCPHash = tpm2.AlgSM3
} else if jConfig.LCP2Hash == "NULL" {
config.LCPHash = LCPPol2HAlgNULL
config.LCPHash = tpm2.AlgNull
} else {
return nil, fmt.Errorf("Couldn't parse LCP hash option: %s", jConfig.LCP2Hash)
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
)
@ -57,15 +58,15 @@ func (fit *FitEntry) FancyPrint() {
log.Println("Fit Table PrintOut")
if fit.Address == type0MagicWord {
log.Println("FitEntry 0")
log.Printf("Fit Size: %v\n Entries", fit.Size())
log.Printf("Version: %v\n", fit.Version)
log.Printf("Checksum indicator: %b\n", fit.CVType)
log.Printf("Fit Size: 0x%x\n Entries", fit.Size())
log.Printf("Version: 0x%x\n", fit.Version)
log.Printf("Checksum indicator: 0x%x\n", fit.CVType)
} else {
log.Printf("Component Address: %v\n", fit.Address)
log.Printf("Component size: %v\n", fit.Size())
log.Printf("Version: %v\n", fit.Version)
log.Printf("C_V & Type: %b\n", fit.CVType)
log.Printf("Checksum: %v\n", fit.CheckSum)
log.Printf("Component Address: 0x%x\n", fit.Address)
log.Printf("Component size: 0x%x\n", fit.Size())
log.Printf("Version: 0x%x\n", fit.Version)
log.Printf("C_V & Type: 0x%x\n", fit.CVType)
log.Printf("Checksum: 0x%x\n", fit.CheckSum)
}
}
@ -93,22 +94,17 @@ func GetFitPointer(data []byte) (uint64, error) {
return uint64(fitPointer), nil
}
func readFit(data []byte, fitSize uint32) ([]FitEntry, error) {
func readFit(reader io.Reader, fitSize uint32, data []byte) ([]FitEntry, error) {
var ret []FitEntry
dummy := FitEntry{}
fit := bytes.NewReader(data)
err := binary.Read(fit, binary.LittleEndian, &dummy)
if err != nil {
return nil, err
}
for i := 16; i < int(fitSize); i += 16 {
ent := FitEntry{}
err := binary.Read(fit, binary.LittleEndian, &ent)
err := binary.Read(reader, binary.LittleEndian, &ent)
if err != nil {
return nil, err
}
if ent.Type() == UnusedEntry {
continue
}
// Intel's Firmware Interface Table Bios Specification
// recommends to clear CheckSumValid bit on all entries
if ent.CheckSumValid() {
@ -117,54 +113,42 @@ func readFit(data []byte, fitSize uint32) ([]FitEntry, error) {
for j := 0; j < 16; j++ {
cksum += data[j+i]
}
if cksum != 0 {
return nil, fmt.Errorf("FIT: Checksum of entry is invalid")
}
}
ret = append(ret, ent)
}
return ret, nil
}
//GetFitHeader extracts the fit header from raw data
func GetFitHeader(data []byte) (FitEntry, error) {
fit := bytes.NewReader(data)
func GetFitHeader(reader io.Reader) (FitEntry, error) {
// read FIT header
hdr := FitEntry{}
err := binary.Read(fit, binary.LittleEndian, &hdr)
if err != nil {
return hdr, err
var err error
for {
err = binary.Read(reader, binary.LittleEndian, &hdr)
if err != nil {
break
}
if hdr.Address == type0MagicWord && hdr.Type() == FitHeader && hdr.Size() != 0 {
return hdr, nil
}
}
if hdr.Address != type0MagicWord {
return hdr, fmt.Errorf("FIT: magic word wrong - See: Firmware Interface Table - BIOS Specification, Document: 338505-001, P.8")
}
if hdr.Type() != 0 {
return hdr, fmt.Errorf("FIT: first entry not of type 0 - See: Firmware Interface Table - BIOS Specification, Document: 338505-001, P.8")
}
if hdr.Size() == 0 {
return hdr, fmt.Errorf("FIT: Invalid size")
}
return hdr, nil
return FitEntry{}, err
}
// ExtractFit extracts all entries from the fit and checks the checksum
func ExtractFit(data []byte) ([]FitEntry, error) {
fit := bytes.NewReader(data)
// read FIT header
hdr, err := GetFitHeader(data)
hdr, err := GetFitHeader(fit)
if err != nil {
return nil, err
}
// read rest of the FIT
fitTable, err := readFit(data, hdr.Size())
fitTable, err := readFit(fit, hdr.Size(), data)
if err != nil {
return nil, err
}
@ -193,7 +177,6 @@ func ExtractFit(data []byte) ([]FitEntry, error) {
return nil, fmt.Errorf("FIT: Checksum of FIT is invalid")
}
}
var lasttype int
for i := range fitTable {
if fitTable[i].Type() == UnusedEntry {

Some files were not shown because too many files have changed in this diff Show More