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