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 }