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  }