xref: /aosp_15_r20/external/tink/go/aead/subtle/xchacha20poly1305_test.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15////////////////////////////////////////////////////////////////////////////////
16
17package subtle_test
18
19import (
20	"bytes"
21	"encoding/hex"
22	"fmt"
23	"math/rand"
24	"testing"
25
26	"golang.org/x/crypto/chacha20poly1305"
27	"github.com/google/tink/go/aead/subtle"
28	"github.com/google/tink/go/subtle/random"
29	"github.com/google/tink/go/testutil"
30)
31
32func TestXChaCha20Poly1305EncryptDecrypt(t *testing.T) {
33	for i, test := range xChaCha20Poly1305Tests {
34		key, _ := hex.DecodeString(test.key)
35		pt, _ := hex.DecodeString(test.plaintext)
36		aad, _ := hex.DecodeString(test.aad)
37		nonce, _ := hex.DecodeString(test.nonce)
38		out, _ := hex.DecodeString(test.out)
39		tag, _ := hex.DecodeString(test.tag)
40
41		x, err := subtle.NewXChaCha20Poly1305(key)
42		if err != nil {
43			t.Errorf("#%d, cannot create new instance of XChaCha20Poly1305: %s", i, err)
44			continue
45		}
46
47		_, err = x.Encrypt(pt, aad)
48		if err != nil {
49			t.Errorf("#%d, unexpected encryption error: %s", i, err)
50			continue
51		}
52
53		var combinedCt []byte
54		combinedCt = append(combinedCt, nonce...)
55		combinedCt = append(combinedCt, out...)
56		combinedCt = append(combinedCt, tag...)
57		if got, err := x.Decrypt(combinedCt, aad); err != nil {
58			t.Errorf("#%d, unexpected decryption error: %s", i, err)
59			continue
60		} else if !bytes.Equal(pt, got) {
61			t.Errorf("#%d, plaintext's don't match: got %x vs %x", i, got, pt)
62			continue
63		}
64	}
65}
66
67func TestXChaCha20Poly1305EmptyAssociatedData(t *testing.T) {
68	key := random.GetRandomBytes(chacha20poly1305.KeySize)
69	aad := []byte{}
70	badAad := []byte{1, 2, 3}
71
72	x, err := subtle.NewXChaCha20Poly1305(key)
73	if err != nil {
74		t.Fatal(err)
75	}
76
77	for i := 0; i < 75; i++ {
78		pt := random.GetRandomBytes(uint32(i))
79		// Encrpting with aad as a 0-length array
80		{
81			ct, err := x.Encrypt(pt, aad)
82			if err != nil {
83				t.Errorf("Encrypt(%x, %x) failed", pt, aad)
84				continue
85			}
86
87			if got, err := x.Decrypt(ct, aad); err != nil || !bytes.Equal(pt, got) {
88				t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x", aad, got, pt)
89			}
90			if got, err := x.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) {
91				t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x", got, pt)
92			}
93			if _, err := x.Decrypt(ct, badAad); err == nil {
94				t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAad)
95			}
96		}
97		// Encrpting with aad equal to null
98		{
99			ct, err := x.Encrypt(pt, nil)
100			if err != nil {
101				t.Errorf("Encrypt(%x, nil) failed", pt)
102			}
103
104			if got, err := x.Decrypt(ct, aad); err != nil || !bytes.Equal(pt, got) {
105				t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", aad, got, pt, err)
106			}
107			if got, err := x.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) {
108				t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x; error: %v", got, pt, err)
109			}
110			if _, err := x.Decrypt(ct, badAad); err == nil {
111				t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAad)
112			}
113		}
114	}
115}
116
117func TestXChaCha20Poly1305LongMessages(t *testing.T) {
118	dataSize := uint32(16)
119	// Encrypts and decrypts messages of size <= 8192.
120	for dataSize <= 1<<24 {
121		pt := random.GetRandomBytes(dataSize)
122		aad := random.GetRandomBytes(dataSize / 3)
123		key := random.GetRandomBytes(chacha20poly1305.KeySize)
124
125		x, err := subtle.NewXChaCha20Poly1305(key)
126		if err != nil {
127			t.Fatal(err)
128		}
129
130		ct, err := x.Encrypt(pt, aad)
131		if err != nil {
132			t.Errorf("Encrypt(%x, %x) failed", pt, aad)
133			continue
134		}
135
136		if got, err := x.Decrypt(ct, aad); err != nil || !bytes.Equal(pt, got) {
137			t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", aad, got, pt, err)
138		}
139
140		dataSize += 5 * dataSize / 11
141	}
142}
143
144func TestXChaCha20Poly1305ModifyCiphertext(t *testing.T) {
145	for i, test := range xChaCha20Poly1305Tests {
146		key, _ := hex.DecodeString(test.key)
147		pt, _ := hex.DecodeString(test.plaintext)
148		aad, _ := hex.DecodeString(test.aad)
149
150		x, err := subtle.NewXChaCha20Poly1305(key)
151		if err != nil {
152			t.Fatal(err)
153		}
154
155		ct, err := x.Encrypt(pt, aad)
156		if err != nil {
157			t.Errorf("#%d: Encrypt failed", i)
158			continue
159		}
160
161		if len(aad) > 0 {
162			alterAadIdx := rand.Intn(len(aad))
163			aad[alterAadIdx] ^= 0x80
164			if _, err := x.Decrypt(ct, aad); err == nil {
165				t.Errorf("#%d: Decrypt was successful after altering additional data", i)
166				continue
167			}
168			aad[alterAadIdx] ^= 0x80
169		}
170
171		alterCtIdx := rand.Intn(len(ct))
172		ct[alterCtIdx] ^= 0x80
173		if _, err := x.Decrypt(ct, aad); err == nil {
174			t.Errorf("#%d: Decrypt was successful after altering ciphertext", i)
175			continue
176		}
177		ct[alterCtIdx] ^= 0x80
178	}
179}
180
181// This is a very simple test for the randomness of the nonce.
182// The test simply checks that the multiple ciphertexts of the same message are distinct.
183func TestXChaCha20Poly1305RandomNonce(t *testing.T) {
184	key := random.GetRandomBytes(chacha20poly1305.KeySize)
185	x, err := subtle.NewXChaCha20Poly1305(key)
186	if err != nil {
187		t.Fatal(err)
188	}
189
190	cts := make(map[string]bool)
191	pt, aad := []byte{}, []byte{}
192	for i := 0; i < 1<<10; i++ {
193		ct, err := x.Encrypt(pt, aad)
194		ctHex := hex.EncodeToString(ct)
195		if err != nil || cts[ctHex] {
196			t.Errorf("TestRandomNonce failed: %v", err)
197		} else {
198			cts[ctHex] = true
199		}
200	}
201}
202
203func TestXChaCha20Poly1305WycheproofCases(t *testing.T) {
204	testutil.SkipTestIfTestSrcDirIsNotSet(t)
205	suite := new(AEADSuite)
206	if err := testutil.PopulateSuite(suite, "xchacha20_poly1305_test.json"); err != nil {
207		t.Fatalf("failed populating suite: %s", err)
208	}
209	for _, group := range suite.TestGroups {
210		if group.KeySize/8 != chacha20poly1305.KeySize {
211			continue
212		}
213		if group.IvSize/8 != chacha20poly1305.NonceSizeX {
214			continue
215		}
216		for _, test := range group.Tests {
217			caseName := fmt.Sprintf("%s-%s:Case-%d", suite.Algorithm, group.Type, test.CaseID)
218			t.Run(caseName, func(t *testing.T) { runXChaCha20Poly1305WycheproofCase(t, test) })
219		}
220	}
221}
222
223func runXChaCha20Poly1305WycheproofCase(t *testing.T, tc *AEADCase) {
224	var combinedCt []byte
225	combinedCt = append(combinedCt, tc.Iv...)
226	combinedCt = append(combinedCt, tc.Ct...)
227	combinedCt = append(combinedCt, tc.Tag...)
228
229	ca, err := subtle.NewXChaCha20Poly1305(tc.Key)
230	if err != nil {
231		t.Fatalf("cannot create new instance of ChaCha20Poly1305: %s", err)
232	}
233
234	_, err = ca.Encrypt(tc.Msg, tc.Aad)
235	if err != nil {
236		t.Fatalf("unexpected encryption error: %s", err)
237	}
238
239	decrypted, err := ca.Decrypt(combinedCt, tc.Aad)
240	if err != nil {
241		if tc.Result == "valid" {
242			t.Errorf("unexpected error: %s", err)
243		}
244	} else {
245		if tc.Result == "invalid" {
246			t.Error("decrypted invalid")
247		}
248		if !bytes.Equal(decrypted, tc.Msg) {
249			t.Error("incorrect decryption")
250		}
251	}
252}
253
254func TestPreallocatedCiphertextMemoryInXChaCha20Poly1305IsExact(t *testing.T) {
255	key := random.GetRandomBytes(chacha20poly1305.KeySize)
256	a, err := subtle.NewXChaCha20Poly1305(key)
257	if err != nil {
258		t.Fatalf("aead.NewAESGCMInsecureIV() err = %v, want nil", err)
259	}
260	plaintext := random.GetRandomBytes(13)
261	associatedData := random.GetRandomBytes(17)
262
263	ciphertext, err := a.Encrypt(plaintext, associatedData)
264	if err != nil {
265		t.Fatalf("a.Encrypt() err = %v, want nil", err)
266	}
267	// Encrypt() uses cipher.Overhead() to pre-allocate the memory needed store the ciphertext.
268	// For ChaCha20Poly1305, the size of the allocated memory should always be exact. If this check
269	// fails, the pre-allocated memory was too large or too small. If it was too small, the system had
270	// to re-allocate more memory, which is expensive and should be avoided.
271	if len(ciphertext) != cap(ciphertext) {
272		t.Errorf("want len(ciphertext) == cap(ciphertext), got %d != %d", len(ciphertext), cap(ciphertext))
273	}
274}
275