github.com/hashicorp/vault/sdk@v0.11.0/helper/keysutil/util.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package keysutil
     5  
     6  import (
     7  	"crypto/x509"
     8  	"crypto/x509/pkix"
     9  	"encoding/asn1"
    10  	"errors"
    11  	"fmt"
    12  
    13  	"golang.org/x/crypto/ed25519"
    14  )
    15  
    16  // pkcs8 reflects an ASN.1, PKCS #8 PrivateKey. See
    17  // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
    18  // and RFC 5208.
    19  //
    20  // Copied from Go: https://github.com/golang/go/blob/master/src/crypto/x509/pkcs8.go#L17-L80
    21  type pkcs8 struct {
    22  	Version    int
    23  	Algo       pkix.AlgorithmIdentifier
    24  	PrivateKey []byte
    25  	// optional attributes omitted.
    26  }
    27  
    28  // ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
    29  // References:
    30  //
    31  //	RFC 5915
    32  //	SEC1 - http://www.secg.org/sec1-v2.pdf
    33  //
    34  // Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
    35  // most cases it is not.
    36  //
    37  // Copied from Go: 	https://github.com/golang/go/blob/master/src/crypto/x509/sec1.go#L18-L31
    38  type ecPrivateKey struct {
    39  	Version       int
    40  	PrivateKey    []byte
    41  	NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
    42  
    43  	// Because the PKCS8/RFC 5915 encoding of the Ed25519 key uses the
    44  	// RFC 8032 Ed25519 seed format, we can ignore the public key parameter
    45  	// and infer it later.
    46  }
    47  
    48  var (
    49  	// See crypto/x509/x509.go in the Go toolchain source distribution.
    50  	oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
    51  
    52  	// NSS encodes Ed25519 private keys with the OID 1.3.6.1.4.1.11591.15.1
    53  	// from https://tools.ietf.org/html/draft-josefsson-pkix-newcurves-01.
    54  	// See https://github.com/nss-dev/nss/blob/NSS_3_79_BRANCH/lib/util/secoid.c#L600-L603.
    55  	oidNSSPKIXEd25519 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11591, 15, 1}
    56  
    57  	// Other implementations may use the OID 1.3.101.110 from
    58  	// https://datatracker.ietf.org/doc/html/rfc8410.
    59  	oidRFC8410Ed25519 = asn1.ObjectIdentifier{1, 3, 101, 110}
    60  
    61  	// See crypto/x509/x509.go in the Go toolchain source distribution.
    62  	oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
    63  )
    64  
    65  func isEd25519OID(oid asn1.ObjectIdentifier) bool {
    66  	return oidNSSPKIXEd25519.Equal(oid) || oidRFC8410Ed25519.Equal(oid)
    67  }
    68  
    69  // ParsePKCS8PrivateKey parses an unencrypted private key in PKCS #8, ASN.1 DER form.
    70  //
    71  // It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey.
    72  // More types might be supported in the future.
    73  //
    74  // This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
    75  func ParsePKCS8Ed25519PrivateKey(der []byte) (key interface{}, err error) {
    76  	var privKey pkcs8
    77  	var ed25519Key ecPrivateKey
    78  
    79  	var checkedOID bool
    80  
    81  	// If this err is nil, we assume we directly have a ECPrivateKey structure
    82  	// with explicit OID; ignore this error for now and return the latter err
    83  	// instead if neither parse correctly.
    84  	if _, err := asn1.Unmarshal(der, &privKey); err == nil {
    85  		switch {
    86  		case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA):
    87  			bytes := privKey.Algo.Parameters.FullBytes
    88  			namedCurveOID := new(asn1.ObjectIdentifier)
    89  			if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
    90  				namedCurveOID = nil
    91  			}
    92  
    93  			if namedCurveOID == nil || !isEd25519OID(*namedCurveOID) {
    94  				return nil, errors.New("keysutil: failed to parse private key (invalid, non-ed25519 curve parameter OID)")
    95  			}
    96  
    97  			der = privKey.PrivateKey
    98  			checkedOID = true
    99  		default:
   100  			// The Go standard library already parses RFC 8410 keys; the
   101  			// inclusion of the OID here is in case it is used with the
   102  			// regular ECDSA PrivateKey structure, rather than the struct
   103  			// recognized by the Go standard library.
   104  			return nil, errors.New("keysutil: failed to parse key as ed25519 private key")
   105  		}
   106  	}
   107  
   108  	_, err = asn1.Unmarshal(der, &ed25519Key)
   109  	if err != nil {
   110  		return nil, fmt.Errorf("keysutil: failed to parse private key (inner Ed25519 ECPrivateKey format was incorrect): %v", err)
   111  	}
   112  
   113  	if !checkedOID && !isEd25519OID(ed25519Key.NamedCurveOID) {
   114  		return nil, errors.New("keysutil: failed to parse private key (invalid, non-ed25519 curve parameter OID)")
   115  	}
   116  
   117  	if len(ed25519Key.PrivateKey) != 32 {
   118  		return nil, fmt.Errorf("keysutil: failed to parse private key as ed25519 private key: got %v bytes but expected %v byte RFC 8032 seed", len(ed25519Key.PrivateKey), ed25519.SeedSize)
   119  	}
   120  
   121  	return ed25519.NewKeyFromSeed(ed25519Key.PrivateKey), nil
   122  }
   123  
   124  // ParsePKCS8PrivateKey parses an unencrypted private key in PKCS #8, ASN.1 DER form.
   125  //
   126  // This helper only supports RSA/PSS keys (with OID 1.2.840.113549.1.1.10).
   127  //
   128  // It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey.
   129  // More types might be supported in the future.
   130  //
   131  // This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
   132  func ParsePKCS8RSAPSSPrivateKey(der []byte) (key interface{}, err error) {
   133  	var privKey pkcs8
   134  	if _, err := asn1.Unmarshal(der, &privKey); err == nil {
   135  		switch {
   136  		case privKey.Algo.Algorithm.Equal(oidSignatureRSAPSS):
   137  			// Fall through; there's no parameters here unlike ECDSA
   138  			// containers, so we can go to parsing the inner rsaPrivateKey
   139  			// object.
   140  		default:
   141  			return nil, errors.New("keysutil: failed to parse key as RSA PSS private key")
   142  		}
   143  	}
   144  
   145  	key, err = x509.ParsePKCS1PrivateKey(privKey.PrivateKey)
   146  	if err != nil {
   147  		return nil, fmt.Errorf("keysutil: failed to parse inner RSA PSS private key: %w", err)
   148  	}
   149  
   150  	return key, nil
   151  }