...

Source file src/github.com/secure-systems-lab/go-securesystemslib/signerverifier/utils.go

Documentation: github.com/secure-systems-lab/go-securesystemslib/signerverifier

     1  package signerverifier
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"crypto/x509"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"encoding/pem"
     9  	"errors"
    10  	"hash"
    11  	"testing"
    12  
    13  	"github.com/secure-systems-lab/go-securesystemslib/cjson"
    14  )
    15  
    16  /*
    17  Credits: Parts of this file were originally authored for in-toto-golang.
    18  */
    19  
    20  var (
    21  	// ErrNoPEMBlock gets triggered when there is no PEM block in the provided file
    22  	ErrNoPEMBlock = errors.New("failed to decode the data as PEM block (are you sure this is a pem file?)")
    23  	// ErrFailedPEMParsing gets returned when PKCS1, PKCS8 or PKIX key parsing fails
    24  	ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type")
    25  )
    26  
    27  // LoadKeyFromSSLibBytes returns a pointer to a Key instance created from the
    28  // contents of the bytes. The key contents are expected to be in the custom
    29  // securesystemslib format.
    30  func LoadKeyFromSSLibBytes(contents []byte) (*SSLibKey, error) {
    31  	var key *SSLibKey
    32  	if err := json.Unmarshal(contents, &key); err != nil {
    33  		return LoadRSAPSSKeyFromBytes(contents)
    34  	}
    35  	if len(key.KeyID) == 0 {
    36  		keyID, err := calculateKeyID(key)
    37  		if err != nil {
    38  			return nil, err
    39  		}
    40  		key.KeyID = keyID
    41  	}
    42  
    43  	return key, nil
    44  }
    45  
    46  func calculateKeyID(k *SSLibKey) (string, error) {
    47  	key := map[string]any{
    48  		"keytype":               k.KeyType,
    49  		"scheme":                k.Scheme,
    50  		"keyid_hash_algorithms": k.KeyIDHashAlgorithms,
    51  		"keyval": map[string]string{
    52  			"public": k.KeyVal.Public,
    53  		},
    54  	}
    55  	canonical, err := cjson.EncodeCanonical(key)
    56  	if err != nil {
    57  		return "", err
    58  	}
    59  	digest := sha256.Sum256(canonical)
    60  	return hex.EncodeToString(digest[:]), nil
    61  }
    62  
    63  /*
    64  generatePEMBlock creates a PEM block from scratch via the keyBytes and the pemType.
    65  If successful it returns a PEM block as []byte slice. This function should always
    66  succeed, if keyBytes is empty the PEM block will have an empty byte block.
    67  Therefore only header and footer will exist.
    68  */
    69  func generatePEMBlock(keyBytes []byte, pemType string) []byte {
    70  	// construct PEM block
    71  	pemBlock := &pem.Block{
    72  		Type:    pemType,
    73  		Headers: nil,
    74  		Bytes:   keyBytes,
    75  	}
    76  	return pem.EncodeToMemory(pemBlock)
    77  }
    78  
    79  /*
    80  decodeAndParsePEM receives potential PEM bytes decodes them via pem.Decode
    81  and pushes them to parseKey. If any error occurs during this process,
    82  the function will return nil and an error (either ErrFailedPEMParsing
    83  or ErrNoPEMBlock). On success it will return the decoded pemData, the
    84  key object interface and nil as error. We need the decoded pemData,
    85  because LoadKey relies on decoded pemData for operating system
    86  interoperability.
    87  */
    88  func decodeAndParsePEM(pemBytes []byte) (*pem.Block, any, error) {
    89  	// pem.Decode returns the parsed pem block and a rest.
    90  	// The rest is everything, that could not be parsed as PEM block.
    91  	// Therefore we can drop this via using the blank identifier "_"
    92  	data, _ := pem.Decode(pemBytes)
    93  	if data == nil {
    94  		return nil, nil, ErrNoPEMBlock
    95  	}
    96  
    97  	// Try to load private key, if this fails try to load
    98  	// key as public key
    99  	key, err := parsePEMKey(data.Bytes)
   100  	if err != nil {
   101  		return nil, nil, err
   102  	}
   103  	return data, key, nil
   104  }
   105  
   106  /*
   107  parseKey tries to parse a PEM []byte slice. Using the following standards
   108  in the given order:
   109  
   110    - PKCS8
   111    - PKCS1
   112    - PKIX
   113  
   114  On success it returns the parsed key and nil.
   115  On failure it returns nil and the error ErrFailedPEMParsing
   116  */
   117  func parsePEMKey(data []byte) (any, error) {
   118  	key, err := x509.ParsePKCS8PrivateKey(data)
   119  	if err == nil {
   120  		return key, nil
   121  	}
   122  	key, err = x509.ParsePKCS1PrivateKey(data)
   123  	if err == nil {
   124  		return key, nil
   125  	}
   126  	key, err = x509.ParsePKIXPublicKey(data)
   127  	if err == nil {
   128  		return key, nil
   129  	}
   130  	key, err = x509.ParseECPrivateKey(data)
   131  	if err == nil {
   132  		return key, nil
   133  	}
   134  	return nil, ErrFailedPEMParsing
   135  }
   136  
   137  func hashBeforeSigning(data []byte, h hash.Hash) []byte {
   138  	h.Write(data)
   139  	return h.Sum(nil)
   140  }
   141  
   142  func hexDecode(t *testing.T, data string) []byte {
   143  	t.Helper()
   144  	b, err := hex.DecodeString(data)
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  	return b
   149  }
   150  

View as plain text