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