github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packer/legacy/anoncrypt/unpack.go (about)

     1  /*
     2  Copyright Avast Software. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package anoncryt
     8  
     9  import (
    10  	"encoding/base64"
    11  	"encoding/json"
    12  	"fmt"
    13  
    14  	"github.com/btcsuite/btcutil/base58"
    15  	chacha "golang.org/x/crypto/chacha20poly1305"
    16  
    17  	"github.com/hyperledger/aries-framework-go/pkg/doc/util/jwkkid"
    18  
    19  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/transport"
    20  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    21  )
    22  
    23  // Unpack will decode the envelope using the legacy format
    24  // Using (X)Chacha20 encryption algorithm and Poly1035 authenticator.
    25  func (p *Packer) Unpack(envelope []byte) (*transport.Envelope, error) {
    26  	var envelopeData legacyEnvelope
    27  
    28  	err := json.Unmarshal(envelope, &envelopeData)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	protectedBytes, err := base64.URLEncoding.DecodeString(envelopeData.Protected)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	var protectedData protected
    39  
    40  	err = json.Unmarshal(protectedBytes, &protectedData)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	if protectedData.Typ != encodingType {
    46  		return nil, fmt.Errorf("message type %s not supported", protectedData.Typ)
    47  	}
    48  
    49  	if protectedData.Alg != anonCrypt {
    50  		return nil, fmt.Errorf("message format %s not supported", protectedData.Alg)
    51  	}
    52  
    53  	keys, err := getCEK(protectedData.Recipients, p.kms)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	cek, recKey := keys.cek, keys.myKey
    59  
    60  	data, err := p.decodeCipherText(cek, &envelopeData)
    61  
    62  	return &transport.Envelope{
    63  		Message: data,
    64  		ToKey:   recKey,
    65  	}, err
    66  }
    67  
    68  type keys struct {
    69  	cek   *[chacha.KeySize]byte
    70  	myKey []byte
    71  }
    72  
    73  func getCEK(recipients []recipient, km kms.KeyManager) (*keys, error) {
    74  	var candidateKeys []string
    75  
    76  	for _, candidate := range recipients {
    77  		candidateKeys = append(candidateKeys, candidate.Header.KID)
    78  	}
    79  
    80  	recKeyIdx, err := findVerKey(km, candidateKeys)
    81  	if err != nil {
    82  		return nil, fmt.Errorf("getCEK: no key accessible %w", err)
    83  	}
    84  
    85  	recip := recipients[recKeyIdx]
    86  	recKey := base58.Decode(recip.Header.KID)
    87  
    88  	encCEK, err := base64.URLEncoding.DecodeString(recip.EncryptedKey)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	b, err := newCryptoBox(km)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	cekSlice, err := b.SealOpen(encCEK, recKey)
    99  	if err != nil {
   100  		return nil, fmt.Errorf("failed to decrypt CEK: %w", err)
   101  	}
   102  
   103  	var cek [chacha.KeySize]byte
   104  
   105  	copy(cek[:], cekSlice)
   106  
   107  	return &keys{
   108  		cek:   &cek,
   109  		myKey: recKey,
   110  	}, nil
   111  }
   112  
   113  func findVerKey(km kms.KeyManager, candidateKeys []string) (int, error) {
   114  	var errs []error
   115  
   116  	for i, key := range candidateKeys {
   117  		recKID, err := jwkkid.CreateKID(base58.Decode(key), kms.ED25519Type)
   118  		if err != nil {
   119  			return -1, err
   120  		}
   121  
   122  		_, err = km.Get(recKID)
   123  		if err == nil {
   124  			return i, nil
   125  		}
   126  
   127  		errs = append(errs, err)
   128  	}
   129  
   130  	return -1, fmt.Errorf("none of the recipient keys were found in kms: %v", errs)
   131  }
   132  
   133  // decodeCipherText decodes (from base64) and decrypts the ciphertext using chacha20poly1305.
   134  func (p *Packer) decodeCipherText(cek *[chacha.KeySize]byte, envelope *legacyEnvelope) ([]byte, error) {
   135  	var cipherText, nonce, tag, aad, message []byte
   136  	aad = []byte(envelope.Protected)
   137  
   138  	cipherText, err := base64.URLEncoding.DecodeString(envelope.CipherText)
   139  	if err != nil {
   140  		return nil, fmt.Errorf("decodeCipherText: failed to decode b64Sender: %w", err)
   141  	}
   142  
   143  	nonce, err = base64.URLEncoding.DecodeString(envelope.IV)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	tag, err = base64.URLEncoding.DecodeString(envelope.Tag)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	chachaCipher, err := chacha.New(cek[:])
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	payload := append(cipherText, tag...)
   159  
   160  	message, err = chachaCipher.Open(nil, nonce, payload, aad)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	return message, nil
   166  }