github.com/trustbloc/kms-go@v1.1.2/doc/util/jwkkid/kid_creator.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package jwkkid 8 9 import ( 10 "crypto" 11 "crypto/ecdsa" 12 "crypto/elliptic" 13 "encoding/base64" 14 "encoding/json" 15 "errors" 16 "fmt" 17 "math/big" 18 19 "github.com/trustbloc/kms-go/util/cryptoutil" 20 21 "github.com/trustbloc/kms-go/spi/kms" 22 23 cryptoapi "github.com/trustbloc/kms-go/spi/crypto" 24 25 "github.com/trustbloc/kms-go/doc/jose/jwk" 26 "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" 27 ) 28 29 var errInvalidKeyType = errors.New("key type is not supported") 30 31 // CreateKID creates a KID value based on the marshalled keyBytes of type kt. This function should be called for 32 // asymmetric public keys only (ECDSA DER or IEEE-P1363, ED25519, X25519, BLS12381G2). 33 // returns: 34 // - base64 raw (no padding) URL encoded KID 35 // - error in case of error 36 // 37 //nolint:gocyclo 38 func CreateKID(keyBytes []byte, kt kms.KeyType) (string, error) { 39 if len(keyBytes) == 0 { 40 return "", errors.New("createKID: empty key") 41 } 42 43 switch kt { 44 case kms.X25519ECDHKWType: // X25519 JWK is not supported by go jose, manually build it and build its resulting KID. 45 x25519KID, err := createX25519KID(keyBytes) 46 if err != nil { 47 return "", fmt.Errorf("createKID: %w", err) 48 } 49 50 return x25519KID, nil 51 case kms.BLS12381G2Type: // BBS+ as JWK thumbprint. 52 bbsKID, err := createBLS12381G2KID(keyBytes) 53 if err != nil { 54 return "", fmt.Errorf("createKID: %w", err) 55 } 56 57 return bbsKID, nil 58 case kms.ECDSASecp256k1TypeDER, kms.ECDSASecp256k1TypeIEEEP1363: 59 secp256k1KID, err := secp256k1Thumbprint(keyBytes, kt) 60 if err != nil { 61 return "", fmt.Errorf("createKID: %w", err) 62 } 63 64 return secp256k1KID, nil 65 } 66 67 j, err := BuildJWK(keyBytes, kt) 68 if err != nil { 69 return "", fmt.Errorf("createKID: failed to build jwk: %w", err) 70 } 71 72 tp, err := j.Thumbprint(crypto.SHA256) 73 if err != nil { 74 return "", fmt.Errorf("createKID: failed to get jwk Thumbprint: %w", err) 75 } 76 77 return base64.RawURLEncoding.EncodeToString(tp), nil 78 } 79 80 func secp256k1Thumbprint(keyBytes []byte, kt kms.KeyType) (string, error) { 81 switch kt { 82 case kms.ECDSASecp256k1IEEEP1363: 83 case kms.ECDSASecp256k1DER: 84 default: 85 return "", fmt.Errorf("secp256k1Thumbprint: invalid key type: %s", kt) 86 } 87 88 k, err := jwksupport.PubKeyBytesToKey(keyBytes, kt) 89 if err != nil { 90 return "", fmt.Errorf("secp256k1Thumbprint: failed to build jwk: %w", err) 91 } 92 93 var input string 94 95 switch key := k.(type) { 96 case *ecdsa.PublicKey: 97 input, err = secp256k1ThumbprintInput(key.Curve, key.X, key.Y) 98 if err != nil { 99 return "", fmt.Errorf("secp256k1Thumbprint: failed to get public key thumbprint input: %w", err) 100 } 101 default: 102 return "", fmt.Errorf("secp256k1Thumbprint: unknown key type '%T'", key) 103 } 104 105 h := crypto.SHA256.New() 106 _, _ = h.Write([]byte(input)) 107 108 return base64.RawURLEncoding.EncodeToString(h.Sum(nil)), nil 109 } 110 111 func secp256k1ThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { 112 ecSecp256K1ThumbprintTemplate := `{"crv":"SECP256K1","kty":"EC","x":"%s","y":"%s"}` 113 114 coordLength := curveSize(curve) 115 116 if len(x.Bytes()) > coordLength || len(y.Bytes()) > coordLength { 117 return "", errors.New("invalid elliptic secp256k1 key (too large)") 118 } 119 120 return fmt.Sprintf(ecSecp256K1ThumbprintTemplate, 121 newFixedSizeBuffer(x.Bytes(), coordLength).base64(), 122 newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil 123 } 124 125 // byteBuffer represents a slice of bytes that can be serialized to url-safe base64. 126 type byteBuffer struct { 127 data []byte 128 } 129 130 func (b *byteBuffer) base64() string { 131 return base64.RawURLEncoding.EncodeToString(b.data) 132 } 133 134 func newBuffer(data []byte) *byteBuffer { 135 if data == nil { 136 return nil 137 } 138 139 return &byteBuffer{ 140 data: data, 141 } 142 } 143 144 func newFixedSizeBuffer(data []byte, length int) *byteBuffer { 145 if len(data) > length { 146 panic("newFixedSizeBuffer: invalid call to newFixedSizeBuffer (len(data) > length)") 147 } 148 149 pad := make([]byte, length-len(data)) 150 151 return newBuffer(append(pad, data...)) 152 } 153 154 // Get size of curve in bytes. 155 func curveSize(crv elliptic.Curve) int { 156 bits := crv.Params().BitSize 157 byteSize := 8 158 159 div := bits / byteSize 160 mod := bits % byteSize 161 162 if mod == 0 { 163 return div 164 } 165 166 return div + 1 167 } 168 169 // BuildJWK builds a go jose JWK from keyBytes with key type kt. 170 func BuildJWK(keyBytes []byte, kt kms.KeyType) (*jwk.JWK, error) { //nolint: gocyclo 171 switch kt { 172 case 173 kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER, 174 kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363, 175 kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType, 176 kms.ECDSASecp256k1DER, kms.ECDSASecp256k1IEEEP1363, 177 kms.ED25519Type, kms.X25519ECDHKWType, kms.BLS12381G2Type: 178 return jwksupport.PubKeyBytesToJWK(keyBytes, kt) 179 default: 180 return nil, fmt.Errorf("buildJWK: %w: '%s'", errInvalidKeyType, kt) 181 } 182 } 183 184 func unmarshalECDHKey(keyBytes []byte) (*cryptoapi.PublicKey, error) { 185 compositeKey := &cryptoapi.PublicKey{} 186 187 err := json.Unmarshal(keyBytes, compositeKey) 188 if err != nil { 189 return nil, fmt.Errorf("unmarshalECDHKey: failed to unmarshal ECDH key: %w", err) 190 } 191 192 return compositeKey, nil 193 } 194 195 func createX25519KID(marshalledKey []byte) (string, error) { 196 compositeKey, err := unmarshalECDHKey(marshalledKey) 197 if err != nil { 198 return "", fmt.Errorf("createX25519KID: %w", err) 199 } 200 201 j, err := buildX25519JWK(compositeKey.X) 202 if err != nil { 203 return "", fmt.Errorf("createX25519KID: %w", err) 204 } 205 206 thumbprint := sha256Sum(j) 207 208 return base64.RawURLEncoding.EncodeToString(thumbprint), nil 209 } 210 211 func buildX25519JWK(keyBytes []byte) (string, error) { 212 const x25519ThumbprintTemplate = `{"crv":"X25519","kty":"OKP","x":"%s"}` 213 214 lenKey := len(keyBytes) 215 if lenKey > cryptoutil.Curve25519KeySize { 216 return "", errors.New("buildX25519JWK: invalid ECDH X25519 key") 217 } 218 219 pad := make([]byte, cryptoutil.Curve25519KeySize-lenKey) 220 x25519RawKey := append(pad, keyBytes...) 221 222 j := fmt.Sprintf(x25519ThumbprintTemplate, base64.RawURLEncoding.EncodeToString(x25519RawKey)) 223 224 return j, nil 225 } 226 227 func createBLS12381G2KID(keyBytes []byte) (string, error) { 228 const ( 229 bls12381g2ThumbprintTemplate = `{"crv":"Bls12381g2","kty":"OKP","x":"%s"}` 230 // Default BLS 12-381 public key length in G2 field. 231 bls12381G2PublicKeyLen = 96 232 ) 233 234 lenKey := len(keyBytes) 235 236 if lenKey > bls12381G2PublicKeyLen { 237 return "", errors.New("invalid BBS+ key") 238 } 239 240 pad := make([]byte, bls12381G2PublicKeyLen-lenKey) 241 bbsRawKey := append(pad, keyBytes...) 242 243 j := fmt.Sprintf(bls12381g2ThumbprintTemplate, base64.RawURLEncoding.EncodeToString(bbsRawKey)) 244 245 thumbprint := sha256Sum(j) 246 247 return base64.RawURLEncoding.EncodeToString(thumbprint), nil 248 } 249 250 func sha256Sum(j string) []byte { 251 h := crypto.SHA256.New() 252 _, _ = h.Write([]byte(j)) // SHA256 digest returns empty error on Write() 253 254 return h.Sum(nil) 255 }