github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packer/anoncrypt/pack.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package anoncrypt 8 9 import ( 10 "encoding/json" 11 "errors" 12 "fmt" 13 "strings" 14 15 "github.com/hyperledger/aries-framework-go/pkg/common/log" 16 cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto" 17 "github.com/hyperledger/aries-framework-go/pkg/didcomm/packer" 18 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" 19 "github.com/hyperledger/aries-framework-go/pkg/doc/jose" 20 "github.com/hyperledger/aries-framework-go/pkg/doc/jose/kid/resolver" 21 "github.com/hyperledger/aries-framework-go/pkg/kms" 22 ) 23 24 // Package anoncrypt includes a Packer implementation to build and parse JWE messages using Anoncrypt. It allows sending 25 // messages anonymously between parties with message repudiation, ie the sender identity is not revealed (and therefore 26 // not authenticated) to the recipient(s). 27 28 var logger = log.New("aries-framework/pkg/didcomm/packer/anoncrypt") 29 30 // Packer represents an Anoncrypt Pack/Unpacker that outputs/reads Aries envelopes. 31 type Packer struct { 32 kms kms.KeyManager 33 encAlg jose.EncAlg 34 cryptoService cryptoapi.Crypto 35 kidResolvers []resolver.KIDResolver 36 } 37 38 // New will create an Packer instance to 'AnonCrypt' payloads for a given list of recipients. 39 // The returned Packer contains all the information required to pack and unpack payloads. 40 func New(ctx packer.Provider, encAlg jose.EncAlg) (*Packer, error) { 41 k := ctx.KMS() 42 if k == nil { 43 return nil, errors.New("anoncrypt: failed to create packer because KMS is empty") 44 } 45 46 c := ctx.Crypto() 47 if c == nil { 48 return nil, errors.New("anoncrypt: failed to create packer because crypto service is empty") 49 } 50 51 vdrReg := ctx.VDRegistry() 52 if vdrReg == nil { 53 return nil, errors.New("anoncrypt: failed to create packer because vdr registry is empty") 54 } 55 56 var kidResolvers []resolver.KIDResolver 57 58 kidResolvers = append(kidResolvers, &resolver.DIDKeyResolver{}, &resolver.DIDDocResolver{VDRRegistry: vdrReg}) 59 60 return &Packer{ 61 kms: k, 62 encAlg: encAlg, 63 cryptoService: c, 64 kidResolvers: kidResolvers, 65 }, nil 66 } 67 68 // Pack will encode the payload argument using the protocol defined by the Anoncrypt message of Aries RFC 0334. 69 // Anoncrypt ignores the sender argument, it's added to meet the Packer interface. It uses DIDComm typ V2 header value 70 // (default envelope 'typ' protected header). 71 func (p *Packer) Pack(contentType string, payload, _ []byte, recipientsPubKeys [][]byte) ([]byte, error) { 72 if len(recipientsPubKeys) == 0 { 73 return nil, fmt.Errorf("anoncrypt Pack: empty recipientsPubKeys") 74 } 75 76 recECKeys, aad, err := unmarshalRecipientKeys(recipientsPubKeys) 77 if err != nil { 78 return nil, fmt.Errorf("anoncrypt Pack: failed to convert recipient keys: %w", err) 79 } 80 81 jweEncrypter, err := jose.NewJWEEncrypt(p.encAlg, p.EncodingType(), contentType, "", 82 nil, recECKeys, p.cryptoService) 83 if err != nil { 84 return nil, fmt.Errorf("anoncrypt Pack: failed to new JWEEncrypt instance: %w", err) 85 } 86 87 jwe, err := jweEncrypter.EncryptWithAuthData(payload, aad) 88 if err != nil { 89 return nil, fmt.Errorf("anoncrypt Pack: failed to encrypt payload: %w", err) 90 } 91 92 mPh, err := json.Marshal(jwe.ProtectedHeaders) 93 if err != nil { 94 return nil, fmt.Errorf("anoncrypt Pack: %w", err) 95 } 96 97 logger.Debugf("protected headers: %s", mPh) 98 99 var s string 100 101 if len(recipientsPubKeys) == 1 { 102 s, err = jwe.CompactSerialize(json.Marshal) 103 } else { 104 s, err = jwe.FullSerialize(json.Marshal) 105 } 106 107 if err != nil { 108 return nil, fmt.Errorf("anoncrypt Pack: failed to serialize JWE message: %w", err) 109 } 110 111 return []byte(s), nil 112 } 113 114 func unmarshalRecipientKeys(keys [][]byte) ([]*cryptoapi.PublicKey, []byte, error) { 115 var ( 116 pubKeys []*cryptoapi.PublicKey 117 aad []byte 118 ) 119 120 for _, key := range keys { 121 var ecKey *cryptoapi.PublicKey 122 123 err := json.Unmarshal(key, &ecKey) 124 if err != nil { 125 return nil, nil, err 126 } 127 128 pubKeys = append(pubKeys, ecKey) 129 } 130 131 return pubKeys, aad, nil 132 } 133 134 // Unpack will decode the envelope using a standard format. 135 func (p *Packer) Unpack(envelope []byte) (*transport.Envelope, error) { 136 // TODO validate incoming `typ` and `cty` values 137 jwe, _, _, err := deserializeEnvelope(envelope) 138 if err != nil { 139 return nil, fmt.Errorf("failed to deserialize JWE envelope: %w", err) 140 } 141 142 for i := range jwe.Recipients { 143 recKey, recKID, err := p.pubKey(i, jwe) 144 if err != nil { 145 return nil, fmt.Errorf("anoncrypt Unpack: %w", err) 146 } 147 148 // verify recKey.KID is found in kms to prove ownership of key. 149 _, err = p.kms.Get(recKey.KID) 150 if err != nil { 151 if errors.Is(err, kms.ErrKeyNotFound) { 152 retriesMsg := "" 153 154 if i < len(jwe.Recipients) { 155 retriesMsg = ", will try another recipient" 156 } 157 158 logger.Debugf("anoncrypt Unpack: recipient keyID not found in KMS: %v%s", recKey.KID, retriesMsg) 159 160 continue 161 } 162 163 return nil, fmt.Errorf("anoncrypt Unpack: failed to get key from kms: %w", err) 164 } 165 166 jweDecrypter := jose.NewJWEDecrypt(p.kidResolvers, p.cryptoService, p.kms) 167 168 pt, err := jweDecrypter.Decrypt(jwe) 169 if err != nil { 170 return nil, fmt.Errorf("anoncrypt Unpack: failed to decrypt JWE envelope: %w", err) 171 } 172 173 // set original recKID in recKey to keep original source of key (ie not the KMS kid) 174 recKey.KID = recKID 175 176 ecdhesPubKeyByes, err := json.Marshal(recKey) 177 if err != nil { 178 return nil, fmt.Errorf("anoncrypt Unpack: failed to marshal public key: %w", err) 179 } 180 181 return &transport.Envelope{ 182 Message: pt, 183 ToKey: ecdhesPubKeyByes, 184 }, nil 185 } 186 187 return nil, fmt.Errorf("anoncrypt Unpack: no matching recipient in envelope") 188 } 189 190 func deserializeEnvelope(envelope []byte) (*jose.JSONWebEncryption, string, string, error) { 191 jwe, err := jose.Deserialize(string(envelope)) 192 if err != nil { 193 return nil, "", "", fmt.Errorf("anoncrypt Unpack: failed to deserialize JWE message: %w", err) 194 } 195 196 typ, _ := jwe.ProtectedHeaders.Type() 197 cty, _ := jwe.ProtectedHeaders.ContentType() 198 199 return jwe, typ, cty, nil 200 } 201 202 // pubKey will resolve recipient kid at i and returns the corresponding full public key with KID as the kms KID value. 203 func (p *Packer) pubKey(i int, jwe *jose.JSONWebEncryption) (*cryptoapi.PublicKey, string, error) { 204 var ( 205 kid string 206 kidResolver resolver.KIDResolver 207 ) 208 209 if i == 0 && len(jwe.Recipients) == 1 { // compact serialization, recipient headers are in jwe.ProtectedHeaders 210 var ok bool 211 212 kid, ok = jwe.ProtectedHeaders.KeyID() 213 if !ok { 214 return nil, "", fmt.Errorf("single recipient missing 'KID' in jwe.ProtectHeaders") 215 } 216 } else { 217 kid = jwe.Recipients[i].Header.KID 218 } 219 220 keySource := "did:key" 221 222 switch { 223 case strings.HasPrefix(kid, keySource): 224 kidResolver = p.kidResolvers[0] 225 case strings.Index(kid, "#") > 0: 226 kidResolver = p.kidResolvers[1] 227 keySource = "didDoc.KeyAgreement[].VerificationMethod.ID" 228 default: 229 return nil, "", fmt.Errorf("invalid kid format, must be a did:key or a DID doc verificaitonMethod ID") 230 } 231 232 // recipient kid header is the did:Key or KeyAgreement.ID, extract the public key and build a kms kid 233 recKey, err := kidResolver.Resolve(kid) 234 if err != nil { 235 return nil, "", fmt.Errorf("failed to resolve recipient key from %s value: %w", keySource, err) 236 } 237 238 return recKey, kid, nil 239 } 240 241 // EncodingType for didcomm. 242 func (p *Packer) EncodingType() string { 243 return transport.MediaTypeV2EncryptedEnvelope 244 }