xref: /aosp_15_r20/external/tink/go/aead/subtle/chacha20poly1305_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 TestChaCha20Poly1305EncryptDecrypt(t *testing.T) {
33	for i, test := range chaCha20Poly1305Tests {
34		key, _ := hex.DecodeString(test.key)
35		pt, _ := hex.DecodeString(test.plaintext)
36		ad, _ := hex.DecodeString(test.associatedData)
37		nonce, _ := hex.DecodeString(test.nonce)
38		out, _ := hex.DecodeString(test.out)
39
40		ca, err := subtle.NewChaCha20Poly1305(key)
41		if err != nil {
42			t.Errorf("#%d, cannot create new instance of ChaCha20Poly1305: %s", i, err)
43			continue
44		}
45
46		_, err = ca.Encrypt(pt, ad)
47		if err != nil {
48			t.Errorf("#%d, unexpected encryption error: %s", i, err)
49			continue
50		}
51
52		var combinedCT []byte
53		combinedCT = append(combinedCT, nonce...)
54		combinedCT = append(combinedCT, out...)
55		if got, err := ca.Decrypt(combinedCT, ad); err != nil {
56			t.Errorf("#%d, unexpected decryption error: %s", i, err)
57			continue
58		} else if !bytes.Equal(pt, got) {
59			t.Errorf("#%d, plaintext's don't match: got %x vs %x", i, got, pt)
60			continue
61		}
62	}
63}
64
65func TestChaCha20Poly1305EmptyAssociatedData(t *testing.T) {
66	key := random.GetRandomBytes(chacha20poly1305.KeySize)
67	emptyAD := []byte{}
68	badAD := []byte{1, 2, 3}
69
70	ca, err := subtle.NewChaCha20Poly1305(key)
71	if err != nil {
72		t.Fatal(err)
73	}
74
75	for i := 0; i < 75; i++ {
76		pt := random.GetRandomBytes(uint32(i))
77		// Encrypting with associatedData as a 0-length array
78		{
79			ct, err := ca.Encrypt(pt, emptyAD)
80			if err != nil {
81				t.Errorf("Encrypt(%x, %x) failed", pt, emptyAD)
82				continue
83			}
84
85			if got, err := ca.Decrypt(ct, emptyAD); err != nil || !bytes.Equal(pt, got) {
86				t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x", emptyAD, got, pt)
87			}
88			if got, err := ca.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) {
89				t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x", got, pt)
90			}
91			if _, err := ca.Decrypt(ct, badAD); err == nil {
92				t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAD)
93			}
94		}
95		// Encrypting with associatedData equal to nil
96		{
97			ct, err := ca.Encrypt(pt, nil)
98			if err != nil {
99				t.Errorf("Encrypt(%x, nil) failed", pt)
100			}
101
102			if got, err := ca.Decrypt(ct, emptyAD); err != nil || !bytes.Equal(pt, got) {
103				t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", emptyAD, got, pt, err)
104			}
105			if got, err := ca.Decrypt(ct, nil); err != nil || !bytes.Equal(pt, got) {
106				t.Errorf("Decrypt(Encrypt(pt, nil)): plaintext's don't match: got %x vs %x; error: %v", got, pt, err)
107			}
108			if _, err := ca.Decrypt(ct, badAD); err == nil {
109				t.Errorf("Decrypt(Encrypt(pt, %x)) = _, nil; want: _, err", badAD)
110			}
111		}
112	}
113}
114
115func TestChaCha20Poly1305LongMessages(t *testing.T) {
116	dataSize := uint32(16)
117	// Encrypts and decrypts messages of size <= 8192.
118	for dataSize <= 1<<24 {
119		pt := random.GetRandomBytes(dataSize)
120		ad := random.GetRandomBytes(dataSize / 3)
121		key := random.GetRandomBytes(chacha20poly1305.KeySize)
122
123		ca, err := subtle.NewChaCha20Poly1305(key)
124		if err != nil {
125			t.Fatal(err)
126		}
127
128		ct, err := ca.Encrypt(pt, ad)
129		if err != nil {
130			t.Errorf("Encrypt(%x, %x) failed", pt, ad)
131			continue
132		}
133
134		if got, err := ca.Decrypt(ct, ad); err != nil || !bytes.Equal(pt, got) {
135			t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", ad, got, pt, err)
136		}
137
138		dataSize += 5 * dataSize / 11
139	}
140}
141
142func TestChaCha20Poly1305ModifyCiphertext(t *testing.T) {
143	for i, test := range chaCha20Poly1305Tests {
144		key, _ := hex.DecodeString(test.key)
145		pt, _ := hex.DecodeString(test.plaintext)
146		ad, _ := hex.DecodeString(test.associatedData)
147
148		ca, err := subtle.NewChaCha20Poly1305(key)
149		if err != nil {
150			t.Fatal(err)
151		}
152
153		ct, err := ca.Encrypt(pt, ad)
154		if err != nil {
155			t.Errorf("#%d: Encrypt failed", i)
156			continue
157		}
158
159		if len(ad) > 0 {
160			alteredIndex := rand.Intn(len(ad))
161			ad[alteredIndex] ^= 0x80
162			if _, err := ca.Decrypt(ct, ad); err == nil {
163				t.Errorf("#%d: Decrypt was successful after altering associated data", i)
164				continue
165			}
166			ad[alteredIndex] ^= 0x80
167		}
168
169		alterCtIdx := rand.Intn(len(ct))
170		ct[alterCtIdx] ^= 0x80
171		if _, err := ca.Decrypt(ct, ad); err == nil {
172			t.Errorf("#%d: Decrypt was successful after altering ciphertext", i)
173			continue
174		}
175		ct[alterCtIdx] ^= 0x80
176	}
177}
178
179// This is a very simple test for the randomness of the nonce.
180// The test simply checks that the multiple ciphertexts of the same message are distinct.
181func TestChaCha20Poly1305RandomNonce(t *testing.T) {
182	key := random.GetRandomBytes(chacha20poly1305.KeySize)
183	ca, err := subtle.NewChaCha20Poly1305(key)
184	if err != nil {
185		t.Fatal(err)
186	}
187
188	cts := make(map[string]bool)
189	pt, ad := []byte{}, []byte{}
190	for i := 0; i < 1<<10; i++ {
191		ct, err := ca.Encrypt(pt, ad)
192		ctHex := hex.EncodeToString(ct)
193		if err != nil || cts[ctHex] {
194			t.Errorf("TestRandomNonce failed: %v", err)
195		} else {
196			cts[ctHex] = true
197		}
198	}
199}
200
201func TestChaCha20Poly1305WycheproofCases(t *testing.T) {
202	testutil.SkipTestIfTestSrcDirIsNotSet(t)
203	suite := new(AEADSuite)
204	if err := testutil.PopulateSuite(suite, "chacha20_poly1305_test.json"); err != nil {
205		t.Fatalf("failed populating suite: %s", err)
206	}
207	for _, group := range suite.TestGroups {
208		if group.KeySize/8 != chacha20poly1305.KeySize {
209			continue
210		}
211		if group.IvSize/8 != chacha20poly1305.NonceSize {
212			continue
213		}
214
215		for _, test := range group.Tests {
216			caseName := fmt.Sprintf("%s-%s:Case-%d", suite.Algorithm, group.Type, test.CaseID)
217			t.Run(caseName, func(t *testing.T) { runChaCha20Poly1305WycheproofCase(t, test) })
218		}
219	}
220}
221
222func runChaCha20Poly1305WycheproofCase(t *testing.T, tc *AEADCase) {
223	var combinedCT []byte
224	combinedCT = append(combinedCT, tc.Iv...)
225	combinedCT = append(combinedCT, tc.Ct...)
226	combinedCT = append(combinedCT, tc.Tag...)
227
228	ca, err := subtle.NewChaCha20Poly1305(tc.Key)
229	if err != nil {
230		t.Fatalf("cannot create new instance of ChaCha20Poly1305: %s", err)
231	}
232
233	_, err = ca.Encrypt(tc.Msg, tc.Aad)
234	if err != nil {
235		t.Fatalf("unexpected encryption error: %s", err)
236	}
237
238	decrypted, err := ca.Decrypt(combinedCT, tc.Aad)
239	if err != nil {
240		if tc.Result == "valid" {
241			t.Errorf("unexpected error: %s", err)
242		}
243	} else {
244		if tc.Result == "invalid" {
245			t.Error("decrypted invalid")
246		}
247		if !bytes.Equal(decrypted, tc.Msg) {
248			t.Error("incorrect decryption")
249		}
250	}
251}
252