github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/jose/kid/resolver/resolver.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package resolver 8 9 import ( 10 "crypto/ecdsa" 11 "crypto/elliptic" 12 "encoding/json" 13 "errors" 14 "fmt" 15 "strings" 16 17 cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto" 18 "github.com/hyperledger/aries-framework-go/pkg/doc/did" 19 "github.com/hyperledger/aries-framework-go/pkg/doc/util/jwkkid" 20 "github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey" 21 vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" 22 "github.com/hyperledger/aries-framework-go/pkg/kms" 23 "github.com/hyperledger/aries-framework-go/spi/storage" 24 ) 25 26 const ( 27 jsonWebKey2020 = "JsonWebKey2020" 28 x25519KeyAgreementKey2019 = "X25519KeyAgreementKey2019" 29 ) 30 31 // KIDResolver helps resolve the kid public key from a recipient 'kid' or a sender 'skid' during JWE decryption. 32 // The JWEDecrypter should be able to load the public key using a resolution scheme for a key reference found in the 33 // 'skid' JWE protected header/'kid' recipient header. 34 type KIDResolver interface { 35 // Resolve a 'kid'/'skid' into a marshalled public key or error if key resolution fails. 36 Resolve(string) (*cryptoapi.PublicKey, error) 37 } 38 39 // DIDKeyResolver resolves a 'kid'/'skid' containing a did:key value. 40 type DIDKeyResolver struct{} 41 42 // Resolve a 'kid'/'skid' protected header with a did:key value into a marshalled public key or error if key 43 // resolution fails. 44 func (k *DIDKeyResolver) Resolve(kid string) (*cryptoapi.PublicKey, error) { 45 return kmsdidkey.EncryptionPubKeyFromDIDKey(kid) 46 } 47 48 // StoreResolver resolves a 'kid'/'skid' containing a kms ID value (JWK fingerprint) from a dedicated pre-loaded store. 49 // Note: this is not a kms keystore. This StoreResolver is useful in cases where a thirdparty store is needed. This is 50 // useful in unit tests and especially for test vectors using the ECDH-1PU Appendix B example to load the sender key 51 // so that recipients can resolve a predefined 'skid'. Aries Framework Go is using the DIDKeyResolver by default (for 52 // request without DID docs) and DIDDocResolver (for requests with existing DID connections). 53 type StoreResolver struct { 54 // store where the kid key is potentially stored. 55 Store storage.Store 56 } 57 58 // Resolve a 'kid'/'skid' by loading kid's PublicKey from a store or return an error if it fails. 59 func (s *StoreResolver) Resolve(kid string) (*cryptoapi.PublicKey, error) { 60 var pubKey *cryptoapi.PublicKey 61 62 mPubKey, err := s.Store.Get(kid) 63 if err != nil { 64 return nil, fmt.Errorf("storeResolver: failed to resolve kid from store: %w", err) 65 } 66 67 err = json.Unmarshal(mPubKey, &pubKey) 68 if err != nil { 69 return nil, fmt.Errorf("storeResolver: failed to unmarshal public key from DB: %w", err) 70 } 71 72 return pubKey, nil 73 } 74 75 // DIDDocResolver helps resolves a KMS kid from 'kid'/'skid' with values set as didDoc[].KeyAgreement[].ID. The list of 76 // DIDDocs should contain both sender and recipients docs for proper resolutio during unpacking. 77 type DIDDocResolver struct { 78 VDRRegistry vdrapi.Registry 79 } 80 81 // Resolve kid into a *cryptoapi.PublicKey with ID set as the KMS kid. Where kid matches the DID doc found in the vdr 82 // registry with first key entry matching doc.keyAgreement[].VerificationMethod.ID. 83 func (d *DIDDocResolver) Resolve(kid string) (*cryptoapi.PublicKey, error) { 84 var ( 85 pubKey *cryptoapi.PublicKey 86 err error 87 ) 88 89 if d.VDRRegistry == nil { 90 return nil, errors.New("didDocResolver: missing vdr registry") 91 } 92 93 i := strings.Index(kid, "#") 94 95 if i < 0 { 96 return nil, fmt.Errorf("didDocResolver: kid is not KeyAgreement.ID: '%v'", kid) 97 } 98 99 didDoc, err := d.VDRRegistry.Resolve(kid[:i]) 100 if err != nil { 101 return nil, fmt.Errorf("didDocResolver: for recipient DID doc resolution %w", err) 102 } 103 104 for _, ka := range didDoc.DIDDocument.KeyAgreement { 105 k := &ka //nolint:gosec 106 keyAgreementID := ka.VerificationMethod.ID 107 108 if strings.HasPrefix(keyAgreementID, "#") { 109 keyAgreementID = didDoc.DIDDocument.ID + keyAgreementID 110 } 111 112 pubKey, err = extractKey(kid, keyAgreementID, k) 113 if err != nil { 114 return nil, err 115 } 116 } 117 118 return pubKey, nil 119 } 120 121 func extractKey(kid, keyAgreementID string, ka *did.Verification) (*cryptoapi.PublicKey, error) { 122 var ( 123 pubKey *cryptoapi.PublicKey 124 err error 125 ) 126 127 if strings.EqualFold(kid, keyAgreementID) { 128 switch ka.VerificationMethod.Type { 129 case x25519KeyAgreementKey2019: 130 pubKey, err = buildX25519Key(ka) 131 if err != nil { 132 return nil, fmt.Errorf("didDocResolver: %w", err) 133 } 134 case jsonWebKey2020: 135 pubKey, err = buildJWKKey(ka) 136 if err != nil { 137 return nil, fmt.Errorf("didDocResolver: %w", err) 138 } 139 default: 140 return nil, fmt.Errorf("didDocResolver: can't build key from KayAgreement with type: '%v'", 141 ka.VerificationMethod.Type) 142 } 143 } 144 145 return pubKey, nil 146 } 147 148 func buildX25519Key(ka *did.Verification) (*cryptoapi.PublicKey, error) { 149 pubKey := &cryptoapi.PublicKey{ 150 X: ka.VerificationMethod.Value, 151 Curve: "X25519", 152 Type: "OKP", 153 } 154 155 mPubKey, err := json.Marshal(pubKey) 156 if err != nil { 157 return nil, fmt.Errorf("buildX25519: marshal key error: %w", err) 158 } 159 160 x25519KMSKID, err := jwkkid.CreateKID(mPubKey, kms.X25519ECDHKWType) 161 if err != nil { 162 return nil, fmt.Errorf("buildX25519: createKID error:%w", err) 163 } 164 165 pubKey.KID = x25519KMSKID 166 167 return pubKey, nil 168 } 169 170 func buildJWKKey(ka *did.Verification) (*cryptoapi.PublicKey, error) { 171 var ( 172 x []byte 173 y []byte 174 kt kms.KeyType 175 ) 176 177 jwkKey := ka.VerificationMethod.JSONWebKey() 178 switch k := jwkKey.Key.(type) { 179 case *ecdsa.PublicKey: 180 x = k.X.Bytes() 181 y = k.Y.Bytes() 182 case []byte: 183 x = make([]byte, len(k)) 184 copy(x, k) 185 default: 186 return nil, fmt.Errorf("buildJWKKey: unsupported JWK format: (%T)", k) 187 } 188 189 pubKey := &cryptoapi.PublicKey{ 190 X: x, 191 Y: y, 192 Curve: jwkKey.Crv, 193 Type: jwkKey.Kty, 194 } 195 196 switch jwkKey.Crv { 197 case elliptic.P256().Params().Name: 198 kt = kms.NISTP256ECDHKWType 199 case elliptic.P384().Params().Name: 200 kt = kms.NISTP384ECDHKWType 201 case elliptic.P521().Params().Name: 202 kt = kms.NISTP521ECDHKWType 203 case "X25519": 204 kt = kms.X25519ECDHKWType 205 } 206 207 mPubKey, err := json.Marshal(pubKey) 208 if err != nil { 209 return nil, fmt.Errorf("buildJWKKey: marshal key error: %w", err) 210 } 211 212 jwkKMSKID, err := jwkkid.CreateKID(mPubKey, kt) 213 if err != nil { 214 return nil, fmt.Errorf("buildJWKKey: createKID error:%w", err) 215 } 216 217 pubKey.KID = jwkKMSKID 218 219 return pubKey, nil 220 }