Merge pull request #188 from 9elements/advanced-stitching
Implement km & bpm stitching of signatures
This commit is contained in:
commit
df5225eaea
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue