github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packer/legacy/authcrypt/unpack.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/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/internal/cryptoutil"
    21  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    22  )
    23  
    24  // Unpack will decode the envelope using the legacy format
    25  // Using (X)Chacha20 encryption algorithm and Poly1035 authenticator.
    26  func (p *Packer) Unpack(envelope []byte) (*transport.Envelope, error) {
    27  	var envelopeData legacyEnvelope
    28  
    29  	err := json.Unmarshal(envelope, &envelopeData)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	protectedBytes, err := base64.URLEncoding.DecodeString(envelopeData.Protected)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	var protectedData protected
    40  
    41  	err = json.Unmarshal(protectedBytes, &protectedData)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	if protectedData.Typ != encodingType {
    47  		return nil, fmt.Errorf("message type %s not supported", protectedData.Typ)
    48  	}
    49  
    50  	if protectedData.Alg != "Authcrypt" {
    51  		// TODO https://github.com/hyperledger/aries-framework-go/issues/41 change this when anoncrypt is introduced
    52  		return nil, fmt.Errorf("message format %s not supported", protectedData.Alg)
    53  	}
    54  
    55  	keys, err := getCEK(protectedData.Recipients, p.kms)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	cek, senderKey, recKey := keys.cek, keys.theirKey, keys.myKey
    61  
    62  	data, err := p.decodeCipherText(cek, &envelopeData)
    63  
    64  	return &transport.Envelope{
    65  		Message: data,
    66  		FromKey: senderKey,
    67  		ToKey:   recKey,
    68  	}, err
    69  }
    70  
    71  type keys struct {
    72  	cek      *[chacha.KeySize]byte
    73  	theirKey []byte
    74  	myKey    []byte
    75  }
    76  
    77  func getCEK(recipients []recipient, km kms.KeyManager) (*keys, error) {
    78  	var candidateKeys []string
    79  
    80  	for _, candidate := range recipients {
    81  		candidateKeys = append(candidateKeys, candidate.Header.KID)
    82  	}
    83  
    84  	recKeyIdx, err := findVerKey(km, candidateKeys)
    85  	if err != nil {
    86  		return nil, fmt.Errorf("getCEK: no key accessible %w", err)
    87  	}
    88  
    89  	recip := recipients[recKeyIdx]
    90  	recKey := base58.Decode(recip.Header.KID)
    91  
    92  	senderPub, senderPubCurve, err := decodeSender(recip.Header.Sender, recKey, km)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	nonceSlice, err := base64.URLEncoding.DecodeString(recip.Header.IV)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	encCEK, err := base64.URLEncoding.DecodeString(recip.EncryptedKey)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	b, err := newCryptoBox(km)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	cekSlice, err := b.EasyOpen(encCEK, nonceSlice, senderPubCurve, recKey)
   113  	if err != nil {
   114  		return nil, fmt.Errorf("failed to decrypt CEK: %w", err)
   115  	}
   116  
   117  	var cek [chacha.KeySize]byte
   118  
   119  	copy(cek[:], cekSlice)
   120  
   121  	return &keys{
   122  		cek:      &cek,
   123  		theirKey: senderPub,
   124  		myKey:    recKey,
   125  	}, nil
   126  }
   127  
   128  func findVerKey(km kms.KeyManager, candidateKeys []string) (int, error) {
   129  	var errs []error
   130  
   131  	for i, key := range candidateKeys {
   132  		recKID, err := jwkkid.CreateKID(base58.Decode(key), kms.ED25519Type)
   133  		if err != nil {
   134  			return -1, err
   135  		}
   136  
   137  		_, err = km.Get(recKID)
   138  		if err == nil {
   139  			return i, nil
   140  		}
   141  
   142  		errs = append(errs, err)
   143  	}
   144  
   145  	return -1, fmt.Errorf("none of the recipient keys were found in kms: %v", errs)
   146  }
   147  
   148  func decodeSender(b64Sender string, pk []byte, km kms.KeyManager) ([]byte, []byte, error) {
   149  	encSender, err := base64.URLEncoding.DecodeString(b64Sender)
   150  	if err != nil {
   151  		return nil, nil, err
   152  	}
   153  
   154  	b, err := newCryptoBox(km)
   155  	if err != nil {
   156  		return nil, nil, err
   157  	}
   158  
   159  	senderPub, err := b.SealOpen(encSender, pk)
   160  	if err != nil {
   161  		return nil, nil, err
   162  	}
   163  
   164  	senderData := base58.Decode(string(senderPub))
   165  
   166  	senderPubCurve, err := cryptoutil.PublicEd25519toCurve25519(senderData)
   167  	if err != nil {
   168  		return nil, nil, fmt.Errorf("decodeSender: failed to convert ed25519 to Curve25519 pub key: %w", err)
   169  	}
   170  
   171  	return senderData, senderPubCurve, err
   172  }
   173  
   174  // decodeCipherText decodes (from base64) and decrypts the ciphertext using chacha20poly1305.
   175  func (p *Packer) decodeCipherText(cek *[chacha.KeySize]byte, envelope *legacyEnvelope) ([]byte, error) {
   176  	var cipherText, nonce, tag, aad, message []byte
   177  	aad = []byte(envelope.Protected)
   178  
   179  	cipherText, err := base64.URLEncoding.DecodeString(envelope.CipherText)
   180  	if err != nil {
   181  		return nil, fmt.Errorf("decodeCipherText: failed to decode b64Sender: %w", err)
   182  	}
   183  
   184  	nonce, err = base64.URLEncoding.DecodeString(envelope.IV)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	tag, err = base64.URLEncoding.DecodeString(envelope.Tag)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	chachaCipher, err := chacha.New(cek[:])
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	payload := append(cipherText, tag...)
   200  
   201  	message, err = chachaCipher.Open(nil, nonce, payload, aad)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	return message, nil
   207  }