github.com/trustbloc/kms-go@v1.1.2/doc/util/fingerprint/fingerprint.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package fingerprint 8 9 import ( 10 "crypto/ecdsa" 11 "crypto/ed25519" 12 "crypto/elliptic" 13 "crypto/rsa" 14 "crypto/x509" 15 "encoding/binary" 16 "errors" 17 "fmt" 18 19 "github.com/btcsuite/btcutil/base58" 20 21 "github.com/trustbloc/kms-go/doc/jose/jwk" 22 ) 23 24 const ( 25 // X25519PubKeyMultiCodec for Curve25519 public key in multicodec table. 26 // source: https://github.com/multiformats/multicodec/blob/master/table.csv. 27 X25519PubKeyMultiCodec = 0xec 28 // ED25519PubKeyMultiCodec for Ed25519 public key in multicodec table. 29 ED25519PubKeyMultiCodec = 0xed 30 // BLS12381g2PubKeyMultiCodec for BLS12-381 G2 public key in multicodec table. 31 BLS12381g2PubKeyMultiCodec = 0xeb 32 // BLS12381g1g2PubKeyMultiCodec for BLS12-381 G1G2 public key in multicodec table. 33 BLS12381g1g2PubKeyMultiCodec = 0xee 34 // P256PubKeyMultiCodec for NIST P-256 public key in multicodec table. 35 P256PubKeyMultiCodec = 0x1200 36 // P384PubKeyMultiCodec for NIST P-384 public key in multicodec table. 37 P384PubKeyMultiCodec = 0x1201 38 // P521PubKeyMultiCodec for NIST P-521 public key in multicodec table. 39 P521PubKeyMultiCodec = 0x1202 40 41 // RSAPubKeyMultiCodec for RSA public key in multicodec table. 42 RSAPubKeyMultiCodec = 0x1205 43 44 // Default BLS 12-381 public key length in G2 field. 45 bls12381G2PublicKeyLen = 96 46 47 // Number of bytes in G1 X coordinate. 48 g1CompressedSize = 48 49 ) 50 51 // CreateDIDKey calls CreateDIDKeyByCode with Ed25519 key code. 52 func CreateDIDKey(pubKey []byte) (string, string) { 53 return CreateDIDKeyByCode(ED25519PubKeyMultiCodec, pubKey) 54 } 55 56 // CreateDIDKeyByCode creates a did:key ID using the multicodec key fingerprint as per the did:key format spec found at: 57 // https://w3c-ccg.github.io/did-method-key/#format. It does not parse the contents of 'pubKey'. Use 58 // kmsdidkey.BuildDIDKeyByKeyType() for marshalled keys extracted from the KMS instead of this function. 59 func CreateDIDKeyByCode(code uint64, pubKey []byte) (string, string) { 60 methodID := KeyFingerprint(code, pubKey) 61 didKey := fmt.Sprintf("did:key:%s", methodID) 62 keyID := fmt.Sprintf("%s#%s", didKey, methodID) 63 64 return didKey, keyID 65 } 66 67 // CreateDIDKeyByJwk creates a did:key ID using the multicodec key fingerprint as per the did:key format spec found at: 68 // https://w3c-ccg.github.io/did-method-key/#format. 69 func CreateDIDKeyByJwk(jsonWebKey *jwk.JWK) (string, string, error) { //nolint:gocyclo 70 if jsonWebKey == nil { 71 return "", "", fmt.Errorf("jsonWebKey is required") 72 } 73 74 switch jsonWebKey.Kty { 75 case "EC": 76 code, curve, err := ecCodeAndCurve(jsonWebKey.Crv) 77 if err != nil { 78 return "", "", err 79 } 80 81 switch key := jsonWebKey.Key.(type) { 82 case *ecdsa.PublicKey: 83 bytes := elliptic.MarshalCompressed(curve, key.X, key.Y) 84 didKey, keyID := CreateDIDKeyByCode(code, bytes) 85 86 return didKey, keyID, nil 87 default: 88 return "", "", fmt.Errorf("unexpected EC key type %T", key) 89 } 90 case "OKP": 91 var code uint64 92 93 switch jsonWebKey.Crv { 94 case "X25519": 95 code = X25519PubKeyMultiCodec 96 case "Ed25519": 97 code = ED25519PubKeyMultiCodec 98 } 99 100 switch key := jsonWebKey.Key.(type) { 101 case ed25519.PublicKey: 102 didKey, keyID := CreateDIDKey(key) 103 104 return didKey, keyID, nil 105 case []byte: 106 didKey, keyID := CreateDIDKeyByCode(code, key) 107 108 return didKey, keyID, nil 109 default: 110 return "", "", fmt.Errorf("unexpected OKP key type %T", key) 111 } 112 case "RSA": 113 key, ok := jsonWebKey.Key.(*rsa.PublicKey) 114 if !ok { 115 return "", "", fmt.Errorf("unexpected RSA key type %T", jsonWebKey.Key) 116 } 117 118 didKey, keyID := CreateDIDKeyByCode(RSAPubKeyMultiCodec, x509.MarshalPKCS1PublicKey(key)) 119 120 return didKey, keyID, nil 121 122 default: 123 return "", "", fmt.Errorf("unsupported kty %s", jsonWebKey.Kty) 124 } 125 } 126 127 func ecCodeAndCurve(ecCurve string) (uint64, elliptic.Curve, error) { 128 var ( 129 curve elliptic.Curve 130 code uint64 131 ) 132 133 switch ecCurve { 134 case elliptic.P256().Params().Name, "NIST_P256": 135 curve = elliptic.P256() 136 code = P256PubKeyMultiCodec 137 case elliptic.P384().Params().Name, "NIST_P384": 138 curve = elliptic.P384() 139 code = P384PubKeyMultiCodec 140 case elliptic.P521().Params().Name, "NIST_P521": 141 curve = elliptic.P521() 142 code = P521PubKeyMultiCodec 143 default: 144 return 0, nil, fmt.Errorf("unsupported crv %s", ecCurve) 145 } 146 147 return code, curve, nil 148 } 149 150 // KeyFingerprint generates a multicode fingerprint for pubKeyValue (raw key []byte). 151 // It is mainly used as the controller ID (methodSpecification ID) of a did key. 152 func KeyFingerprint(code uint64, pubKeyValue []byte) string { 153 multicodecValue := multicodec(code) 154 mcLength := len(multicodecValue) 155 buf := make([]uint8, mcLength+len(pubKeyValue)) 156 copy(buf, multicodecValue) 157 copy(buf[mcLength:], pubKeyValue) 158 159 return fmt.Sprintf("z%s", base58.Encode(buf)) 160 } 161 162 func multicodec(code uint64) []byte { 163 buf := make([]byte, binary.MaxVarintLen64) 164 bw := binary.PutUvarint(buf, code) 165 166 return buf[:bw] 167 } 168 169 // PubKeyFromFingerprint extracts the raw public key from a did:key fingerprint. 170 func PubKeyFromFingerprint(fingerprint string) ([]byte, uint64, error) { 171 // did:key:MULTIBASE(base58-btc, MULTICODEC(public-key-type, raw-public-key-bytes)) 172 // https://w3c-ccg.github.io/did-method-key/#format 173 const maxMulticodecBytes = 9 174 175 if len(fingerprint) < 2 || fingerprint[0] != 'z' { 176 return nil, 0, errors.New("unknown key encoding") 177 } 178 179 mc := base58.Decode(fingerprint[1:]) // skip leading "z" 180 181 code, br := binary.Uvarint(mc) 182 if br == 0 { 183 return nil, 0, errors.New("unknown key encoding") 184 } 185 186 if br > maxMulticodecBytes { 187 return nil, 0, errors.New("code exceeds maximum size") 188 } 189 190 if code == BLS12381g1g2PubKeyMultiCodec { 191 // for BBS+ G1G2 did:key type, return the G2 public key only (discard G1 key for now). 192 if len(mc[br+g1CompressedSize:]) != bls12381G2PublicKeyLen { 193 return nil, 0, errors.New("invalid bbs+ public key") 194 } 195 196 return mc[br+g1CompressedSize:], code, nil 197 } 198 199 return mc[br:], code, nil 200 } 201 202 // PubKeyFromDIDKey parses the did:key DID and returns the key's raw value. 203 // note: for NIST P ECDSA keys, the raw value does not have the compression point. 204 // 205 // In order to use elliptic.Unmarshal() with the raw value, the uncompressed point ([]byte{4}) must be prepended. 206 // see https://github.com/golang/go/blob/master/src/crypto/elliptic/elliptic.go#L384. 207 func PubKeyFromDIDKey(didKey string) ([]byte, error) { 208 idMethodSpecificID, err := MethodIDFromDIDKey(didKey) 209 if err != nil { 210 return nil, fmt.Errorf("pubKeyFromDIDKey: MethodIDFromDIDKey: %w", err) 211 } 212 213 pubKey, code, err := PubKeyFromFingerprint(idMethodSpecificID) 214 if err != nil { 215 return nil, err 216 } 217 218 switch code { 219 case X25519PubKeyMultiCodec, ED25519PubKeyMultiCodec, BLS12381g2PubKeyMultiCodec, BLS12381g1g2PubKeyMultiCodec, 220 P256PubKeyMultiCodec, P384PubKeyMultiCodec, P521PubKeyMultiCodec: 221 break 222 default: 223 return nil, fmt.Errorf("pubKeyFromDIDKey: unsupported key multicodec code [0x%x]", code) 224 } 225 226 return pubKey, nil 227 }