github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packager/packager.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 packager 9 10 import ( 11 "bytes" 12 "encoding/base64" 13 "encoding/json" 14 "errors" 15 "fmt" 16 "strings" 17 18 "github.com/btcsuite/btcutil/base58" 19 20 "github.com/hyperledger/aries-framework-go/pkg/common/log" 21 "github.com/hyperledger/aries-framework-go/pkg/crypto" 22 "github.com/hyperledger/aries-framework-go/pkg/didcomm/packer" 23 "github.com/hyperledger/aries-framework-go/pkg/didcomm/packer/authcrypt" 24 legacyAuthCrypt "github.com/hyperledger/aries-framework-go/pkg/didcomm/packer/legacy/authcrypt" 25 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" 26 "github.com/hyperledger/aries-framework-go/pkg/doc/did" 27 "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" 28 "github.com/hyperledger/aries-framework-go/pkg/doc/util/jwkkid" 29 "github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey" 30 "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" 31 "github.com/hyperledger/aries-framework-go/pkg/kms" 32 ) 33 34 const ( 35 authSuffix = "-authcrypt" 36 jsonWebKey2020 = "JsonWebKey2020" 37 x25519KeyAgreementKey2019 = "X25519KeyAgreementKey2019" 38 ) 39 40 var logger = log.New("aries-framework/pkg/didcomm/packager") 41 42 // Provider contains dependencies for the base packager and is typically created by using aries.Context(). 43 type Provider interface { 44 Packers() []packer.Packer 45 PrimaryPacker() packer.Packer 46 VDRegistry() vdr.Registry 47 } 48 49 // Creator method to create new packager service. 50 type Creator func(prov Provider) (transport.Packager, error) 51 52 // Packager is the basic implementation of Packager. 53 type Packager struct { 54 primaryPacker packer.Packer 55 packers map[string]packer.Packer 56 vdrRegistry vdr.Registry 57 } 58 59 // PackerCreator holds a creator function for a Packer and the name of the Packer's encoding method. 60 type PackerCreator struct { 61 PackerName string 62 Creator packer.Creator 63 } 64 65 // New return new instance of Packager implementation of transport.Packager. 66 func New(ctx Provider) (*Packager, error) { 67 basePackager := Packager{ 68 primaryPacker: nil, 69 packers: map[string]packer.Packer{}, 70 vdrRegistry: ctx.VDRegistry(), 71 } 72 73 for _, packerType := range ctx.Packers() { 74 basePackager.addPacker(packerType) 75 } 76 77 basePackager.primaryPacker = ctx.PrimaryPacker() 78 if basePackager.primaryPacker == nil { 79 return nil, fmt.Errorf("need primary packer to initialize packager") 80 } 81 82 basePackager.addPacker(basePackager.primaryPacker) 83 84 return &basePackager, nil 85 } 86 87 func (bp *Packager) addPacker(pack packer.Packer) { 88 packerID := pack.EncodingType() 89 90 _, isAuthCrypt := pack.(*authcrypt.Packer) 91 _, isLegacyAuthCrypt := pack.(*legacyAuthCrypt.Packer) 92 93 if isAuthCrypt || isLegacyAuthCrypt { 94 // anoncrypt and authcrypt have the same encoding type 95 // so authcrypt will have an appended suffix 96 packerID += authSuffix 97 } 98 99 if bp.packers[packerID] == nil { 100 bp.packers[packerID] = pack 101 } 102 } 103 104 // PackMessage Pack a message for one or more recipients. 105 func (bp *Packager) PackMessage(messageEnvelope *transport.Envelope) ([]byte, error) { 106 if messageEnvelope == nil { 107 return nil, errors.New("packMessage: envelope argument is nil") 108 } 109 110 cty, p, err := bp.getCTYAndPacker(messageEnvelope) 111 if err != nil { 112 return nil, fmt.Errorf("packMessage: %w", err) 113 } 114 115 senderKey, recipients, err := bp.prepareSenderAndRecipientKeys(cty, messageEnvelope) 116 if err != nil { 117 return nil, fmt.Errorf("packMessage: %w", err) 118 } 119 120 marshalledEnvelope, err := p.Pack(cty, messageEnvelope.Message, senderKey, recipients) 121 if err != nil { 122 return nil, fmt.Errorf("packMessage: failed to pack: %w", err) 123 } 124 125 return marshalledEnvelope, nil 126 } 127 128 //nolint:funlen,gocyclo,gocognit 129 func (bp *Packager) prepareSenderAndRecipientKeys(cty string, envelope *transport.Envelope) ([]byte, [][]byte, error) { 130 var recipients [][]byte 131 132 isLegacy := isMediaTypeForLegacyPacker(cty) 133 134 for i, receiverKeyID := range envelope.ToKeys { 135 switch { 136 case strings.HasPrefix(receiverKeyID, "did:key"): 137 marshalledKey, err := addDIDKeyToRecipients(i, receiverKeyID, isLegacy) 138 if err != nil { 139 return nil, nil, err 140 } 141 142 recipients = append(recipients, marshalledKey) 143 case strings.Index(receiverKeyID, "#") > 0: 144 receiverKey, err := bp.resolveKeyAgreementFromDIDDoc(receiverKeyID) 145 if err != nil { 146 return nil, nil, fmt.Errorf("prepareSenderAndRecipientKeys: for recipient %d: %w", i+1, err) 147 } 148 149 if isLegacy { 150 recipients = append(recipients, receiverKey.X) 151 } else { 152 marshalledKey, err := json.Marshal(receiverKey) 153 if err != nil { 154 return nil, nil, fmt.Errorf("prepareSenderAndRecipientKeys: marshal recipient key %d: %w", i+1, err) 155 } 156 157 recipients = append(recipients, marshalledKey) 158 } 159 case cty == transport.LegacyDIDCommV1Profile: 160 recipients = append(recipients, base58.Decode(receiverKeyID)) 161 default: 162 recipients = append(recipients, []byte(receiverKeyID)) 163 } 164 } 165 166 var senderKID []byte 167 168 switch { 169 case strings.HasPrefix(string(envelope.FromKey), "did:key"): 170 senderKey, err := kmsdidkey.EncryptionPubKeyFromDIDKey(string(envelope.FromKey)) 171 if err != nil { 172 return nil, nil, fmt.Errorf("prepareSenderAndRecipientKeys: failed to extract pubKeyBytes from "+ 173 "senderVerKey: %w", err) 174 } 175 176 if isLegacy { 177 senderKID = senderKey.X // for legacy, use the sender raw key (Ed25519 key) 178 } else { 179 senderKID = buildSenderKID(senderKey, envelope) 180 } 181 //nolint:gocritic // need to check with strings not bytes 182 case strings.Index(string(envelope.FromKey), "#") > 0: 183 senderKey, err := bp.resolveKeyAgreementFromDIDDoc(string(envelope.FromKey)) 184 if err != nil { 185 return nil, nil, fmt.Errorf("prepareSenderAndRecipientKeys: for sender: %w", err) 186 } 187 188 if isLegacy { 189 senderKID = senderKey.X 190 } else { 191 marshalledSenderKey, err := json.Marshal(senderKey) 192 if err != nil { 193 return nil, nil, fmt.Errorf("prepareSenderAndRecipientKeys: marshal sender key: %w", err) 194 } 195 196 senderKMSKID, err := jwkkid.CreateKID(marshalledSenderKey, getKMSKeyType(senderKey.Type, senderKey.Curve)) 197 if err != nil { 198 return nil, nil, fmt.Errorf("prepareSenderAndRecipientKeys: for sender KMS KID: %w", err) 199 } 200 201 senderKey.KID = senderKMSKID 202 203 senderKID = buildSenderKID(senderKey, envelope) 204 } 205 default: 206 senderKID = envelope.FromKey 207 } 208 209 return senderKID, recipients, nil 210 } 211 212 func addDIDKeyToRecipients(i int, receiverKey string, isLegacy bool) ([]byte, error) { 213 recKey, err := kmsdidkey.EncryptionPubKeyFromDIDKey(receiverKey) 214 if err != nil { 215 return nil, fmt.Errorf("prepareSenderAndRecipientKeys: failed to parse public key bytes from "+ 216 "did:key verKey for recipient %d: %w", i+1, err) 217 } 218 219 if isLegacy { 220 return recKey.X, nil 221 } 222 223 var marshalledKey []byte 224 225 // for packing purposes, recipient kid header is the did:key value, the packers must parse it and extract 226 // the kms kid value during unpack. 227 recKey.KID = receiverKey 228 229 marshalledKey, err = json.Marshal(recKey) 230 if err != nil { 231 return nil, fmt.Errorf("prepareSenderAndRecipientKeys: for recipient %d did:key marshal: %w", i+1, err) 232 } 233 234 return marshalledKey, nil 235 } 236 237 func getKMSKeyType(keyType, curve string) kms.KeyType { 238 switch keyType { 239 case "EC": 240 switch curve { 241 case "P-256": 242 return kms.NISTP256ECDHKWType 243 case "P-384": 244 return kms.NISTP384ECDHKWType 245 case "P-521": 246 return kms.NISTP521ECDHKWType 247 } 248 case "OKP": 249 return kms.X25519ECDHKWType 250 } 251 252 return "" 253 } 254 255 func isMediaTypeForLegacyPacker(cty string) bool { 256 var isLegacy bool 257 258 switch cty { 259 case transport.MediaTypeRFC0019EncryptedEnvelope, transport.MediaTypeAIP2RFC0019Profile, 260 transport.MediaTypeProfileDIDCommAIP1, transport.LegacyDIDCommV1Profile: 261 isLegacy = true 262 default: 263 isLegacy = false 264 } 265 266 return isLegacy 267 } 268 269 func buildSenderKID(senderPubKey *crypto.PublicKey, envelopeSenderKey *transport.Envelope) []byte { 270 // Authcrypt/Anoncrypt for DIDComm V2 only require the sender KID as senderKey. Its value is: 271 // "kms kid value"."kid did:key value" without the double-quotes. The packer should parse it, then use kms kid value 272 // to fetch the key from kms and use the did:key or KeyAgreement.ID value as the 'skid' header. 273 senderKey := []byte(senderPubKey.KID + ".") 274 senderKey = append(senderKey, envelopeSenderKey.FromKey...) 275 276 return senderKey 277 } 278 279 type envelopeStub struct { 280 Protected string `json:"protected,omitempty"` 281 } 282 283 type headerStub struct { 284 Type string `json:"typ,omitempty"` 285 SKID string `json:"skid,omitempty"` 286 Alg string `json:"alg,omitempty"` 287 } 288 289 //nolint:funlen, gocyclo 290 func getEncodingType(encMessage []byte) (string, []byte, error) { 291 var b64DecodedMessage []byte 292 293 env := &envelopeStub{} 294 295 //nolint:nestif 296 if strings.HasPrefix(string(encMessage), "{") { // full serialized 297 err := json.Unmarshal(encMessage, env) 298 if err != nil { 299 return "", nil, fmt.Errorf("parse envelope: %w", err) 300 } 301 } else { 302 doubleQuote := []byte("\"") 303 304 // packed message is base64 encoded and double-quoted. 305 if bytes.HasPrefix(encMessage, doubleQuote) && bytes.HasSuffix(encMessage, doubleQuote) { 306 msg := string(encMessage[1 : len(encMessage)-1]) 307 var encodedEnvelope []byte 308 309 protBytes1, err1 := base64.URLEncoding.DecodeString(msg) 310 protBytes2, err2 := base64.RawURLEncoding.DecodeString(msg) 311 312 switch { 313 case err1 == nil: 314 encodedEnvelope = protBytes1 315 case err2 == nil: 316 encodedEnvelope = protBytes2 317 default: 318 return "", nil, fmt.Errorf("decode wrapped header: URLEncoding error: %w, RawURLEncoding error: %v", 319 err1, err2) 320 } 321 322 if bytes.HasPrefix(encodedEnvelope, []byte("{")) { 323 err := json.Unmarshal(encodedEnvelope, env) 324 if err != nil { 325 return "", nil, fmt.Errorf("parse wrapped envelope: %w", err) 326 } 327 } else { // compact serialized 328 env.Protected = strings.Split(string(encodedEnvelope), ".")[0] 329 } 330 331 b64DecodedMessage = encodedEnvelope 332 } else { // compact serialized 333 env.Protected = strings.Split(string(encMessage), ".")[0] 334 } 335 } 336 337 var protBytes []byte 338 339 protBytes1, err1 := base64.URLEncoding.DecodeString(env.Protected) 340 protBytes2, err2 := base64.RawURLEncoding.DecodeString(env.Protected) 341 342 switch { 343 case err1 == nil: 344 protBytes = protBytes1 345 case err2 == nil: 346 protBytes = protBytes2 347 default: 348 return "", nil, fmt.Errorf("decode header: URLEncoding error: %w, RawURLEncoding error: %v", err1, err2) 349 } 350 351 prot := &headerStub{} 352 353 err := json.Unmarshal(protBytes, prot) 354 if err != nil { 355 return "", nil, fmt.Errorf("parse header: %w", err) 356 } 357 358 packerID := prot.Type 359 360 if prot.SKID != "" || prot.Alg == "Authcrypt" { 361 // since Type protected header is the same for authcrypt and anoncrypt, the differentiating factor is SKID. 362 // If it is present, then it's authcrypt. 363 packerID += authSuffix 364 } 365 366 return packerID, b64DecodedMessage, nil 367 } 368 369 // UnpackMessage Unpack a message. 370 func (bp *Packager) UnpackMessage(encMessage []byte) (*transport.Envelope, error) { 371 encType, b64DecodedMessage, err := getEncodingType(encMessage) 372 if err != nil { 373 return nil, fmt.Errorf("getEncodingType: %w", err) 374 } 375 376 p, ok := bp.packers[encType] 377 if !ok { 378 return nil, fmt.Errorf("message Type not recognized") 379 } 380 381 if len(b64DecodedMessage) > 0 { 382 encMessage = b64DecodedMessage 383 } 384 385 envelope, err := p.Unpack(encMessage) 386 if err != nil { 387 return nil, fmt.Errorf("unpack: %w", err) 388 } 389 390 return envelope, nil 391 } 392 393 func (bp *Packager) getCTYAndPacker(envelope *transport.Envelope) (string, packer.Packer, error) { 394 switch envelope.MediaTypeProfile { 395 case transport.MediaTypeAIP2RFC0019Profile, transport.MediaTypeProfileDIDCommAIP1: 396 packerName := addAuthcryptSuffix(envelope.FromKey, transport.MediaTypeRFC0019EncryptedEnvelope) 397 398 return transport.MediaTypeRFC0019EncryptedEnvelope, bp.packers[packerName], nil 399 case transport.MediaTypeRFC0019EncryptedEnvelope, transport.LegacyDIDCommV1Profile: 400 packerName := addAuthcryptSuffix(envelope.FromKey, transport.MediaTypeRFC0019EncryptedEnvelope) 401 402 return envelope.MediaTypeProfile, bp.packers[packerName], nil 403 case transport.MediaTypeV2EncryptedEnvelope, transport.MediaTypeV2PlaintextPayload, 404 transport.MediaTypeAIP2RFC0587Profile, transport.MediaTypeDIDCommV2Profile: 405 packerName := addAuthcryptSuffix(envelope.FromKey, transport.MediaTypeV2EncryptedEnvelope) 406 407 return transport.MediaTypeV2PlaintextPayload, bp.packers[packerName], nil 408 case transport.MediaTypeV2EncryptedEnvelopeV1PlaintextPayload, transport.MediaTypeV1PlaintextPayload: 409 packerName := addAuthcryptSuffix(envelope.FromKey, transport.MediaTypeV2EncryptedEnvelope) 410 411 return transport.MediaTypeV1PlaintextPayload, bp.packers[packerName], nil 412 default: 413 // use primaryPacker if mediaProfile not registered. 414 if bp.primaryPacker != nil { 415 return bp.primaryPacker.EncodingType(), bp.primaryPacker, nil 416 } 417 } 418 419 // this should never happen since outbound calls use the framework's default media type profile (unless the default 420 // was overridden with an empty value during framework instance creation). 421 return "", nil, fmt.Errorf("no packer found for mediatype profile: '%v'", envelope.MediaTypeProfile) 422 } 423 424 func addAuthcryptSuffix(fromKey []byte, packerName string) string { 425 if len(fromKey) > 0 { 426 packerName += authSuffix 427 } 428 429 return packerName 430 } 431 432 func (bp *Packager) resolveKeyAgreementFromDIDDoc(keyAgrID string) (*crypto.PublicKey, error) { 433 i := strings.Index(keyAgrID, "#") 434 435 keyAgrDID := keyAgrID[:i] 436 keyAgrFragment := keyAgrID[i+1:] 437 438 docResolution, err := bp.vdrRegistry.Resolve(keyAgrDID) 439 if err != nil { 440 return nil, fmt.Errorf("resolveKeyAgreementFromDIDDoc: for recipient DID doc resolution %w", err) 441 } 442 443 for j, ka := range docResolution.DIDDocument.KeyAgreement { 444 kaID := ka.VerificationMethod.ID[strings.Index(ka.VerificationMethod.ID, "#")+1:] 445 if strings.EqualFold(kaID, keyAgrFragment) { 446 return marshalKeyFromVerificationMethod(keyAgrID, &ka.VerificationMethod, j) 447 } 448 449 logger.Debugf("skipping keyID %s since it's not found in didDoc.KeyAgreement of did %s", kaID, keyAgrDID) 450 } 451 452 for j := range docResolution.DIDDocument.VerificationMethod { 453 vm := &docResolution.DIDDocument.VerificationMethod[j] 454 455 vmID := vm.ID[strings.Index(vm.ID, "#")+1:] 456 logger.Infof("vm: %#v", vm) 457 458 if strings.EqualFold(vmID, keyAgrFragment) { 459 return marshalKeyFromVerificationMethod(keyAgrID, vm, j) 460 } 461 } 462 463 return nil, fmt.Errorf("resolveKeyAgreementFromDIDDoc: keyAgreement ID '%s' not found in DID '%s'", keyAgrID, 464 docResolution.DIDDocument.ID) 465 } 466 467 func marshalKeyFromVerificationMethod(keyAgrID string, vm *did.VerificationMethod, i int) (*crypto.PublicKey, error) { 468 var ( 469 recKey *crypto.PublicKey 470 err error 471 ) 472 473 switch vm.Type { 474 case jsonWebKey2020: 475 jwkKey := vm.JSONWebKey() 476 477 recKey, err = jwksupport.PublicKeyFromJWK(jwkKey) 478 if err != nil { 479 return nil, fmt.Errorf("resolveKeyAgreementFromDIDDoc: for recipient JWK to PubKey %d: %w", i+1, err) 480 } 481 482 // for packing purposes, recipient kid header is the keyAgreement.ID value, the packers must resolve it and extract 483 // the kms kid value during unpack. 484 recKey.KID = keyAgrID 485 case x25519KeyAgreementKey2019: 486 recKey = &crypto.PublicKey{ 487 KID: keyAgrID, 488 X: vm.Value, 489 Curve: "X25519", 490 Type: "OKP", 491 } 492 case "Ed25519VerificationKey2018": 493 recKey = &crypto.PublicKey{ 494 KID: keyAgrID, 495 X: vm.Value, 496 Curve: "Ed25519", 497 Type: "OKP", 498 } 499 default: 500 return nil, fmt.Errorf("resolveKeyAgreementFromDIDDoc: invalid KeyAgreement type %d: %s", i+1, 501 vm.Type) 502 } 503 504 return recKey, nil 505 }