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  }