xref: /aosp_15_r20/external/tink/go/aead/subtle/encrypt_then_authenticate_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	"testing"
23
24	"github.com/google/tink/go/aead/subtle"
25	subtleMac "github.com/google/tink/go/mac/subtle"
26	"github.com/google/tink/go/subtle/random"
27	"github.com/google/tink/go/tink"
28)
29
30func createAEADWithKeys(encryptionKey []byte, ivSize int, hashAlgo string, macKey []byte, tagSize int) (tink.AEAD, error) {
31	ctr, err := subtle.NewAESCTR(encryptionKey, ivSize)
32	if err != nil {
33		return nil, err
34	}
35
36	mac, err := subtleMac.NewHMAC(hashAlgo, macKey, uint32(tagSize))
37	if err != nil {
38		return nil, err
39	}
40
41	cipher, err := subtle.NewEncryptThenAuthenticate(ctr, mac, tagSize)
42	if err != nil {
43		return nil, err
44	}
45	return cipher, nil
46}
47
48func createAEAD(keySize, ivSize int, hashAlgo string, macKeySize int, tagSize int) (tink.AEAD, error) {
49	encryptionKey := random.GetRandomBytes(uint32(keySize))
50	ctr, err := subtle.NewAESCTR(encryptionKey, ivSize)
51	if err != nil {
52		return nil, err
53	}
54
55	macKey := random.GetRandomBytes(uint32(macKeySize))
56	mac, err := subtleMac.NewHMAC(hashAlgo, macKey, uint32(tagSize))
57	if err != nil {
58		return nil, err
59	}
60
61	cipher, err := subtle.NewEncryptThenAuthenticate(ctr, mac, tagSize)
62	if err != nil {
63		return nil, err
64	}
65	return cipher, nil
66}
67
68// Copied from
69// https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05.
70//
71// We use CTR but the RFC uses CBC mode, so it's not possible to compare
72// plaintexts. However, the tests are still valueable to ensure that we
73// correcly compute HMAC over ciphertext and associatedData.
74var rfcTestVectors = []struct {
75	macKey         string
76	encryptionKey  string
77	ciphertext     string
78	associatedData string
79	hashAlgo       string
80	ivSize         int
81	tagSize        int
82}{
83	{
84		macKey:        "000102030405060708090a0b0c0d0e0f",
85		encryptionKey: "101112131415161718191a1b1c1d1e1f",
86		ciphertext: "" +
87			"1af38c2dc2b96ffdd86694092341bc04" +
88			"c80edfa32ddf39d5ef00c0b468834279" +
89			"a2e46a1b8049f792f76bfe54b903a9c9" +
90			"a94ac9b47ad2655c5f10f9aef71427e2" +
91			"fc6f9b3f399a221489f16362c7032336" +
92			"09d45ac69864e3321cf82935ac4096c8" +
93			"6e133314c54019e8ca7980dfa4b9cf1b" +
94			"384c486f3a54c51078158ee5d79de59f" +
95			"bd34d848b3d69550a67646344427ade5" +
96			"4b8851ffb598f7f80074b9473c82e2db" +
97			"652c3fa36b0a7c5b3219fab3a30bc1c4",
98		associatedData: "" +
99			"546865207365636f6e64207072696e63" +
100			"69706c65206f66204175677573746520" +
101			"4b6572636b686f666673",
102		hashAlgo: "SHA256",
103		ivSize:   16,
104		tagSize:  16,
105	},
106	{
107		macKey:        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
108		encryptionKey: "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
109		ciphertext: "" +
110			"1af38c2dc2b96ffdd86694092341bc04" +
111			"4affaaadb78c31c5da4b1b590d10ffbd" +
112			"3dd8d5d302423526912da037ecbcc7bd" +
113			"822c301dd67c373bccb584ad3e9279c2" +
114			"e6d12a1374b77f077553df829410446b" +
115			"36ebd97066296ae6427ea75c2e0846a1" +
116			"1a09ccf5370dc80bfecbad28c73f09b3" +
117			"a3b75e662a2594410ae496b2e2e6609e" +
118			"31e6e02cc837f053d21f37ff4f51950b" +
119			"be2638d09dd7a4930930806d0703b1f6" +
120			"4dd3b4c088a7f45c216839645b2012bf" +
121			"2e6269a8c56a816dbc1b267761955bc5",
122		associatedData: "" +
123			"546865207365636f6e64207072696e63" +
124			"69706c65206f66204175677573746520" +
125			"4b6572636b686f666673",
126		hashAlgo: "SHA512",
127		ivSize:   16,
128		tagSize:  32,
129	},
130}
131
132func hexDecode(t *testing.T, data string) []byte {
133	t.Helper()
134	decoded, err := hex.DecodeString(data)
135	if err != nil {
136		t.Fatal(err)
137	}
138	return decoded
139}
140
141func TestETARFCTestVectors(t *testing.T) {
142	for _, v := range rfcTestVectors {
143		macKey := hexDecode(t, v.macKey)
144		encryptionKey := hexDecode(t, v.encryptionKey)
145		ciphertext := hexDecode(t, v.ciphertext)
146		associatedData := hexDecode(t, v.associatedData)
147
148		cipher, err := createAEADWithKeys(encryptionKey, v.ivSize, v.hashAlgo, macKey, v.tagSize)
149		if err != nil {
150			t.Fatalf("failed to create AEAD from RFC test vector: %v", v)
151		}
152
153		if _, err := cipher.Decrypt(ciphertext, associatedData); err != nil {
154			t.Errorf("decryption failed to RFC test vector: %v, error: %v", v, err)
155		}
156	}
157}
158
159func TestETAEncryptDecrypt(t *testing.T) {
160	const keySize = 16
161	const ivSize = 12
162	const macKeySize = 16
163	const tagSize = 16
164
165	cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize)
166	if err != nil {
167		t.Fatalf("got: %v, want: success", err)
168	}
169
170	message := []byte("Some data to encrypt.")
171	associatedData := []byte("Some data to authenticate.")
172
173	ciphertext, err := cipher.Encrypt(message, associatedData)
174	if err != nil {
175		t.Fatalf("encryption failed, error: %v", err)
176	}
177
178	if len(ciphertext) != len(message)+ivSize+tagSize {
179		t.Errorf("invalid ciphertext size, got: %d, want: %d", len(ciphertext), len(message)+ivSize+tagSize)
180	}
181
182	plaintext, err := cipher.Decrypt(ciphertext, associatedData)
183	if err != nil {
184		t.Fatalf("decryption failed, error: %v", err)
185	}
186
187	if !bytes.Equal(plaintext, message) {
188		t.Errorf("invalid plaintext, got: %q, want: %q", plaintext, message)
189	}
190}
191
192func TestETAWithAssociatedDataSlice(t *testing.T) {
193	const keySize = 16
194	const ivSize = 12
195	const macKeySize = 16
196	const tagSize = 16
197	cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize)
198	if err != nil {
199		t.Fatalf("got: %v, want: success", err)
200	}
201
202	message := []byte("message")
203	largeData := []byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
204	associatedData := largeData[:1]
205
206	_, err = cipher.Encrypt(message, associatedData)
207	if err != nil {
208		t.Fatalf("encryption failed, error: %v", err)
209	}
210
211	wantLargeData := []byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
212	if !bytes.Equal(largeData, wantLargeData) {
213		t.Errorf("largeData = %q, want: %q", largeData, wantLargeData)
214	}
215}
216
217func TestETAEncryptDecryptRandomMessage(t *testing.T) {
218	const keySize = 16
219	const ivSize = 12
220	const macKeySize = 16
221	const tagSize = 16
222
223	cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize)
224	if err != nil {
225		t.Fatalf("got: %v, want: success", err)
226	}
227
228	for i := 0; i < 256; i++ {
229		message := random.GetRandomBytes(uint32(i))
230		associatedData := random.GetRandomBytes(uint32(i))
231
232		ciphertext, err := cipher.Encrypt(message, associatedData)
233		if err != nil {
234			t.Fatalf("encryption failed, error: %v", err)
235		}
236
237		if len(ciphertext) != len(message)+ivSize+tagSize {
238			t.Errorf("invalid ciphertext size, got: %d, want: %d", len(ciphertext), len(message)+ivSize+tagSize)
239		}
240
241		plaintext, err := cipher.Decrypt(ciphertext, associatedData)
242		if err != nil {
243			t.Fatalf("decryption failed, error: %v", err)
244		}
245
246		if !bytes.Equal(plaintext, message) {
247			t.Errorf("invalid plaintext, got: %q, want: %q", plaintext, message)
248		}
249	}
250}
251
252func TestETAMultipleEncrypt(t *testing.T) {
253	const keySize = 16
254	const ivSize = 12
255	const macKeySize = 16
256	const tagSize = 16
257
258	cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize)
259	if err != nil {
260		t.Fatalf("got: %v, want: success", err)
261	}
262
263	message := []byte("Some data to encrypt.")
264	associatedData := []byte("Some data to authenticate.")
265
266	ciphertext1, err := cipher.Encrypt(message, associatedData)
267	if err != nil {
268		t.Fatalf("encryption failed, error: %v", err)
269	}
270
271	ciphertext2, err := cipher.Encrypt(message, associatedData)
272	if err != nil {
273		t.Fatalf("encryption failed, error: %v", err)
274	}
275
276	if bytes.Equal(ciphertext1, ciphertext2) {
277		t.Error("ciphertexts must not be the same")
278	}
279}
280
281func TestETAInvalidTagSize(t *testing.T) {
282	const keySize = 16
283	const ivSize = 12
284	const macKeySize = 16
285	const tagSize = 9 // Invalid!
286
287	if _, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize); err == nil {
288		t.Error("got: success, want: error invalid tag size")
289	}
290}
291
292func TestETADecryptModifiedCiphertext(t *testing.T) {
293	const keySize = 16
294	const ivSize = 12
295	const macKeySize = 16
296	const tagSize = 16
297
298	cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize)
299	if err != nil {
300		t.Fatalf("got: %v, want: success", err)
301	}
302
303	message := []byte("Some data to encrypt.")
304	associatedData := []byte("Some data to authenticate.")
305	ciphertext, err := cipher.Encrypt(message, associatedData)
306	if err != nil {
307		t.Fatalf("encryption failed, error: %v", err)
308	}
309
310	// Modify the ciphertext and try to decrypt.
311	modCiphertext := make([]byte, len(ciphertext))
312	copy(modCiphertext, ciphertext)
313	for i := 0; i < len(ciphertext)*8; i++ {
314		// Save the byte to be modified.
315		b := modCiphertext[i/8]
316		modCiphertext[i/8] ^= (1 << uint(i%8))
317		if bytes.Equal(ciphertext, modCiphertext) {
318			t.Errorf("modCiphertext shouldn't be the same as ciphertext")
319		}
320		if _, err := cipher.Decrypt(modCiphertext, associatedData); err == nil {
321			t.Errorf("successfully decrypted modified ciphertext (i = %d)", i)
322		}
323		// Restore the modified byte.
324		modCiphertext[i/8] = b
325	}
326
327	// Modify the associated data.
328	modAssociatedData := make([]byte, len(associatedData))
329	copy(modAssociatedData, associatedData)
330	for i := 0; i < len(associatedData)*8; i++ {
331		// Save the byte to be modified.
332		b := modAssociatedData[i/8]
333		modAssociatedData[i/8] ^= (1 << uint(i%8))
334		if bytes.Equal(associatedData, modAssociatedData) {
335			t.Errorf("modAssociatedData shouldn't be the same as associatedData")
336		}
337		if _, err := cipher.Decrypt(ciphertext, modAssociatedData); err == nil {
338			t.Errorf("successfully decrypted with modified associated data (i = %d)", i)
339		}
340		// Restore the modified byte.
341		modAssociatedData[i/8] = b
342	}
343
344	// Truncate the ciphertext.
345	truncatedCiphertext := make([]byte, len(ciphertext))
346	copy(truncatedCiphertext, ciphertext)
347	for i := 1; i <= len(ciphertext); i++ {
348		truncatedCiphertext = truncatedCiphertext[:len(ciphertext)-i]
349		if _, err := cipher.Decrypt(truncatedCiphertext, associatedData); err == nil {
350			t.Errorf("successfully decrypted truncated ciphertext (i = %d)", i)
351		}
352	}
353}
354
355func TestETAEmptyParams(t *testing.T) {
356	const keySize = 16
357	const ivSize = 12
358	const macKeySize = 16
359	const tagSize = 16
360
361	cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize)
362	if err != nil {
363		t.Fatalf("got: %v, want: success", err)
364	}
365
366	message := []byte("Some data to encrypt.")
367	if _, err := cipher.Encrypt(message, []byte{}); err != nil {
368		t.Errorf("encryption failed with empty associatedData")
369	}
370	if _, err := cipher.Encrypt([]byte{}, []byte{}); err != nil {
371		t.Errorf("encryption failed with empty ciphertext and associatedData")
372	}
373}
374