github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packer/authcrypt/pack.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package authcrypt 8 9 import ( 10 "encoding/json" 11 "errors" 12 "fmt" 13 "strings" 14 15 "github.com/google/tink/go/keyset" 16 17 "github.com/hyperledger/aries-framework-go/pkg/common/log" 18 cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto" 19 "github.com/hyperledger/aries-framework-go/pkg/didcomm/packer" 20 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" 21 "github.com/hyperledger/aries-framework-go/pkg/doc/jose" 22 "github.com/hyperledger/aries-framework-go/pkg/doc/jose/kid/resolver" 23 "github.com/hyperledger/aries-framework-go/pkg/kms" 24 ) 25 26 // Package authcrypt includes a Packer implementation to build and parse JWE messages using Authcrypt. It allows sending 27 // messages between parties with non-repudiation messages, ie the sender identity is revealed (and therefore 28 // authenticated) to the recipient(s). The assumption of using this package is that public keys exchange has previously 29 // occurred between the sender and the recipient(s). 30 31 var logger = log.New("aries-framework/pkg/didcomm/packer/authcrypt") 32 33 // Packer represents an Authcrypt Pack/Unpacker that outputs/reads Aries envelopes. 34 type Packer struct { 35 kms kms.KeyManager 36 encAlg jose.EncAlg 37 cryptoService cryptoapi.Crypto 38 kidResolvers []resolver.KIDResolver 39 } 40 41 // New will create a Packer instance to 'AuthCrypt' payloads for a given sender and list of recipients keys using 42 // DIDComm typ V2 value (default envelope 'typ' protected header). 43 // It opens thirdPartyKS store (or fetch cached one) that contains third party keys. This store must be 44 // pre-populated with the sender key required by a recipient to Unpack a JWE envelope. It is not needed by the sender 45 // (as the sender packs the envelope with its own key). 46 // The returned Packer contains all the information required to pack and unpack payloads. 47 func New(ctx packer.Provider, encAlg jose.EncAlg) (*Packer, error) { 48 err := validateEncAlg(encAlg) 49 if err != nil { 50 return nil, fmt.Errorf("authcrypt: %w", err) 51 } 52 53 k := ctx.KMS() 54 if k == nil { 55 return nil, errors.New("authcrypt: failed to create packer because KMS is empty") 56 } 57 58 c := ctx.Crypto() 59 if c == nil { 60 return nil, errors.New("authcrypt: failed to create packer because crypto service is empty") 61 } 62 63 vdrReg := ctx.VDRegistry() 64 if vdrReg == nil { 65 return nil, errors.New("authcrypt: failed to create packer because vdr registry is empty") 66 } 67 68 var kidResolvers []resolver.KIDResolver 69 70 kidResolvers = append(kidResolvers, &resolver.DIDKeyResolver{}, &resolver.DIDDocResolver{VDRRegistry: vdrReg}) 71 72 return &Packer{ 73 kms: k, 74 encAlg: encAlg, 75 cryptoService: c, 76 kidResolvers: kidResolvers, 77 }, nil 78 } 79 80 func validateEncAlg(alg jose.EncAlg) error { 81 switch alg { 82 // authcrypt supports AES-CBC+HMAC-SHA algorithms only, anoncrypt supports the same and AES256-GCM. 83 case jose.A128CBCHS256, jose.A192CBCHS384ALG, jose.A256CBCHS384, jose.A256CBCHS512, jose.XC20P: 84 return nil 85 default: 86 return fmt.Errorf("unsupported content encrytpion algorithm: %v", alg) 87 } 88 } 89 90 // Pack will encode the payload argument with contentType argument 91 // Using the protocol defined by the Authcrypt message of Aries RFC 0334 92 // with the following arguments: 93 // payload: the payload message that will be protected 94 // senderID: the key id of the sender (stored in the KMS) 95 // recipientsPubKeys: public keys. 96 func (p *Packer) Pack( // nolint:gocyclo 97 contentType string, payload, senderID []byte, recipientsPubKeys [][]byte) ([]byte, error) { 98 if len(recipientsPubKeys) == 0 { 99 return nil, fmt.Errorf("authcrypt Pack: empty recipientsPubKeys") 100 } 101 102 recECKeys, aad, err := unmarshalRecipientKeys(recipientsPubKeys) 103 if err != nil { 104 return nil, fmt.Errorf("authcrypt Pack: failed to convert recipient keys: %w", err) 105 } 106 107 senderKID := string(senderID) 108 skid := senderKID 109 110 if idx := strings.Index(senderKID, "."); idx > 0 { 111 senderKID = senderKID[:idx] // senderKID is the kms kid. 112 skid = skid[idx+1:] // skid as did:key or didDoc.KeyAgreement[].VerificationMethod.ID value. 113 } 114 115 kh, err := p.kms.Get(senderKID) 116 if err != nil { 117 return nil, fmt.Errorf("authcrypt Pack: failed to get sender key from KMS: %w", err) 118 } 119 120 sKH, ok := kh.(*keyset.Handle) 121 if !ok { 122 sKH = nil 123 } 124 125 jweEncrypter, err := jose.NewJWEEncrypt(p.encAlg, p.EncodingType(), contentType, skid, 126 sKH, recECKeys, p.cryptoService) 127 if err != nil { 128 return nil, fmt.Errorf("authcrypt Pack: failed to new JWEEncrypt instance: %w", err) 129 } 130 131 jwe, err := jweEncrypter.EncryptWithAuthData(payload, aad) 132 if err != nil { 133 return nil, fmt.Errorf("authcrypt Pack: failed to encrypt payload: %w", err) 134 } 135 136 mPh, err := json.Marshal(jwe.ProtectedHeaders) 137 if err != nil { 138 return nil, fmt.Errorf("authcrypt Pack: %w", err) 139 } 140 141 logger.Debugf("protected headers: %s", mPh) 142 143 var s string 144 145 if len(recipientsPubKeys) == 1 { 146 s, err = jwe.CompactSerialize(json.Marshal) 147 } else { 148 s, err = jwe.FullSerialize(json.Marshal) 149 } 150 151 if err != nil { 152 return nil, fmt.Errorf("authcrypt Pack: failed to serialize JWE message: %w", err) 153 } 154 155 return []byte(s), nil 156 } 157 158 func unmarshalRecipientKeys(keys [][]byte) ([]*cryptoapi.PublicKey, []byte, error) { 159 var ( 160 pubKeys []*cryptoapi.PublicKey 161 aad []byte 162 ) 163 164 for _, key := range keys { 165 var ecKey *cryptoapi.PublicKey 166 167 err := json.Unmarshal(key, &ecKey) 168 if err != nil { 169 return nil, nil, err 170 } 171 172 pubKeys = append(pubKeys, ecKey) 173 } 174 175 return pubKeys, aad, nil 176 } 177 178 // Unpack will decode the envelope using a standard format. Using either did:key KID resolver or the 179 // DIDDoc.KeyAgreement[].VerificationMethod.ID resolver which are set in p.resolvers. 180 func (p *Packer) Unpack(envelope []byte) (*transport.Envelope, error) { 181 // TODO validate `typ` and `cty` values 182 jwe, _, _, err := deserializeEnvelope(envelope) 183 if err != nil { 184 return nil, fmt.Errorf("failed to deserialize envelope: %w", err) 185 } 186 187 for i := range jwe.Recipients { 188 var ( 189 recKey *cryptoapi.PublicKey 190 pt []byte 191 recKID string 192 env *transport.Envelope 193 ) 194 195 recKey, recKID, err = p.pubKey(i, jwe) 196 if err != nil { 197 return nil, fmt.Errorf("authcrypt Unpack: %w", err) 198 } 199 200 // verify recKey.KID is found in kms to prove ownership of key. 201 _, err = p.kms.Get(recKey.KID) 202 if err != nil { 203 if errors.Is(err, kms.ErrKeyNotFound) { 204 retriesMsg := "" 205 206 if i < len(jwe.Recipients) { 207 retriesMsg = ", will try another recipient" 208 } 209 210 logger.Debugf("authcrypt Unpack: recipient keyID not found in KMS: %v%s", recKey.KID, retriesMsg) 211 212 continue 213 } 214 215 return nil, fmt.Errorf("authcrypt Unpack: failed to get key from kms: %w", err) 216 } 217 218 jweDecrypter := jose.NewJWEDecrypt(p.kidResolvers, p.cryptoService, p.kms) 219 220 pt, err = jweDecrypter.Decrypt(jwe) 221 if err != nil { 222 return nil, fmt.Errorf("authcrypt Unpack: failed to decrypt JWE envelope: %w", err) 223 } 224 225 env, err = p.buildEnvelope(recKey, recKID, pt, jwe) 226 if err != nil { 227 return nil, fmt.Errorf("authcrypt Unpack: %w", err) 228 } 229 230 return env, nil 231 } 232 233 return nil, fmt.Errorf("authcrypt Unpack: no matching recipient in envelope") 234 } 235 236 func (p *Packer) buildEnvelope(recKey *cryptoapi.PublicKey, recKID string, message []byte, 237 jwe *jose.JSONWebEncryption) (*transport.Envelope, error) { 238 // set original recKID in recKey to keep original source of key (ie not the KMS kid) 239 recKey.KID = recKID 240 241 ecdh1puPubKeyByes, err := json.Marshal(recKey) 242 if err != nil { 243 return nil, fmt.Errorf("buildEnvelope: failed to marshal recipient public key: %w", err) 244 } 245 246 mSenderPubKey, err := p.extractSenderKey(jwe) 247 if err != nil { 248 return nil, err 249 } 250 251 return &transport.Envelope{ 252 Message: message, 253 FromKey: mSenderPubKey, 254 ToKey: ecdh1puPubKeyByes, 255 }, nil 256 } 257 258 func (p *Packer) extractSenderKey(jwe *jose.JSONWebEncryption) ([]byte, error) { 259 var ( 260 senderKey *cryptoapi.PublicKey 261 mSenderPubKey []byte 262 err error 263 ) 264 265 skidHeader, ok := jwe.ProtectedHeaders["skid"] 266 if ok { //nolint:nestif 267 skid, ok := skidHeader.(string) 268 if ok { 269 for _, r := range p.kidResolvers { 270 senderKey, err = r.Resolve(skid) 271 if err != nil { 272 logger.Debugf("authcrypt Unpack: unpack successful, but resolving sender key failed [%v] "+ 273 "using %T resolver, skipping it.", err.Error(), r) 274 } 275 276 if senderKey != nil { 277 logger.Debugf("authcrypt Unpack: unpack successful with resolving sender key success "+ 278 "using %T resolver, will be using resolved senderKey for skid: %v", r, skid) 279 break 280 } 281 } 282 283 if senderKey != nil { 284 // original senderKey.KID is a kms kid. The agent unpacking the envelope doesn't have this kid in kms. 285 // Set value as skid to keep trace of the origin of the key (didDoc.keyAgreement[].VerificationMehod.ID) 286 senderKey.KID = skid 287 mSenderPubKey, err = json.Marshal(senderKey) 288 289 if err != nil { 290 return nil, fmt.Errorf("authcrypt Unpack: failed to marshal sender public key: %w", err) 291 } 292 } else { 293 logger.Debugf("authcrypt Unpack: senderKey not resolved, skipping FromKey in envelope") 294 } 295 } 296 } 297 298 return mSenderPubKey, nil 299 } 300 301 func deserializeEnvelope(envelope []byte) (*jose.JSONWebEncryption, string, string, error) { 302 jwe, err := jose.Deserialize(string(envelope)) 303 if err != nil { 304 return nil, "", "", fmt.Errorf("authcrypt Unpack: failed to deserialize JWE message: %w", err) 305 } 306 307 typ, _ := jwe.ProtectedHeaders.Type() 308 cty, _ := jwe.ProtectedHeaders.ContentType() 309 310 return jwe, typ, cty, nil 311 } 312 313 // pubKey will resolve recipient kid at i and returns the corresponding full public key with KID as the kms KID value. 314 func (p *Packer) pubKey(i int, jwe *jose.JSONWebEncryption) (*cryptoapi.PublicKey, string, error) { 315 var ( 316 kid string 317 kidResolver resolver.KIDResolver 318 ) 319 320 if i == 0 && len(jwe.Recipients) == 1 { // compact serialization, recipient headers are in jwe.ProtectedHeaders 321 var ok bool 322 323 kid, ok = jwe.ProtectedHeaders.KeyID() 324 if !ok { 325 return nil, "", fmt.Errorf("single recipient missing 'KID' in jwe.ProtectHeaders") 326 } 327 } else { 328 kid = jwe.Recipients[i].Header.KID 329 } 330 331 keySource := "did:key" 332 333 switch { 334 case strings.HasPrefix(kid, keySource): 335 kidResolver = p.kidResolvers[0] 336 case strings.Index(kid, "#") > 0: 337 kidResolver = p.kidResolvers[1] 338 keySource = "didDoc.KeyAgreement[].VerificationMethod.ID" 339 default: 340 return nil, "", fmt.Errorf("invalid kid format, must be a did:key or a DID doc verificaitonMethod ID") 341 } 342 343 // recipient kid header is the did:Key or KeyAgreement.ID, extract the public key and build a kms kid. 344 recKey, err := kidResolver.Resolve(kid) 345 if err != nil { 346 return nil, "", fmt.Errorf("failed to resolve recipient key from %s value: %w", keySource, err) 347 } 348 349 return recKey, kid, nil 350 } 351 352 // EncodingType for didcomm. 353 func (p *Packer) EncodingType() string { 354 return transport.MediaTypeV2EncryptedEnvelope 355 }