github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/stboot/signature.go (about)

     1  // Copyright 2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package stboot
     6  
     7  import (
     8  	"crypto"
     9  	"crypto/rand"
    10  	"crypto/rsa"
    11  	"crypto/sha512"
    12  	"crypto/x509"
    13  	"encoding/pem"
    14  	"errors"
    15  	"fmt"
    16  	"io/ioutil"
    17  )
    18  
    19  type Signature struct {
    20  	Bytes []byte
    21  	Cert  *x509.Certificate
    22  }
    23  
    24  // Signer is used by BootBall to hash, sign and varify the BootConfigs
    25  // with appropriate algorithms
    26  type Signer interface {
    27  	Hash(files ...string) ([]byte, error)
    28  	Sign(privKey string, data []byte) ([]byte, error)
    29  	Verify(sig Signature, hash []byte) error
    30  }
    31  
    32  // AlwaysValidSigner creates signatures that are always valid.
    33  type AlwaysValidSigner struct{}
    34  
    35  // Hash hashes the the provided files. I case of AlwaysValidSigner
    36  // just 8 random bytes are returned.
    37  func (AlwaysValidSigner) Hash(files ...string) ([]byte, error) {
    38  	hash := make([]byte, 8)
    39  	_, err := rand.Read(hash)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	return hash, nil
    44  }
    45  
    46  // Sign signes the provided data with privKey. In case of AlwaysValidSigner
    47  // just 8 random bytes are returned
    48  func (AlwaysValidSigner) Sign(privKey string, data []byte) ([]byte, error) {
    49  	sig := make([]byte, 8)
    50  	_, err := rand.Read(sig)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	return sig, nil
    55  }
    56  
    57  // Verify checks if sig contains a valid signature of hash. In case of
    58  // AlwaysValidSigner this is allwazs the case.
    59  func (AlwaysValidSigner) Verify(sig Signature, hash []byte) error {
    60  	return nil
    61  }
    62  
    63  // Sha512PssSigner uses SHA512 hashes ans PSS signatures along with
    64  // x509 certificates.
    65  type Sha512PssSigner struct{}
    66  
    67  // Hash hashes the the provided files. In case of Sha512PssSigner
    68  // it is a SHA512 hash.
    69  func (Sha512PssSigner) Hash(files ...string) ([]byte, error) {
    70  	h := sha512.New()
    71  	for _, file := range files {
    72  		buf, err := ioutil.ReadFile(file)
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  		_, err = h.Write(buf)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  	}
    81  	return h.Sum(nil), nil
    82  }
    83  
    84  // Sign signes the provided data with privKey. In case of Sha512PssSigner
    85  // it is a PSS signature.
    86  func (Sha512PssSigner) Sign(privKey string, data []byte) ([]byte, error) {
    87  	buf, err := ioutil.ReadFile(privKey)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	privPem, _ := pem.Decode(buf)
    93  	key, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	if key == nil {
    98  		err = fmt.Errorf("key is empty")
    99  		return nil, err
   100  	}
   101  
   102  	opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
   103  
   104  	sig, err := rsa.SignPSS(rand.Reader, key, crypto.SHA512, data, opts)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	if sig == nil {
   109  		return nil, fmt.Errorf("signature is nil")
   110  	}
   111  	return sig, nil
   112  }
   113  
   114  // Verify checks if sig contains a valid signature of hash.
   115  func (Sha512PssSigner) Verify(sig Signature, hash []byte) error {
   116  	opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
   117  	err := rsa.VerifyPSS(sig.Cert.PublicKey.(*rsa.PublicKey), crypto.SHA512, hash, sig.Bytes, opts)
   118  	if err != nil {
   119  		return fmt.Errorf("signature verification failed: %v", err)
   120  	}
   121  	return nil
   122  }
   123  
   124  // parseCertificate parses certificate from raw certificate.
   125  func parseCertificate(raw []byte) (*x509.Certificate, error) {
   126  	block, _ := pem.Decode(raw)
   127  	return x509.ParseCertificate(block.Bytes)
   128  }
   129  
   130  // certPool returns a x509 certificate pool from raw certificate.
   131  func certPool(pem []byte) (*x509.CertPool, error) {
   132  	certPool := x509.NewCertPool()
   133  	ok := certPool.AppendCertsFromPEM(pem)
   134  	if !ok {
   135  		return nil, errors.New("Failed to parse root certificate")
   136  	}
   137  	return certPool, nil
   138  }
   139  
   140  // validateCertificate validates cert against certPool. If cert is not signed
   141  // by a certificate of certPool an error is returned.
   142  func validateCertificate(cert *x509.Certificate, rootCertPEM []byte) error {
   143  	certPool, err := certPool(rootCertPEM)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	opts := x509.VerifyOptions{
   148  		Roots: certPool,
   149  	}
   150  	_, err = cert.Verify(opts)
   151  	return err
   152  }