github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packer/legacy/anoncrypt/pack.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 "errors" 13 "fmt" 14 15 "github.com/btcsuite/btcutil/base58" 16 chacha "golang.org/x/crypto/chacha20poly1305" 17 "golang.org/x/crypto/poly1305" 18 19 "github.com/hyperledger/aries-framework-go/pkg/common/log" 20 "github.com/hyperledger/aries-framework-go/pkg/internal/cryptoutil" 21 ) 22 23 var logger = log.New("aries-framework/pkg/didcomm/packer/legacy/anoncrypt") 24 25 // Pack will encode the payload argument 26 // Using the protocol defined by Aries RFC 0019. 27 func (p *Packer) Pack(_ string, payload, _ []byte, recipientPubKeys [][]byte) ([]byte, error) { 28 var err error 29 30 if len(recipientPubKeys) == 0 { 31 return nil, errors.New("empty recipients keys, must have at least one recipient") 32 } 33 34 nonce := make([]byte, chacha.NonceSize) 35 36 _, err = p.randSource.Read(nonce) 37 if err != nil { 38 return nil, fmt.Errorf("pack: failed to generate random nonce: %w", err) 39 } 40 41 // cek (content encryption key) is a symmetric key, for chacha20, a symmetric cipher 42 cek := &[chacha.KeySize]byte{} 43 44 _, err = p.randSource.Read(cek[:]) 45 if err != nil { 46 return nil, fmt.Errorf("pack: failed to generate cek: %w", err) 47 } 48 49 var recipients []recipient 50 51 recipients, err = p.buildRecipients(cek, recipientPubKeys) 52 if err != nil { 53 return nil, fmt.Errorf("pack: failed to build recipients: %w", err) 54 } 55 56 header := protected{ 57 Enc: anonCryptEncType, 58 Typ: encodingType, 59 Alg: anonCrypt, 60 Recipients: recipients, 61 } 62 63 return p.buildEnvelope(nonce, payload, cek[:], &header) 64 } 65 66 func (p *Packer) buildEnvelope(nonce, payload, cek []byte, header *protected) ([]byte, error) { 67 protectedBytes, err := json.Marshal(header) 68 if err != nil { 69 return nil, err 70 } 71 72 protectedB64 := base64.URLEncoding.EncodeToString(protectedBytes) 73 74 chachaCipher, err := chacha.New(cek) 75 if err != nil { 76 return nil, err 77 } 78 79 // Additional data is b64encode(jsonencode(header)) 80 symPld := chachaCipher.Seal(nil, nonce, payload, []byte(protectedB64)) 81 82 // symPld has a length of len(pld) + poly1305.TagSize 83 // fetch the tag from the tail 84 tag := symPld[len(symPld)-poly1305.TagSize:] 85 // fetch the cipherText from the head (0:up to the trailing tag) 86 cipherText := symPld[0 : len(symPld)-poly1305.TagSize] 87 88 env := legacyEnvelope{ 89 Protected: protectedB64, 90 IV: base64.URLEncoding.EncodeToString(nonce), 91 CipherText: base64.URLEncoding.EncodeToString(cipherText), 92 Tag: base64.URLEncoding.EncodeToString(tag), 93 } 94 95 out, err := json.Marshal(env) 96 if err != nil { 97 return nil, err 98 } 99 100 return out, nil 101 } 102 103 func (p *Packer) buildRecipients(cek *[chacha.KeySize]byte, recPubKeys [][]byte) ([]recipient, error) { 104 encodedRecipients := make([]recipient, 0) 105 106 for _, recKey := range recPubKeys { 107 rec, err := p.buildRecipient(cek, recKey) 108 if err != nil { 109 logger.Warnf("buildRecipients: failed to build recipient: %w", err) 110 111 continue 112 } 113 114 encodedRecipients = append(encodedRecipients, *rec) 115 } 116 117 if len(encodedRecipients) == 0 { 118 return nil, fmt.Errorf("recipients keys are empty") 119 } 120 121 return encodedRecipients, nil 122 } 123 124 // buildRecipient encodes the necessary data for the recipient to decrypt the message 125 // encrypting the CEK. 126 func (p *Packer) buildRecipient(cek *[chacha.KeySize]byte, recKey []byte) (*recipient, error) { 127 recEncKey, err := cryptoutil.PublicEd25519toCurve25519(recKey) 128 if err != nil { 129 return nil, fmt.Errorf("buildRecipient: failed to convert public Ed25519 to Curve25519: %w", err) 130 } 131 132 box, err := newCryptoBox(p.kms) 133 if err != nil { 134 return nil, fmt.Errorf("buildRecipient: failed to create new CryptoBox: %w", err) 135 } 136 137 encCEK, err := box.Seal(cek[:], recEncKey, p.randSource) 138 if err != nil { 139 return nil, fmt.Errorf("buildRecipient: failed to encrypt cek: %w", err) 140 } 141 142 return &recipient{ 143 EncryptedKey: base64.URLEncoding.EncodeToString(encCEK), 144 Header: recipientHeader{ 145 KID: base58.Encode(recKey), // recKey is the Ed25519 recipient pk in b58 encoding 146 }, 147 }, nil 148 }