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:
parent
2faa705940
commit
aca443109c
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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 Manufacturer’s BPM revision number.
|
||||
--svn=UINT-8 Boot Policy Manifest Security Version Number
|
||||
--id=UINT-8 The key Manifest Identifier
|
||||
--pkhashalg=UINT-16 Hash algorithm of OEM public key digest
|
||||
--bpmpubkey=STRING Path to bpm public signing key
|
||||
--bpmhashalgo=ALGORITHM Hash algorithm for bpm public signing key
|
||||
--out=STRING Path to write applied config to
|
||||
--cut Cuts the signature before writing to binary (Facebook requirement)
|
||||
```
|
||||
|
||||
```bash
|
||||
./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 Manufacturer’s BPM revision number.
|
||||
--svn Boot Policy Manifest Security Version Number
|
||||
--acmsvn Authorized ACM Security Version Number
|
||||
--nems Size of data region need by IBB expressed in 4K pages.
|
||||
E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero
|
||||
--pbet Protect BIOS Environment Timer (PBET) value.
|
||||
--ibbflags IBB Control flags
|
||||
--mchbar MCHBAR address
|
||||
--vdtbar VTDPVC0BAR address
|
||||
--dmabase0 Low DMA protected range base
|
||||
--dmasize0 Low DMA protected range limit
|
||||
--dmabase1 High DMA protected range base.
|
||||
--dmasize1 High DMA protected range limit.
|
||||
--entrypoint IBB (Startup BIOS) entry point
|
||||
--sintmin OEM authorized SinitMinSvn value
|
||||
--txtflags TXT Element control flags
|
||||
--powerdowninterval Duration of Power Down in 5 sec increments
|
||||
--acpibaseoffset ACPI IO offset.
|
||||
--powermbaseoffset ACPI MMIO offset.
|
||||
--cmosoff0 CMOS byte in bank 0 to store platform wakeup time
|
||||
--cmosoff1 Second CMOS byte in bank 0 to store platform wakeup time
|
||||
|
||||
--out Path to write applied config to
|
||||
```
|
||||
|
||||
```bash
|
||||
./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 Manufacturer’s BPM revision number.
|
||||
--svn Boot Policy Manifest Security Version Number
|
||||
--acmsvn Authorized ACM Security Version Number
|
||||
--nems Size of data region need by IBB expressed in 4K pages.
|
||||
E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero
|
||||
--pbet Protect BIOS Environment Timer (PBET) value.
|
||||
--ibbflags IBB Control flags
|
||||
--mchbar MCHBAR address
|
||||
--vdtbar VTDPVC0BAR address
|
||||
--dmabase0 Low DMA protected range base
|
||||
--dmasize0 Low DMA protected range limit
|
||||
--dmabase1 High DMA protected range base.
|
||||
--dmasize1 High DMA protected range limit.
|
||||
--entrypoint IBB (Startup BIOS) entry point
|
||||
--sintmin OEM authorized SinitMinSvn value
|
||||
--txtflags TXT Element control flags
|
||||
--powerdowninterval Duration of Power Down in 5 sec increments
|
||||
--acpibaseoffset ACPI IO offset.
|
||||
--powermbaseoffset ACPI MMIO offset.
|
||||
--cmosoff0 CMOS byte in bank 0 to store platform wakeup time
|
||||
--cmosoff1 Second CMOS byte in bank 0 to store platform wakeup time
|
||||
```
|
||||
|
||||
Workflows
|
||||
==========
|
||||
|
||||
I. Boot Policy / Key Manifest Generation/Signing/Stitching
|
||||
-------------------------------
|
||||
|
||||
1. Create a template config file
|
||||
```bash
|
||||
./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
|
||||
```
|
|
@ -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 Manufacturer’s BPM revision number."`
|
||||
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
|
||||
ACMSVN manifest.SVN `flag optional name:"acmsvn" help:"Authorized ACM Security Version Number"`
|
||||
NEMS bootpolicy.Size4K `flag optional name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"`
|
||||
// IBB args
|
||||
PBET bootpolicy.PBETValue `flag optional name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."`
|
||||
IBBSegFlags bootpolicy.SEFlags `flag optional name:"ibbflags" help:"IBB Control flags"`
|
||||
MCHBAR uint64 `flag optional name:"mchbar" help:"MCHBAR address"`
|
||||
VDTBAR uint64 `flag optional name:"vdtbar" help:"VTDPVC0BAR address"`
|
||||
DMABase0 uint32 `flag optional name:"dmabase0" help:"Low DMA protected range base"`
|
||||
DMASize0 uint32 `flag optional name:"dmasize0" help:"Low DMA protected range limit"`
|
||||
DMABase1 uint64 `flag optional name:"dmabase1" help:"High DMA protected range base."`
|
||||
DMASize1 uint64 `flag optional name:"dmasize1" help:"High DMA protected range limit."`
|
||||
EntryPoint uint32 `flag optional name:"entrypoint" help:"IBB (Startup BIOS) entry point"`
|
||||
IbbHash []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 Manufacturer’s BPM revision number."`
|
||||
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
|
||||
ID uint8 `flag optional name:"id" help:"The key Manifest Identifier"`
|
||||
PKHashAlg 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 Manufacturer’s BPM revision number."`
|
||||
SVN manifest.SVN `flag optional name:"svn" help:"Boot Policy Manifest Security Version Number"`
|
||||
ACMSVN manifest.SVN `flag optional name:"acmsvn" help:"Authorized ACM Security Version Number"`
|
||||
NEMS bootpolicy.Size4K `flag optional name:"nems" help:"Size of data region need by IBB expressed in 4K pages. E.g., value of 1 = 4096 bytes; 2 = 8092 bytes, etc. Must not be zero"`
|
||||
// IBB args
|
||||
PBET bootpolicy.PBETValue `flag optional name:"pbet" help:"Protect BIOS Environment Timer (PBET) value."`
|
||||
IBBSegFlags bootpolicy.SEFlags `flag optional name:"ibbflags" help:"IBB Control flags"`
|
||||
MCHBAR uint64 `flag optional name:"mchbar" help:"MCHBAR address"`
|
||||
VDTBAR uint64 `flag optional name:"vdtbar" help:"VTDPVC0BAR address"`
|
||||
DMABase0 uint32 `flag optional name:"dmabase0" help:"Low DMA protected range base"`
|
||||
DMASize0 uint32 `flag optional name:"dmasize0" help:"Low DMA protected range limit"`
|
||||
DMABase1 uint64 `flag optional name:"dmabase1" help:"High DMA protected range base."`
|
||||
DMASize1 uint64 `flag optional name:"dmasize1" help:"High DMA protected range limit."`
|
||||
EntryPoint uint32 `flag optional name:"entrypoint" help:"IBB (Startup BIOS) entry point"`
|
||||
IbbHash []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"`
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
22
go.mod
|
@ -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
158
go.sum
|
@ -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=
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ./
|
||||
```
|
|
@ -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)
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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"`
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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"`
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
@ -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"`
|
||||
}
|
|
@ -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.
|
@ -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())
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 }}
|
||||
`
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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()))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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.
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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) {
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package bg
|
||||
|
||||
const (
|
||||
keySignatureElementMaxSize = 3072 // how this value was calculated?
|
||||
defaultStackAndDataSize = 4096
|
||||
defaultLLCSize = 0x800000
|
||||
additionalNEMSize = 0x80000
|
||||
)
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package bg
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN -----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuA0rrZM4ZWo+JEKy1Dxn
|
||||
KERb9MijZYUezT5bFH3xF5aVbLk7/WXSzkmDIrgxrdvx+JG2OTa5r8qquxqesxTD
|
||||
o2rwTt0eC0EgvoLHVwPJBRm53Ik9vEHO7X0fgeK4hrml9XpCpllhWvMK7re+CyCm
|
||||
+8kxQajxnELV2rZhXFdiPvJEnp3bwYmbiHzsOcrLTveRPwAJDdJZKBLL2MZ1ONJa
|
||||
zZ5/YOcRLpVpF8zxvUWN/vFKxYp7ClgkaZPQpIGuMgGyCbuAed31ca46ynoIskQ/
|
||||
uo7WPfG3O+ocQ5pHNoXtjRushTvDOQ0JqkLTkeF79I6tL59cspjCvtaCeLgay4LV
|
||||
rQIDAQAB
|
||||
-----END -----
|
Binary file not shown.
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN -----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtWYUau8w1RXhoZUPeIOp
|
||||
7t/ZnjHWMZlt1U7730l6nFAU56PBz0xpYhcLpPw+p1+n64fdMFPvM8M8IcIWuUNm
|
||||
FWbKAvl7M0Pl/UNfLKGeHN65VhVNysWnVTRvmyFb1cAmAZqjqkbE9pkK5AzDDiHE
|
||||
z7VgTiPTRmiKg4Udm0ZZdz9rBU9+FOLlOoALUjsaMQDsEuhAaP+O3CtmtRpSFemP
|
||||
WzEVJB/HYUh5sZxcDVUKIkkJhAbIreekJpCexp2KsW/EDnPC4LkVVJE7lzwue0xa
|
||||
J6jCaB+fQdXWF9UOimdz5mjcqToub1jp4iG2VUS0YOENLcC3e2yWSSt1EwZiZPei
|
||||
HwIDAQAB
|
||||
-----END -----
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package provisioning
|
||||
package txt
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package provisioning
|
||||
package txt
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package provisioning
|
||||
package txt
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -1,4 +1,4 @@
|
|||
package provisioning
|
||||
package txt
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package provisioning
|
||||
package txt
|
||||
|
||||
import (
|
||||
"crypto"
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
172
pkg/tools/acm.go
172
pkg/tools/acm.go
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue