Skip to content

Commit

Permalink
Merge pull request #188 from 9elements/advanced-stitching
Browse files Browse the repository at this point in the history
Implement km & bpm stitching of signatures
  • Loading branch information
zaolin authored Mar 1, 2021
2 parents 8b29740 + 94413bb commit df5225e
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 26 deletions.
113 changes: 97 additions & 16 deletions cmd/bg-prov/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"`
}
17 changes: 17 additions & 0 deletions pkg/intel/metadata/manifest/key_signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -51,10 +52,26 @@ 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)
}

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)
}
49 changes: 43 additions & 6 deletions pkg/intel/metadata/manifest/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down
44 changes: 40 additions & 4 deletions pkg/intel/metadata/manifest/signature_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
25 changes: 25 additions & 0 deletions pkg/provisioning/bg/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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)
Expand Down
Loading

0 comments on commit df5225e

Please sign in to comment.