Merge pull request #188 from 9elements/advanced-stitching

Implement km & bpm stitching of signatures
This commit is contained in:
Zaolin 2021-03-01 17:10:21 +01:00 committed by GitHub
commit df5225eaea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 248 additions and 26 deletions

View File

@ -158,6 +158,20 @@ type readConfigCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
}
type stitchingKMCmd struct {
KM string `arg required name:"km" help:"Path to the Key Manifest binary file." type:"path"`
Signature string `arg required name:"signature" help:"Path to the Key Manifest signature file." type:"path"`
PubKey string `arg required name:"pubkey" help:"Path to the Key Manifest public key file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly stitched KM binary file." type:"path"`
}
type stitchingBPMCmd struct {
BPM string `arg required name:"bpm" help:"Path to the Boot Policy Manifest binary file." type:"path"`
Signature string `arg required name:"signature" help:"Path to the Boot Policy Manifest signature file." type:"path"`
PubKey string `arg required name:"pubkey" help:"Path to the Boot Policy Manifest public key file." type:"path"`
Out string `arg required name:"out" help:"Path to the newly stitched BPM binary file." type:"path"`
}
type stitchingCmd struct {
BIOS string `arg required name:"bios" help:"Path to the full BIOS binary file." type:"path"`
ACM string `arg required name:"acm" help:"Path to the ACM binary file." type:"path"`
@ -592,6 +606,68 @@ func (rc *readConfigCmd) Run(ctx *context) error {
return nil
}
func (s *stitchingKMCmd) Run(ctx *context) error {
kmData, err := ioutil.ReadFile(s.KM)
if err != nil {
return err
}
sig, err := ioutil.ReadFile(s.Signature)
if err != nil {
return err
}
pub, err := bg.ReadPubKey(s.PubKey)
if err != nil {
return err
}
if len(kmData) < 1 || len(sig) < 1 {
return fmt.Errorf("loaded files are empty")
}
reader := bytes.NewReader(kmData)
km, err := bg.ParseKM(reader)
if err != nil {
return err
}
kmRaw, err := bg.StitchKM(km, pub, sig)
if err != nil {
return err
}
if err := ioutil.WriteFile(s.Out, kmRaw, 0644); err != nil {
return err
}
return nil
}
func (s *stitchingBPMCmd) Run(ctx *context) error {
bpmData, err := ioutil.ReadFile(s.BPM)
if err != nil {
return err
}
sig, err := ioutil.ReadFile(s.Signature)
if err != nil {
return err
}
pub, err := bg.ReadPubKey(s.PubKey)
if err != nil {
return err
}
if len(bpmData) < 1 || len(sig) < 1 {
return fmt.Errorf("loaded files are empty")
}
reader := bytes.NewReader(bpmData)
bpm, err := bg.ParseBPM(reader)
if err != nil {
return err
}
bpmRaw, err := bg.StitchBPM(bpm, pub, sig)
if err != nil {
return err
}
if err := ioutil.WriteFile(s.Out, bpmRaw, 0644); err != nil {
return err
}
return nil
}
func (s *stitchingCmd) Run(ctx *context) error {
bpm, _ := ioutil.ReadFile(s.BPM)
km, _ := ioutil.ReadFile(s.KM)
@ -655,20 +731,25 @@ 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"`
KMShow kmPrintCmd `cmd help:"Prints Key Manifest binary in human-readable format"`
KMGen generateKMCmd `cmd help:"Generate KM file based von json configuration"`
KMSign signKMCmd `cmd help:"Sign key manifest with given key"`
KMStitch stitchingKMCmd `cmd help:"Stitches KM Signatue into unsigned KM"`
KMExport kmExportCmd `cmd help:"Exports KM structures from BIOS image into file"`
BPMShow bpmPrintCmd `cmd help:"Prints Boot Policy Manifest binary in human-readable format"`
BPMGen generateBPMCmd `cmd help:"Generate BPM file based von json configuration"`
BPMSign signBPMCmd `cmd help:"Sign Boot Policy Manifest with given key"`
BPMStitch stitchingBPMCmd `cmd help:"Stitches BPM Signatue into unsigned BPM"`
BPMExport bpmExportCmd `cmd help:"Exports BPM structures from BIOS image into file"`
ACMExport acmExportCmd `cmd help:"Exports ACM structures from BIOS image into file"`
ACMShow acmPrintCmd `cmd help:"Prints ACM binary in human-readable format"`
ShowAll biosPrintCmd `cmd help:"Prints BPM, KM, FIT and ACM from BIOS binary in human-readable format"`
Stitch stitchingCmd `cmd help:"Stitches BPM, KM and ACM into given BIOS image file"`
KeyGen keygenCmd `cmd help:"Generates key for KM and BPM signing"`
Template templateCmd `cmd help:"Writes template JSON configuration into file"`
ReadConfig readConfigCmd `cmd help:"Reads config from existing BIOS file and translates it to a JSON configuration"`
Version versionCmd `cmd help:"Prints the version of the program"`
}

View File

@ -37,6 +37,7 @@ func (m *KeySignature) Verify(signedData []byte) error {
// if signAlgo is zero then it is detected automatically, based on the type
// of the provided private key.
func (ks *KeySignature) SetSignature(signAlgo Algorithm, privKey crypto.Signer, signedData []byte) error {
ks.Version = 0x10
err := ks.Key.SetPubKey(privKey.Public())
if err != nil {
return fmt.Errorf("unable to set public key: %w", err)
@ -51,6 +52,7 @@ func (ks *KeySignature) SetSignature(signAlgo Algorithm, privKey crypto.Signer,
// 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 {
ks.Version = 0x10
err := ks.Key.SetPubKey(privKey.Public())
if err != nil {
return fmt.Errorf("unable to set public key: %w", err)
@ -58,3 +60,18 @@ func (ks *KeySignature) SetSignatureAuto(privKey crypto.Signer, signedData []byt
return ks.SetSignature(0, privKey, signedData)
}
// FillSignature sets a signature and all the values of KeyManifest,
// accordingly to arguments signAlgo, pubKey and signedData.
//
// if signAlgo is zero then it is detected automatically, based on the type
// of the provided private key.
func (ks *KeySignature) FillSignature(signAlgo Algorithm, pubKey crypto.PublicKey, signedData []byte, hashAlgo Algorithm) error {
ks.Version = 0x10
err := ks.Key.SetPubKey(pubKey)
if err != nil {
return fmt.Errorf("unable to set public key: %w", err)
}
return ks.Signature.FillSignature(signAlgo, pubKey, signedData, hashAlgo)
}

View File

@ -66,7 +66,7 @@ func (m Signature) SignatureData() (SignatureDataInterface, error) {
// * SignatureRSAASA
// * SignatureECDSA
// * SignatureSM2
func (m *Signature) SetSignatureByData(sig SignatureDataInterface) error {
func (m *Signature) SetSignatureByData(sig SignatureDataInterface, hashAlgo Algorithm) error {
err := m.SetSignatureData(sig)
if err != nil {
return err
@ -75,19 +75,35 @@ func (m *Signature) SetSignatureByData(sig SignatureDataInterface) error {
switch sig := sig.(type) {
case SignatureRSAPSS:
m.SigScheme = AlgRSAPSS
m.HashAlg = AlgSHA256
if hashAlgo.IsNull() {
m.HashAlg = AlgSHA256
} else {
m.HashAlg = hashAlgo
}
m.KeySize.SetInBytes(uint16(len(m.Data)))
case SignatureRSAASA:
m.SigScheme = AlgRSASSA
m.HashAlg = AlgSHA256
if hashAlgo.IsNull() {
m.HashAlg = AlgSHA256
} else {
m.HashAlg = hashAlgo
}
m.KeySize.SetInBytes(uint16(len(m.Data)))
case SignatureECDSA:
m.SigScheme = AlgECDSA
m.HashAlg = AlgSHA256
if hashAlgo.IsNull() {
m.HashAlg = AlgSHA512
} else {
m.HashAlg = hashAlgo
}
m.KeySize.SetInBits(uint16(sig.R.BitLen()))
case SignatureSM2:
m.SigScheme = AlgSM2
m.HashAlg = AlgSM3_256
if hashAlgo.IsNull() {
m.HashAlg = AlgSM3_256
} else {
m.HashAlg = hashAlgo
}
m.KeySize.SetInBits(uint16(sig.R.BitLen()))
default:
return fmt.Errorf("unexpected signature type: %T", sig)
@ -138,12 +154,33 @@ func (m *Signature) SetSignatureData(sig SignatureDataInterface) error {
// if signAlgo is zero then it is detected automatically, based on the type
// of the provided private key.
func (m *Signature) SetSignature(signAlgo Algorithm, privKey crypto.Signer, signedData []byte) error {
m.Version = 0x10
signData, err := NewSignatureData(signAlgo, privKey, signedData)
if err != nil {
return fmt.Errorf("unable to construct the signature data: %w", err)
}
err = m.SetSignatureByData(signData)
err = m.SetSignatureByData(signData, AlgNull)
if err != nil {
return fmt.Errorf("unable to set the signature: %w", err)
}
return nil
}
// FillSignature sets the signature accordingly to arguments signAlgo,
// pubKey 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) FillSignature(signAlgo Algorithm, pubKey crypto.PublicKey, signedData []byte, hashAlgo Algorithm) error {
m.Version = 0x10
signData, err := NewSignatureByData(signAlgo, pubKey, signedData)
if err != nil {
return fmt.Errorf("unable to construct the signature data: %w", err)
}
err = m.SetSignatureByData(signData, hashAlgo)
if err != nil {
return fmt.Errorf("unable to set the signature: %w", err)
}

View File

@ -53,7 +53,6 @@ func NewSignatureData(
return nil, fmt.Errorf("unable to sign with RSAPSS the data: %w", err)
}
return SignatureRSAPSS(data), nil
case AlgRSASSA:
rsaPrivateKey, ok := privKey.(*rsa.PrivateKey)
if !ok {
@ -67,7 +66,6 @@ func NewSignatureData(
return nil, fmt.Errorf("unable to sign with RSASSA the data: %w", err)
}
return SignatureRSAASA(data), nil
case AlgECDSA:
eccPrivateKey, ok := privKey.(*ecdsa.PrivateKey)
if !ok {
@ -80,7 +78,6 @@ func NewSignatureData(
return nil, fmt.Errorf("unable to sign with ECDSA the data: %w", err)
}
return data, nil
case AlgSM2:
eccPrivateKey, ok := privKey.(*sm2.PrivateKey)
if !ok {
@ -98,6 +95,46 @@ func NewSignatureData(
return nil, fmt.Errorf("signing algorithm '%s' is not implemented in this library", signAlgo)
}
// NewSignatureByData returns an implementation of SignatureDataInterface,
// accordingly to signAlgo, publicKey and signedData.
//
// if signAlgo is zero then it is detected automatically, based on the type
// of the provided private key.
func NewSignatureByData(
signAlgo Algorithm,
pubKey crypto.PublicKey,
signedData []byte,
) (SignatureDataInterface, error) {
if signAlgo == 0 {
// auto-detect the sign algorithm, based on the provided signing key
switch pubKey.(type) {
case *rsa.PublicKey:
signAlgo = AlgRSASSA
case *ecdsa.PublicKey:
signAlgo = AlgECDSA
case *sm2.PublicKey:
signAlgo = AlgSM2
}
}
switch signAlgo {
case AlgRSAPSS:
return SignatureRSAPSS(signedData), nil
case AlgRSASSA:
return SignatureRSAASA(signedData), nil
case AlgECDSA:
return SignatureECDSA{
R: new(big.Int).SetBytes(reverseBytes(signedData[:len(signedData)/2])),
S: new(big.Int).SetBytes(reverseBytes(signedData[len(signedData)/2:])),
}, nil
case AlgSM2:
return SignatureSM2{
R: new(big.Int).SetBytes(reverseBytes(signedData[:len(signedData)/2])),
S: new(big.Int).SetBytes(reverseBytes(signedData[len(signedData)/2:])),
}, 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
@ -167,7 +204,6 @@ func (s SignatureRSAASA) Verify(pkIface crypto.PublicKey, signedData []byte) err
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
}

View File

@ -7,6 +7,7 @@ import (
"encoding/pem"
"fmt"
"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"
)
@ -25,6 +26,30 @@ func WriteBPM(bpm *bootpolicy.Manifest) ([]byte, error) {
return buf.Bytes(), err
}
// StitchKM returns a key manifest manifest as byte slice
func StitchKM(km *key.Manifest, pubKey crypto.PublicKey, signature []byte) ([]byte, error) {
if err := km.KeyAndSignature.FillSignature(0, pubKey, signature, km.PubKeyHashAlg); err != nil {
return nil, err
}
km.RehashRecursive()
if err := km.Validate(); err != nil {
return nil, err
}
return WriteKM(km)
}
// StitchBPM returns a boot policy manifest as byte slice
func StitchBPM(bpm *bootpolicy.Manifest, pubKey crypto.PublicKey, signature []byte) ([]byte, error) {
if err := bpm.PMSE.KeySignature.FillSignature(0, pubKey, signature, manifest.AlgNull); err != nil {
return nil, err
}
bpm.RehashRecursive()
if err := bpm.Validate(); err != nil {
return nil, err
}
return WriteBPM(bpm)
}
func parsePrivateKey(raw []byte) (crypto.Signer, error) {
for {
block, rest := pem.Decode(raw)

View File

@ -2,6 +2,7 @@ package bg
import (
"errors"
"fmt"
"io"
"github.com/9elements/converged-security-suite/v2/pkg/intel/metadata/manifest/bootpolicy"
@ -18,6 +19,17 @@ func ParseBPM(reader io.Reader) (*bootpolicy.Manifest, error) {
return bpm, nil
}
// ValidateBPM reads from a binary, parses into the boot policy manifest structure
// and validates the structure
func ValidateBPM(reader io.Reader) error {
bpm := &bootpolicy.Manifest{}
_, err := bpm.ReadFrom(reader)
if err != nil && !errors.Is(err, io.EOF) {
return err
}
return bpm.Validate()
}
// ParseKM reads from a binary source and parses into the key manifest structure
func ParseKM(reader io.Reader) (*key.Manifest, error) {
km := &key.Manifest{}
@ -27,3 +39,17 @@ func ParseKM(reader io.Reader) (*key.Manifest, error) {
}
return km, nil
}
// ValidateKM reads from a binary source, parses into the key manifest structure
// and validates the structure
func ValidateKM(reader io.Reader) error {
km := &key.Manifest{}
_, err := km.ReadFrom(reader)
if err != nil && !errors.Is(err, io.EOF) {
return err
}
if km.PubKeyHashAlg != km.KeyAndSignature.Signature.HashAlg {
return fmt.Errorf("header pubkey hash algorithm doesn't match signature hash")
}
return km.Validate()
}