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