github.com/trustbloc/kms-go@v1.1.2/doc/util/kmsdidkey/kmsdidkey.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 Copyright Avast Software. All Rights Reserved. 4 5 SPDX-License-Identifier: Apache-2.0 6 */ 7 8 package kmsdidkey 9 10 import ( 11 "crypto/elliptic" 12 "encoding/json" 13 "fmt" 14 15 "github.com/btcsuite/btcutil/base58" 16 commonpb "github.com/google/tink/go/proto/common_go_proto" 17 18 afgocrypto "github.com/trustbloc/kms-go/crypto" 19 "github.com/trustbloc/kms-go/doc/util/fingerprint" 20 "github.com/trustbloc/kms-go/doc/util/jwkkid" 21 22 cryptoapi "github.com/trustbloc/kms-go/spi/crypto" 23 "github.com/trustbloc/kms-go/spi/kms" 24 ) 25 26 // keyTypeCodecs maps kms.KeyType to did:key codec. 27 // nolint: gochecknoglobals 28 var keyTypeCodecs = map[kms.KeyType]uint64{ 29 // signing keys 30 kms.ED25519Type: fingerprint.ED25519PubKeyMultiCodec, 31 kms.BLS12381G2Type: fingerprint.BLS12381g2PubKeyMultiCodec, 32 kms.ECDSAP256TypeIEEEP1363: fingerprint.P256PubKeyMultiCodec, 33 kms.ECDSAP256TypeDER: fingerprint.P256PubKeyMultiCodec, 34 kms.ECDSAP384TypeIEEEP1363: fingerprint.P384PubKeyMultiCodec, 35 kms.ECDSAP384TypeDER: fingerprint.P384PubKeyMultiCodec, 36 kms.ECDSAP521TypeIEEEP1363: fingerprint.P521PubKeyMultiCodec, 37 kms.ECDSAP521TypeDER: fingerprint.P521PubKeyMultiCodec, 38 39 // encryption keys 40 kms.X25519ECDHKWType: fingerprint.X25519PubKeyMultiCodec, 41 kms.NISTP256ECDHKWType: fingerprint.P256PubKeyMultiCodec, 42 kms.NISTP384ECDHKWType: fingerprint.P384PubKeyMultiCodec, 43 kms.NISTP521ECDHKWType: fingerprint.P521PubKeyMultiCodec, 44 } 45 46 // BuildDIDKeyByKeyType creates a did key for pubKeyBytes based on the kms keyType. 47 func BuildDIDKeyByKeyType(pubKeyBytes []byte, keyType kms.KeyType) (string, error) { 48 switch keyType { 49 case kms.X25519ECDHKW: 50 pubKey := &cryptoapi.PublicKey{} 51 52 err := json.Unmarshal(pubKeyBytes, pubKey) 53 if err != nil { 54 return "", fmt.Errorf("buildDIDkeyByKMSKeyType failed to unmarshal key type %v: %w", keyType, err) 55 } 56 57 pubKeyBytes = make([]byte, len(pubKey.X)) 58 copy(pubKeyBytes, pubKey.X) 59 case kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType: 60 pubKey := &cryptoapi.PublicKey{} 61 62 err := json.Unmarshal(pubKeyBytes, pubKey) 63 if err != nil { 64 return "", fmt.Errorf("buildDIDkeyByKMSKeyType failed to unmarshal key type %v: %w", keyType, err) 65 } 66 67 ecKey, err := afgocrypto.ToECKey(pubKey) 68 if err != nil { 69 return "", fmt.Errorf("buildDIDkeyByKMSKeyType failed to unmarshal key type %v: %w", keyType, err) 70 } 71 72 // used Compressed EC format for did:key, the same way as vdr key creator. 73 pubKeyBytes = elliptic.MarshalCompressed(ecKey.Curve, ecKey.X, ecKey.Y) 74 } 75 76 if codec, ok := keyTypeCodecs[keyType]; ok { 77 didKey, _ := fingerprint.CreateDIDKeyByCode(codec, pubKeyBytes) 78 79 return didKey, nil 80 } 81 82 return "", fmt.Errorf("keyType '%s' does not have a multi-base codec", keyType) 83 } 84 85 // EncryptionPubKeyFromDIDKey parses the did:key DID and returns the key's raw value. 86 // note: for NIST P ECDSA keys, the raw value does not have the compression point. 87 // 88 // In order to use elliptic.Unmarshal() with the raw value, the uncompressed point ([]byte{4}) must be prepended. 89 // see https://github.com/golang/go/blob/master/src/crypto/elliptic/elliptic.go#L384. 90 // 91 //nolint:funlen,gocyclo 92 func EncryptionPubKeyFromDIDKey(didKey string) (*cryptoapi.PublicKey, error) { 93 pubKey, code, err := extractRawKey(didKey) 94 if err != nil { 95 return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err) 96 } 97 98 var ( 99 crv string 100 kmtKT kms.KeyType 101 kt string 102 x []byte 103 y []byte 104 ) 105 106 switch code { 107 case fingerprint.ED25519PubKeyMultiCodec: // TODO remove this case when legacyPacker is decommissioned. 108 var edKID string 109 110 kmtKT = kms.ED25519Type 111 pubEDKey := &cryptoapi.PublicKey{ 112 X: pubKey, 113 Curve: "Ed25519", 114 Type: "OKP", 115 } 116 117 edKID, err = jwkkid.CreateKID(pubKey, kmtKT) 118 if err != nil { 119 return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err) 120 } 121 122 pubEDKey.KID = edKID 123 124 return pubEDKey, nil 125 case fingerprint.X25519PubKeyMultiCodec: 126 var ( 127 mPubXKey []byte 128 xKID string 129 ) 130 131 kmtKT = kms.X25519ECDHKWType 132 pubXKey := &cryptoapi.PublicKey{ 133 X: pubKey, 134 Curve: "X25519", 135 Type: "OKP", 136 } 137 138 mPubXKey, err = json.Marshal(pubXKey) 139 if err != nil { 140 return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err) 141 } 142 143 xKID, err = jwkkid.CreateKID(mPubXKey, kmtKT) 144 if err != nil { 145 return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err) 146 } 147 148 pubXKey.KID = xKID 149 150 return pubXKey, nil 151 case fingerprint.P256PubKeyMultiCodec: 152 kmtKT = kms.ECDSAP256IEEEP1363 153 kt = "EC" 154 crv, x, y, pubKey = unmarshalECKey(elliptic.P256(), pubKey) 155 case fingerprint.P384PubKeyMultiCodec: 156 kmtKT = kms.ECDSAP384IEEEP1363 157 kt = "EC" 158 crv, x, y, pubKey = unmarshalECKey(elliptic.P384(), pubKey) 159 case fingerprint.P521PubKeyMultiCodec: 160 kmtKT = kms.ECDSAP521TypeIEEEP1363 161 kt = "EC" 162 crv, x, y, pubKey = unmarshalECKey(elliptic.P521(), pubKey) 163 default: 164 return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: unsupported key multicodec code [0x%x]", code) 165 } 166 167 kid, err := jwkkid.CreateKID(pubKey, kmtKT) 168 if err != nil { 169 return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err) 170 } 171 172 return &cryptoapi.PublicKey{ 173 KID: kid, 174 X: x, 175 Y: y, 176 Curve: crv, 177 Type: kt, 178 }, nil 179 } 180 181 func unmarshalECKey(ecCRV elliptic.Curve, pubKey []byte) (string, []byte, []byte, []byte) { 182 var ( 183 x []byte 184 y []byte 185 ) 186 187 ecCurves := map[elliptic.Curve]string{ 188 elliptic.P256(): commonpb.EllipticCurveType_NIST_P256.String(), 189 elliptic.P384(): commonpb.EllipticCurveType_NIST_P384.String(), 190 elliptic.P521(): commonpb.EllipticCurveType_NIST_P521.String(), 191 } 192 193 xBig, yBig := elliptic.UnmarshalCompressed(ecCRV, pubKey) 194 if xBig != nil && yBig != nil { 195 x = xBig.Bytes() 196 y = yBig.Bytes() 197 198 // need to marshal pubKey in uncompressed format for CreateKID() call in EncryptionPubKeyFromDIDKey above since 199 // did:key uses compressed elliptic format. 200 pubKey = elliptic.Marshal(ecCRV, xBig, yBig) 201 } else { // try normal Unmarshal if compressed returned nil xBig and yBig. 202 // add compression byte for uncompressed key, comment of fingerprint.PubKeyFromDIDKey(). 203 pubKey = append([]byte{4}, pubKey...) 204 xBig, yBig = elliptic.Unmarshal(ecCRV, pubKey) 205 206 x = xBig.Bytes() 207 y = yBig.Bytes() 208 } 209 210 return ecCurves[ecCRV], x, y, pubKey 211 } 212 213 func extractRawKey(didKey string) ([]byte, uint64, error) { 214 idMethodSpecificID, err := fingerprint.MethodIDFromDIDKey(didKey) 215 if err != nil { 216 return nil, 0, fmt.Errorf("extractRawKey: MethodIDFromDIDKey failure: %w", err) 217 } 218 219 pubKey, code, err := fingerprint.PubKeyFromFingerprint(idMethodSpecificID) 220 if err != nil { 221 return nil, 0, fmt.Errorf("extractRawKey: PubKeyFromFingerprint failure: %w", err) 222 } 223 224 return pubKey, code, nil 225 } 226 227 // GetBase58PubKeyFromDIDKey parses the did:key DID and returns the key's base58 encoded value. 228 func GetBase58PubKeyFromDIDKey(didKey string) (string, error) { 229 key, err := EncryptionPubKeyFromDIDKey(didKey) 230 if err != nil { 231 return "", fmt.Errorf("GetBase58PubKeyFromDIDKey: failed to parse public key bytes from "+ 232 "%s: %w", didKey, err) 233 } 234 235 return base58.Encode(key.X), nil 236 }