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 }