github.com/in-toto/in-toto-golang@v0.9.1-0.20240517212500-990269f763cf/in_toto/keylib.go (about)

     1  package in_toto
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/ed25519"
     6  	"crypto/rsa"
     7  	"crypto/sha256"
     8  	"crypto/x509"
     9  	"encoding/hex"
    10  	"encoding/pem"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"os"
    15  	"strings"
    16  
    17  	"github.com/secure-systems-lab/go-securesystemslib/cjson"
    18  )
    19  
    20  // ErrFailedPEMParsing gets returned when PKCS1, PKCS8 or PKIX key parsing fails
    21  var ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type")
    22  
    23  // ErrNoPEMBlock gets triggered when there is no PEM block in the provided file
    24  var ErrNoPEMBlock = errors.New("failed to decode the data as PEM block (are you sure this is a pem file?)")
    25  
    26  // ErrUnsupportedKeyType is returned when we are dealing with a key type different to ed25519 or RSA
    27  var ErrUnsupportedKeyType = errors.New("unsupported key type")
    28  
    29  // ErrInvalidSignature is returned when the signature is invalid
    30  var ErrInvalidSignature = errors.New("invalid signature")
    31  
    32  // ErrInvalidKey is returned when a given key is none of RSA, ECDSA or ED25519
    33  var ErrInvalidKey = errors.New("invalid key")
    34  
    35  const (
    36  	rsaKeyType            string = "rsa"
    37  	ecdsaKeyType          string = "ecdsa"
    38  	ed25519KeyType        string = "ed25519"
    39  	rsassapsssha256Scheme string = "rsassa-pss-sha256"
    40  	ecdsaSha2nistp224     string = "ecdsa-sha2-nistp224"
    41  	ecdsaSha2nistp256     string = "ecdsa-sha2-nistp256"
    42  	ecdsaSha2nistp384     string = "ecdsa-sha2-nistp384"
    43  	ecdsaSha2nistp521     string = "ecdsa-sha2-nistp521"
    44  	ed25519Scheme         string = "ed25519"
    45  	pemPublicKey          string = "PUBLIC KEY"
    46  	pemPrivateKey         string = "PRIVATE KEY"
    47  	pemRSAPrivateKey      string = "RSA PRIVATE KEY"
    48  )
    49  
    50  /*
    51  getSupportedKeyIDHashAlgorithms returns a string slice of supported
    52  KeyIDHashAlgorithms. We need to use this function instead of a constant,
    53  because Go does not support global constant slices.
    54  */
    55  func getSupportedKeyIDHashAlgorithms() Set {
    56  	return NewSet("sha256", "sha512")
    57  }
    58  
    59  /*
    60  getSupportedRSASchemes returns a string slice of supported RSA Key schemes.
    61  We need to use this function instead of a constant because Go does not support
    62  global constant slices.
    63  */
    64  func getSupportedRSASchemes() []string {
    65  	return []string{rsassapsssha256Scheme}
    66  }
    67  
    68  /*
    69  getSupportedEcdsaSchemes returns a string slice of supported ecdsa Key schemes.
    70  We need to use this function instead of a constant because Go does not support
    71  global constant slices.
    72  */
    73  func getSupportedEcdsaSchemes() []string {
    74  	return []string{ecdsaSha2nistp224, ecdsaSha2nistp256, ecdsaSha2nistp384, ecdsaSha2nistp521}
    75  }
    76  
    77  /*
    78  getSupportedEd25519Schemes returns a string slice of supported ed25519 Key
    79  schemes. We need to use this function instead of a constant because Go does
    80  not support global constant slices.
    81  */
    82  func getSupportedEd25519Schemes() []string {
    83  	return []string{ed25519Scheme}
    84  }
    85  
    86  /*
    87  generateKeyID creates a partial key map and generates the key ID
    88  based on the created partial key map via the SHA256 method.
    89  The resulting keyID will be directly saved in the corresponding key object.
    90  On success generateKeyID will return nil, in case of errors while encoding
    91  there will be an error.
    92  */
    93  func (k *Key) generateKeyID() error {
    94  	// Create partial key map used to create the keyid
    95  	// Unfortunately, we can't use the Key object because this also carries
    96  	// yet unwanted fields, such as KeyID and KeyVal.Private and therefore
    97  	// produces a different hash. We generate the keyID exactly as we do in
    98  	// the securesystemslib  to keep interoperability between other in-toto
    99  	// implementations.
   100  	var keyToBeHashed = map[string]interface{}{
   101  		"keytype":               k.KeyType,
   102  		"scheme":                k.Scheme,
   103  		"keyid_hash_algorithms": k.KeyIDHashAlgorithms,
   104  		"keyval": map[string]string{
   105  			"public": k.KeyVal.Public,
   106  		},
   107  	}
   108  	keyCanonical, err := cjson.EncodeCanonical(keyToBeHashed)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	// calculate sha256 and return string representation of keyID
   113  	keyHashed := sha256.Sum256(keyCanonical)
   114  	k.KeyID = fmt.Sprintf("%x", keyHashed)
   115  	err = validateKey(*k)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	return nil
   120  }
   121  
   122  /*
   123  generatePEMBlock creates a PEM block from scratch via the keyBytes and the pemType.
   124  If successful it returns a PEM block as []byte slice. This function should always
   125  succeed, if keyBytes is empty the PEM block will have an empty byte block.
   126  Therefore only header and footer will exist.
   127  */
   128  func generatePEMBlock(keyBytes []byte, pemType string) []byte {
   129  	// construct PEM block
   130  	pemBlock := &pem.Block{
   131  		Type:    pemType,
   132  		Headers: nil,
   133  		Bytes:   keyBytes,
   134  	}
   135  	return pem.EncodeToMemory(pemBlock)
   136  }
   137  
   138  /*
   139  setKeyComponents sets all components in our key object.
   140  Furthermore it makes sure to remove any trailing and leading whitespaces or newlines.
   141  We treat key types differently for interoperability reasons to the in-toto python
   142  implementation and the securesystemslib.
   143  */
   144  func (k *Key) setKeyComponents(pubKeyBytes []byte, privateKeyBytes []byte, keyType string, scheme string, KeyIDHashAlgorithms []string) error {
   145  	// assume we have a privateKey if the key size is bigger than 0
   146  
   147  	switch keyType {
   148  	case rsaKeyType:
   149  		if len(privateKeyBytes) > 0 {
   150  			k.KeyVal = KeyVal{
   151  				Private: strings.TrimSpace(string(generatePEMBlock(privateKeyBytes, pemRSAPrivateKey))),
   152  				Public:  strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))),
   153  			}
   154  		} else {
   155  			k.KeyVal = KeyVal{
   156  				Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))),
   157  			}
   158  		}
   159  	case ecdsaKeyType:
   160  		if len(privateKeyBytes) > 0 {
   161  			k.KeyVal = KeyVal{
   162  				Private: strings.TrimSpace(string(generatePEMBlock(privateKeyBytes, pemPrivateKey))),
   163  				Public:  strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))),
   164  			}
   165  		} else {
   166  			k.KeyVal = KeyVal{
   167  				Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))),
   168  			}
   169  		}
   170  	case ed25519KeyType:
   171  		if len(privateKeyBytes) > 0 {
   172  			k.KeyVal = KeyVal{
   173  				Private: strings.TrimSpace(hex.EncodeToString(privateKeyBytes)),
   174  				Public:  strings.TrimSpace(hex.EncodeToString(pubKeyBytes)),
   175  			}
   176  		} else {
   177  			k.KeyVal = KeyVal{
   178  				Public: strings.TrimSpace(hex.EncodeToString(pubKeyBytes)),
   179  			}
   180  		}
   181  	default:
   182  		return fmt.Errorf("%w: %s", ErrUnsupportedKeyType, keyType)
   183  	}
   184  	k.KeyType = keyType
   185  	k.Scheme = scheme
   186  	k.KeyIDHashAlgorithms = KeyIDHashAlgorithms
   187  	if err := k.generateKeyID(); err != nil {
   188  		return err
   189  	}
   190  	return nil
   191  }
   192  
   193  /*
   194  parseKey tries to parse a PEM []byte slice. Using the following standards
   195  in the given order:
   196  
   197    - PKCS8
   198    - PKCS1
   199    - PKIX
   200  
   201  On success it returns the parsed key and nil.
   202  On failure it returns nil and the error ErrFailedPEMParsing
   203  */
   204  func parseKey(data []byte) (interface{}, error) {
   205  	key, err := x509.ParsePKCS8PrivateKey(data)
   206  	if err == nil {
   207  		return key, nil
   208  	}
   209  	key, err = x509.ParsePKCS1PrivateKey(data)
   210  	if err == nil {
   211  		return key, nil
   212  	}
   213  	key, err = x509.ParsePKIXPublicKey(data)
   214  	if err == nil {
   215  		return key, nil
   216  	}
   217  	key, err = x509.ParseCertificate(data)
   218  	if err == nil {
   219  		return key, nil
   220  	}
   221  	key, err = x509.ParseECPrivateKey(data)
   222  	if err == nil {
   223  		return key, nil
   224  	}
   225  	return nil, ErrFailedPEMParsing
   226  }
   227  
   228  /*
   229  decodeAndParse receives potential PEM bytes decodes them via pem.Decode
   230  and pushes them to parseKey. If any error occurs during this process,
   231  the function will return nil and an error (either ErrFailedPEMParsing
   232  or ErrNoPEMBlock). On success it will return the decoded pemData, the
   233  key object interface and nil as error. We need the decoded pemData,
   234  because LoadKey relies on decoded pemData for operating system
   235  interoperability.
   236  */
   237  func decodeAndParse(pemBytes []byte) (*pem.Block, interface{}, error) {
   238  	// pem.Decode returns the parsed pem block and a rest.
   239  	// The rest is everything, that could not be parsed as PEM block.
   240  	// Therefore we can drop this via using the blank identifier "_"
   241  	data, _ := pem.Decode(pemBytes)
   242  	if data == nil {
   243  		return nil, nil, ErrNoPEMBlock
   244  	}
   245  
   246  	// Try to load private key, if this fails try to load
   247  	// key as public key
   248  	key, err := parseKey(data.Bytes)
   249  	if err != nil {
   250  		return nil, nil, err
   251  	}
   252  	return data, key, nil
   253  }
   254  
   255  /*
   256  LoadKey loads the key file at specified file path into the key object.
   257  It automatically derives the PEM type and the key type.
   258  Right now the following PEM types are supported:
   259  
   260    - PKCS1 for private keys
   261    - PKCS8	for private keys
   262    - PKIX for public keys
   263  
   264  The following key types are supported and will be automatically assigned to
   265  the key type field:
   266  
   267    - ed25519
   268    - rsa
   269    - ecdsa
   270  
   271  The following schemes are supported:
   272  
   273    - ed25519 -> ed25519
   274    - rsa -> rsassa-pss-sha256
   275    - ecdsa -> ecdsa-sha256-nistp256
   276  
   277  Note that, this behavior is consistent with the securesystemslib, except for
   278  ecdsa. We do not use the scheme string as key type in in-toto-golang.
   279  Instead we are going with a ecdsa/ecdsa-sha2-nistp256 pair.
   280  
   281  On success it will return nil. The following errors can happen:
   282  
   283    - path not found or not readable
   284    - no PEM block in the loaded file
   285    - no valid PKCS8/PKCS1 private key or PKIX public key
   286    - errors while marshalling
   287    - unsupported key types
   288  */
   289  func (k *Key) LoadKey(path string, scheme string, KeyIDHashAlgorithms []string) error {
   290  	pemFile, err := os.Open(path)
   291  	if err != nil {
   292  		return err
   293  	}
   294  	defer pemFile.Close()
   295  
   296  	err = k.LoadKeyReader(pemFile, scheme, KeyIDHashAlgorithms)
   297  	if err != nil {
   298  		return err
   299  	}
   300  
   301  	return pemFile.Close()
   302  }
   303  
   304  func (k *Key) LoadKeyDefaults(path string) error {
   305  	pemFile, err := os.Open(path)
   306  	if err != nil {
   307  		return err
   308  	}
   309  	defer pemFile.Close()
   310  
   311  	err = k.LoadKeyReaderDefaults(pemFile)
   312  	if err != nil {
   313  		return err
   314  	}
   315  
   316  	return pemFile.Close()
   317  }
   318  
   319  // LoadKeyReader loads the key from a supplied reader. The logic matches LoadKey otherwise.
   320  func (k *Key) LoadKeyReader(r io.Reader, scheme string, KeyIDHashAlgorithms []string) error {
   321  	if r == nil {
   322  		return ErrNoPEMBlock
   323  	}
   324  	// Read key bytes
   325  	pemBytes, err := io.ReadAll(r)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	// decodeAndParse returns the pemData for later use
   330  	// and a parsed key object (for operations on that key, like extracting the public Key)
   331  	pemData, key, err := decodeAndParse(pemBytes)
   332  	if err != nil {
   333  		return err
   334  	}
   335  
   336  	return k.loadKey(key, pemData, scheme, KeyIDHashAlgorithms)
   337  }
   338  
   339  func (k *Key) LoadKeyReaderDefaults(r io.Reader) error {
   340  	if r == nil {
   341  		return ErrNoPEMBlock
   342  	}
   343  	// Read key bytes
   344  	pemBytes, err := io.ReadAll(r)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	// decodeAndParse returns the pemData for later use
   349  	// and a parsed key object (for operations on that key, like extracting the public Key)
   350  	pemData, key, err := decodeAndParse(pemBytes)
   351  	if err != nil {
   352  		return err
   353  	}
   354  
   355  	scheme, keyIDHashAlgorithms, err := getDefaultKeyScheme(key)
   356  	if err != nil {
   357  		return err
   358  	}
   359  
   360  	return k.loadKey(key, pemData, scheme, keyIDHashAlgorithms)
   361  }
   362  
   363  func getDefaultKeyScheme(key interface{}) (scheme string, keyIDHashAlgorithms []string, err error) {
   364  	keyIDHashAlgorithms = []string{"sha256", "sha512"}
   365  
   366  	switch k := key.(type) {
   367  	case *rsa.PublicKey, *rsa.PrivateKey:
   368  		scheme = rsassapsssha256Scheme
   369  	case ed25519.PrivateKey, ed25519.PublicKey:
   370  		scheme = ed25519Scheme
   371  	case *ecdsa.PrivateKey, *ecdsa.PublicKey:
   372  		scheme = ecdsaSha2nistp256
   373  	case *x509.Certificate:
   374  		return getDefaultKeyScheme(k.PublicKey)
   375  	default:
   376  		err = ErrUnsupportedKeyType
   377  	}
   378  
   379  	return scheme, keyIDHashAlgorithms, err
   380  }
   381  
   382  func (k *Key) loadKey(keyObj interface{}, pemData *pem.Block, scheme string, keyIDHashAlgorithms []string) error {
   383  	switch key := keyObj.(type) {
   384  	case *rsa.PublicKey:
   385  		pubKeyBytes, err := x509.MarshalPKIXPublicKey(key)
   386  		if err != nil {
   387  			return err
   388  		}
   389  		if err := k.setKeyComponents(pubKeyBytes, []byte{}, rsaKeyType, scheme, keyIDHashAlgorithms); err != nil {
   390  			return err
   391  		}
   392  	case *rsa.PrivateKey:
   393  		// Note: RSA Public Keys will get stored as X.509 SubjectPublicKeyInfo (RFC5280)
   394  		// This behavior is consistent to the securesystemslib
   395  		pubKeyBytes, err := x509.MarshalPKIXPublicKey(key.Public())
   396  		if err != nil {
   397  			return err
   398  		}
   399  		if err := k.setKeyComponents(pubKeyBytes, pemData.Bytes, rsaKeyType, scheme, keyIDHashAlgorithms); err != nil {
   400  			return err
   401  		}
   402  	case ed25519.PublicKey:
   403  		if err := k.setKeyComponents(key, []byte{}, ed25519KeyType, scheme, keyIDHashAlgorithms); err != nil {
   404  			return err
   405  		}
   406  	case ed25519.PrivateKey:
   407  		pubKeyBytes := key.Public()
   408  		if err := k.setKeyComponents(pubKeyBytes.(ed25519.PublicKey), key, ed25519KeyType, scheme, keyIDHashAlgorithms); err != nil {
   409  			return err
   410  		}
   411  	case *ecdsa.PrivateKey:
   412  		pubKeyBytes, err := x509.MarshalPKIXPublicKey(key.Public())
   413  		if err != nil {
   414  			return err
   415  		}
   416  		if err := k.setKeyComponents(pubKeyBytes, pemData.Bytes, ecdsaKeyType, scheme, keyIDHashAlgorithms); err != nil {
   417  			return err
   418  		}
   419  	case *ecdsa.PublicKey:
   420  		pubKeyBytes, err := x509.MarshalPKIXPublicKey(key)
   421  		if err != nil {
   422  			return err
   423  		}
   424  		if err := k.setKeyComponents(pubKeyBytes, []byte{}, ecdsaKeyType, scheme, keyIDHashAlgorithms); err != nil {
   425  			return err
   426  		}
   427  	case *x509.Certificate:
   428  		err := k.loadKey(key.PublicKey, pemData, scheme, keyIDHashAlgorithms)
   429  		if err != nil {
   430  			return err
   431  		}
   432  
   433  		k.KeyVal.Certificate = string(pem.EncodeToMemory(pemData))
   434  
   435  	default:
   436  		// We should never get here, because we implement all from Go supported Key Types
   437  		return errors.New("unexpected Error in LoadKey function")
   438  	}
   439  
   440  	return nil
   441  }
   442  
   443  /*
   444  VerifyCertificateTrust verifies that the certificate has a chain of trust
   445  to a root in rootCertPool, possibly using any intermediates in
   446  intermediateCertPool
   447  */
   448  func VerifyCertificateTrust(cert *x509.Certificate, rootCertPool, intermediateCertPool *x509.CertPool) ([][]*x509.Certificate, error) {
   449  	verifyOptions := x509.VerifyOptions{
   450  		Roots:         rootCertPool,
   451  		Intermediates: intermediateCertPool,
   452  	}
   453  	chains, err := cert.Verify(verifyOptions)
   454  	if len(chains) == 0 || err != nil {
   455  		return nil, fmt.Errorf("cert cannot be verified by provided roots and intermediates")
   456  	}
   457  	return chains, nil
   458  }