History

lib/crypto: tests: Add KUnit test suite for AES-CCM

Add a KUnit test suite for the AES-CCM library API. It consists of: - All the shared test cases from aead-test-template.h. These include extensive consistency tests, a "Monte-Carlo test", and a benchmark. - Tests against hardcoded AES-CCM test vectors from external sources. - Tests for CCM-specific message length validation. To generate the expected aes_ccm_monte_carlo_checksum[] value, add a script gen-aead-testvecs.py which computes it using python-cryptography.

Eric Biggers committed 3 months ago

Commit 2aeef501 parent 9396dc95 files · +427 −0Browse files

Unified · Split

5 files
  1. lib/crypto/.kunitconfig +1 −0
  2. lib/crypto/tests/Kconfig +9 −0
  3. lib/crypto/tests/Makefile +1 −0
  4. lib/crypto/tests/aes_ccm_kunit.c +356 −0
  5. scripts/crypto/gen-aead-testvecs.py +60 −0
lib/crypto/.kunitconfig+1−0
@@ -3,6 +3,7 @@ CONFIG_KUNIT=y · 2 unmodified lines
33 CONFIG_CRYPTO_LIB_ENABLE_ALL_FOR_KUNIT=y
44
55 CONFIG_CRYPTO_LIB_AES_CBC_MACS_KUNIT_TEST=y
6+CONFIG_CRYPTO_LIB_AES_CCM_KUNIT_TEST=y
67 CONFIG_CRYPTO_LIB_BLAKE2B_KUNIT_TEST=y
78 CONFIG_CRYPTO_LIB_BLAKE2S_KUNIT_TEST=y
89 CONFIG_CRYPTO_LIB_CHACHA20POLY1305_KUNIT_TEST=y
lib/crypto/tests/Kconfig+9−0
@@ -9,6 +9,15 @@ config CRYPTO_LIB_AES_CBC_MACS_KUNIT_TEST · 8 unmodified lines
99 KUnit tests for the AES-CMAC, AES-XCBC-MAC, and AES-CBC-MAC message
1010 authentication codes.
1111
12+config CRYPTO_LIB_AES_CCM_KUNIT_TEST
13+ tristate "KUnit tests for AES-CCM" if !KUNIT_ALL_TESTS
14+ depends on KUNIT && CRYPTO_LIB_AES_CCM
15+ default KUNIT_ALL_TESTS
16+ select CRYPTO_LIB_BENCHMARK_VISIBLE
17+ # This test uses BLAKE2s, but that's always built-in.
18+ help
19+ KUnit tests for the AES-CCM authenticated encryption algorithm.
20+
1221 config CRYPTO_LIB_BLAKE2B_KUNIT_TEST
1322 tristate "KUnit tests for BLAKE2b" if !KUNIT_ALL_TESTS
1423 depends on KUNIT && CRYPTO_LIB_BLAKE2B
lib/crypto/tests/Makefile+1−0
@@ -1,6 +1,7 @@
11 # SPDX-License-Identifier: GPL-2.0-or-later
22
33 obj-$(CONFIG_CRYPTO_LIB_AES_CBC_MACS_KUNIT_TEST) += aes_cbc_macs_kunit.o
4+obj-$(CONFIG_CRYPTO_LIB_AES_CCM_KUNIT_TEST) += aes_ccm_kunit.o
45 obj-$(CONFIG_CRYPTO_LIB_BLAKE2B_KUNIT_TEST) += blake2b_kunit.o
56 obj-$(CONFIG_CRYPTO_LIB_BLAKE2S_KUNIT_TEST) += blake2s_kunit.o
67 obj-$(CONFIG_CRYPTO_LIB_CHACHA20POLY1305_KUNIT_TEST) += chacha20poly1305_kunit.o
lib/crypto/tests/aes_ccm_kunit.c+356−0
@@ -0,0 +1,356 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * KUnit test suite for AES-CCM
4+ *
5+ * Copyright 2026 Google LLC
6+ */
7+#include <crypto/aes-ccm.h>
8+#include <crypto/blake2s.h>
9+#include "test-utils.h"
10+
11+/* AES-CCM test vectors from external sources */
12+static const struct aes_ccm_testvec {
13+ const char *name;
14+ const char *key;
15+ size_t key_len;
16+ const char *nonce;
17+ size_t nonce_len;
18+ const char *ad;
19+ size_t ad_len;
20+ const char *ptext;
21+ const char *ctext;
22+ size_t data_len;
23+ const char *tag;
24+ size_t tag_len;
25+} aes_ccm_testvecs[] = {
26+ {
27+ .name = "RFC 3610 Packet Vector #1",
28+ .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
29+ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
30+ .key_len = 16,
31+ .nonce = "\x00\x00\x00\x03\x02\x01\x00\xa0"
32+ "\xa1\xa2\xa3\xa4\xa5",
33+ .nonce_len = 13,
34+ .ad = "\x00\x01\x02\x03\x04\x05\x06\x07",
35+ .ad_len = 8,
36+ .ptext = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
37+ "\x10\x11\x12\x13\x14\x15\x16\x17"
38+ "\x18\x19\x1a\x1b\x1c\x1d\x1e",
39+ .ctext = "\x58\x8c\x97\x9a\x61\xc6\x63\xd2"
40+ "\xf0\x66\xd0\xc2\xc0\xf9\x89\x80"
41+ "\x6d\x5f\x6b\x61\xda\xc3\x84",
42+ .data_len = 23,
43+ .tag = "\x17\xe8\xd1\x2c\xfd\xf9\x26\xe0",
44+ .tag_len = 8,
45+ },
46+ {
47+ .name = "RFC 3610 Packet Vector #5",
48+ .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
49+ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
50+ .key_len = 16,
51+ .nonce = "\x00\x00\x00\x07\x06\x05\x04\xa0"
52+ "\xa1\xa2\xa3\xa4\xa5",
53+ .nonce_len = 13,
54+ .ad = "\x00\x01\x02\x03\x04\x05\x06\x07"
55+ "\x08\x09\x0a\x0b",
56+ .ad_len = 12,
57+ .ptext = "\x0c\x0d\x0e\x0f\x10\x11\x12\x13"
58+ "\x14\x15\x16\x17\x18\x19\x1a\x1b"
59+ "\x1c\x1d\x1e\x1f",
60+ .ctext = "\xdc\xf1\xfb\x7b\x5d\x9e\x23\xfb"
61+ "\x9d\x4e\x13\x12\x53\x65\x8a\xd8"
62+ "\x6e\xbd\xca\x3e",
63+ .data_len = 20,
64+ .tag = "\x51\xe8\x3f\x07\x7d\x9c\x2d\x93",
65+ .tag_len = 8,
66+ },
67+ {
68+ .name = "RFC 3610 Packet Vector #9",
69+ .key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
70+ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
71+ .key_len = 16,
72+ .nonce = "\x00\x00\x00\x0b\x0a\x09\x08\xa0"
73+ "\xa1\xa2\xa3\xa4\xa5",
74+ .nonce_len = 13,
75+ .ad = "\x00\x01\x02\x03\x04\x05\x06\x07",
76+ .ad_len = 8,
77+ .ptext = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
78+ "\x10\x11\x12\x13\x14\x15\x16\x17"
79+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
80+ "\x20",
81+ .ctext = "\x82\x53\x1a\x60\xcc\x24\x94\x5a"
82+ "\x4b\x82\x79\x18\x1a\xb5\xc8\x4d"
83+ "\xf2\x1c\xe7\xf9\xb7\x3f\x42\xe1"
84+ "\x97",
85+ .data_len = 25,
86+ .tag = "\xea\x9c\x07\xe5\x6b\x5e\xb1\x7e"
87+ "\x5f\x4e",
88+ .tag_len = 10,
89+ },
90+ {
91+ .name = "NIST SP 800-38C Example 1",
92+ .key = "\x40\x41\x42\x43\x44\x45\x46\x47"
93+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
94+ .key_len = 16,
95+ .nonce = "\x10\x11\x12\x13\x14\x15\x16",
96+ .nonce_len = 7,
97+ .ad = "\x00\x01\x02\x03\x04\x05\x06\x07",
98+ .ad_len = 8,
99+ .ptext = "\x20\x21\x22\x23",
100+ .ctext = "\x71\x62\x01\x5b",
101+ .data_len = 4,
102+ .tag = "\x4d\xac\x25\x5d",
103+ .tag_len = 4,
104+ },
105+ {
106+ .name = "NIST SP 800-38C Example 2",
107+ .key = "\x40\x41\x42\x43\x44\x45\x46\x47"
108+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
109+ .key_len = 16,
110+ .nonce = "\x10\x11\x12\x13\x14\x15\x16\x17",
111+ .nonce_len = 8,
112+ .ad = "\x00\x01\x02\x03\x04\x05\x06\x07"
113+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
114+ .ad_len = 16,
115+ .ptext = "\x20\x21\x22\x23\x24\x25\x26\x27"
116+ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
117+ .ctext = "\xd2\xa1\xf0\xe0\x51\xea\x5f\x62"
118+ "\x08\x1a\x77\x92\x07\x3d\x59\x3d",
119+ .data_len = 16,
120+ .tag = "\x1f\xc6\x4f\xbf\xac\xcd",
121+ .tag_len = 6,
122+ },
123+ {
124+ .name = "NIST SP 800-38C Example 3",
125+ .key = "\x40\x41\x42\x43\x44\x45\x46\x47"
126+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
127+ .key_len = 16,
128+ .nonce = "\x10\x11\x12\x13\x14\x15\x16\x17"
129+ "\x18\x19\x1a\x1b",
130+ .nonce_len = 12,
131+ .ad = "\x00\x01\x02\x03\x04\x05\x06\x07"
132+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
133+ "\x10\x11\x12\x13",
134+ .ad_len = 20,
135+ .ptext = "\x20\x21\x22\x23\x24\x25\x26\x27"
136+ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
137+ "\x30\x31\x32\x33\x34\x35\x36\x37",
138+ .ctext = "\xe3\xb2\x01\xa9\xf5\xb7\x1a\x7a"
139+ "\x9b\x1c\xea\xec\xcd\x97\xe7\x0b"
140+ "\x61\x76\xaa\xd9\xa4\x42\x8a\xa5",
141+ .data_len = 24,
142+ .tag = "\x48\x43\x92\xfb\xc1\xb0\x99\x51",
143+ .tag_len = 8,
144+ },
145+};
146+
147+static void test_aes_ccm_one_test_vector(struct kunit *test,
148+ const struct aes_ccm_testvec *tv)
149+{
150+ u8 *ctext = alloc_buf(test, tv->data_len);
151+ u8 *decrypted = alloc_buf(test, tv->data_len);
152+ u8 *tag = alloc_buf(test, tv->tag_len);
153+ struct aes_ccm_key key;
154+ int err;
155+
156+ err = aes_ccm_preparekey(&key, tv->key, tv->key_len, tv->tag_len);
157+ KUNIT_ASSERT_EQ_MSG(test, 0, err, "Failed to prepare key for %s",
158+ tv->name);
159+
160+ err = aes_ccm_encrypt(ctext, tv->ptext, tv->data_len, tag, tv->ad,
161+ tv->ad_len, tv->nonce, tv->nonce_len, &key);
162+ KUNIT_ASSERT_EQ_MSG(test, 0, err, "Encryption failed for %s", tv->name);
163+ KUNIT_ASSERT_MEMEQ_MSG(test, tv->ctext, ctext, tv->data_len,
164+ "Wrong ciphertext for %s", tv->name);
165+ KUNIT_ASSERT_MEMEQ_MSG(test, tag, tv->tag, tv->tag_len,
166+ "Wrong tag for %s", tv->name);
167+
168+ err = aes_ccm_decrypt(decrypted, ctext, tv->data_len, tag, tv->ad,
169+ tv->ad_len, tv->nonce, tv->nonce_len, &key);
170+ KUNIT_ASSERT_EQ_MSG(test, 0, err, "Decryption failed for %s", tv->name);
171+ KUNIT_ASSERT_MEMEQ_MSG(test, tv->ptext, decrypted, tv->data_len,
172+ "Wrong plaintext for %s", tv->name);
173+}
174+
175+static void test_aes_ccm_test_vectors(struct kunit *test)
176+{
177+ for (size_t i = 0; i < ARRAY_SIZE(aes_ccm_testvecs); i++)
178+ test_aes_ccm_one_test_vector(test, &aes_ccm_testvecs[i]);
179+}
180+
181+/*
182+ * Test NIST SP 800-38C Example 4, which uses a deterministically-generated
183+ * 65536-byte associated data string.
184+ */
185+static void test_aes_ccm_nist_sp800_38c_example4(struct kunit *test)
186+{
187+ struct aes_ccm_testvec tv = {
188+ .name = "NIST SP 800-38C Example 4",
189+ .key_len = 16,
190+ .nonce_len = 13,
191+ .ad_len = 65536,
192+ .ctext = "\x69\x91\x5d\xad\x1e\x84\xc6\x37"
193+ "\x6a\x68\xc2\x96\x7e\x4d\xab\x61"
194+ "\x5a\xe0\xfd\x1f\xae\xc4\x4c\xc4"
195+ "\x84\x82\x85\x29\x46\x3c\xcf\x72",
196+ .data_len = 32,
197+ .tag = "\xb4\xac\x6b\xec\x93\xe8\x59\x8e"
198+ "\x7f\x0d\xad\xbc\xea\x5b",
199+ .tag_len = 14,
200+ };
201+ u8 *key, *nonce, *ad, *ptext;
202+
203+ key = alloc_buf(test, tv.key_len);
204+ for (size_t i = 0; i < tv.key_len; i++)
205+ key[i] = 0x40 + i;
206+
207+ nonce = alloc_guarded_buf(test, tv.nonce_len);
208+ for (size_t i = 0; i < tv.nonce_len; i++)
209+ nonce[i] = 0x10 + i;
210+
211+ ad = alloc_guarded_buf(test, tv.ad_len);
212+ for (size_t i = 0; i < tv.ad_len; i++)
213+ ad[i] = (u8)i;
214+
215+ ptext = alloc_guarded_buf(test, tv.data_len);
216+ for (size_t i = 0; i < tv.data_len; i++)
217+ ptext[i] = 0x20 + i;
218+
219+ tv.key = key;
220+ tv.nonce = nonce;
221+ tv.ad = ad;
222+ tv.ptext = ptext;
223+
224+ test_aes_ccm_one_test_vector(test, &tv);
225+}
226+
227+static const size_t aes_ccm_valid_key_lens[] = { 16, 24, 32 };
228+#define AEAD_MAX_KEY_LEN 32
229+#define AEAD_VALID_KEY_LENS aes_ccm_valid_key_lens
230+
231+static const size_t aes_ccm_valid_nonce_lens[] = { 7, 8, 9, 10, 11, 12, 13 };
232+#define AEAD_MAX_NONCE_LEN 13
233+#define AEAD_VALID_NONCE_LENS aes_ccm_valid_nonce_lens
234+
235+static const size_t aes_ccm_valid_tag_lens[] = { 4, 6, 8, 10, 12, 14, 16 };
236+#define AEAD_MAX_TAG_LEN 16
237+#define AEAD_VALID_TAG_LENS aes_ccm_valid_tag_lens
238+
239+#define AEAD_KEY aes_ccm_key
240+#define AEAD_CTX aes_ccm_ctx
241+#define AEAD_PREPAREKEY aes_ccm_preparekey
242+#define AEAD_ENCRYPT aes_ccm_encrypt
243+#define AEAD_DECRYPT aes_ccm_decrypt
244+
245+#define AEAD_INIT aes_ccm_init
246+#define AEAD_AUTH_UPDATE aes_ccm_auth_update
247+#define AEAD_ENCRYPT_UPDATE aes_ccm_encrypt_update
248+#define AEAD_DECRYPT_UPDATE aes_ccm_decrypt_update
249+#define AEAD_ENCRYPT_FINAL aes_ccm_encrypt_final
250+#define AEAD_DECRYPT_FINAL aes_ccm_decrypt_final
251+
252+/* This value was generated by gen-aead-testvecs.py. */
253+static const u8 aes_ccm_monte_carlo_checksum[BLAKE2S_HASH_SIZE] = {
254+ 0x70, 0x1c, 0xde, 0xa4, 0xe2, 0x03, 0x50, 0xb2, 0xf5, 0x9e, 0x61,
255+ 0x66, 0xe4, 0xe5, 0x13, 0x1a, 0x00, 0x95, 0x34, 0x03, 0xb7, 0x61,
256+ 0x2c, 0xdb, 0xc3, 0x15, 0x36, 0x84, 0x93, 0x7f, 0xb4, 0x5b,
257+};
258+#define AEAD_MONTE_CARLO_CHECKSUM aes_ccm_monte_carlo_checksum
259+
260+#include "aead-test-template.h"
261+
262+/*
263+ * Test that for each AES-CCM nonce length, the message length is validated
264+ * against the correct corresponding maximum message length.
265+ */
266+static void test_aes_ccm_data_len_too_large(struct kunit *test)
267+{
268+ static const struct {
269+ size_t nonce_len;
270+ u64 max_data_len;
271+ } lens[] = {
272+ /* clang-format off */
273+ { 7, 0xffffffffffffffff }, /* U64_MAX */
274+ { 8, 0xffffffffffffff },
275+ { 9, 0xffffffffffff },
276+ { 10, 0xffffffffff },
277+ { 11, 0xffffffff },
278+ { 12, 0xffffff },
279+ { 13, 0xffff },
280+ /* clang-format on */
281+ };
282+ u8 nonce[13] = {};
283+ u8 raw_key[AES_KEYSIZE_256] = {};
284+ int err;
285+ struct aes_ccm_key *key = alloc_buf(test, sizeof(*key));
286+ struct aes_ccm_ctx ctx;
287+
288+ err = aes_ccm_preparekey(key, raw_key, sizeof(raw_key), 16);
289+ KUNIT_ASSERT_EQ(test, 0, err);
290+
291+ for (size_t i = 0; i < ARRAY_SIZE(lens); i++) {
292+ size_t nonce_len = lens[i].nonce_len;
293+ u64 max_data_len = lens[i].max_data_len;
294+
295+ /* data_len <= max_data_len should be accepted. */
296+ err = aes_ccm_init(&ctx, 0, 0, nonce, nonce_len, key);
297+ KUNIT_ASSERT_EQ_MSG(
298+ test, 0, err,
299+ "data_len=0 wasn't accepted with nonce_len=%zu",
300+ nonce_len);
301+ err = aes_ccm_init(&ctx, max_data_len, 0, nonce, nonce_len,
302+ key);
303+ KUNIT_ASSERT_EQ_MSG(
304+ test, 0, err,
305+ "data_len=%llu wasn't accepted with nonce_len=%zu",
306+ max_data_len, nonce_len);
307+
308+ /* data_len > max_data_len should be rejected. */
309+ if (max_data_len == U64_MAX)
310+ continue;
311+ err = aes_ccm_init(&ctx, max_data_len + 1, 0, nonce, nonce_len,
312+ key);
313+ KUNIT_ASSERT_EQ_MSG(
314+ test, -EOVERFLOW, err,
315+ "data_len=%llu wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_init)",
316+ max_data_len + 1, nonce_len);
317+ if (max_data_len + 1 <= SIZE_MAX) {
318+ err = aes_ccm_encrypt(NULL, NULL, max_data_len + 1,
319+ NULL, NULL, 0, nonce, nonce_len,
320+ key);
321+ KUNIT_ASSERT_EQ_MSG(
322+ test, -EOVERFLOW, err,
323+ "data_len=%llu wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_encrypt)",
324+ max_data_len + 1, nonce_len);
325+ err = aes_ccm_decrypt(NULL, NULL, max_data_len + 1,
326+ NULL, NULL, 0, nonce, nonce_len,
327+ key);
328+ KUNIT_ASSERT_EQ_MSG(
329+ test, -EOVERFLOW, err,
330+ "data_len=%llu wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_decrypt)",
331+ max_data_len + 1, nonce_len);
332+ }
333+ err = aes_ccm_init(&ctx, U64_MAX, 0, nonce, nonce_len, key);
334+ KUNIT_ASSERT_EQ_MSG(
335+ test, -EOVERFLOW, err,
336+ "data_len=U64_MAX wasn't rejected with -EOVERFLOW with nonce_len=%zu (aes_ccm_init)",
337+ nonce_len);
338+ }
339+}
340+
341+static struct kunit_case aes_ccm_test_cases[] = {
342+ KUNIT_CASE(test_aes_ccm_test_vectors),
343+ KUNIT_CASE(test_aes_ccm_nist_sp800_38c_example4),
344+ KUNIT_CASE(test_aes_ccm_data_len_too_large),
345+ AEAD_KUNIT_CASES,
346+ {},
347+};
348+
349+static struct kunit_suite aes_ccm_test_suite = {
350+ .name = "aes_ccm",
351+ .test_cases = aes_ccm_test_cases,
352+};
353+kunit_test_suite(aes_ccm_test_suite);
354+
355+MODULE_DESCRIPTION("KUnit tests and benchmark for AES-CCM");
356+MODULE_LICENSE("GPL");
scripts/crypto/gen-aead-testvecs.py+60−0
@@ -0,0 +1,60 @@
1+#!/usr/bin/env python3
2+# SPDX-License-Identifier: GPL-2.0-or-later
3+#
4+# Script that generates known-good data used in the AEAD tests.
5+#
6+# Requires that python-cryptography be installed.
7+#
8+# Copyright 2026 Google LLC
9+
10+import hashlib
11+import sys
12+import cryptography.hazmat.primitives.ciphers.aead
13+
14+
15+# Deterministically generate 'length' random bytes.
16+def rand_bytes(length):
17+ seed = length
18+ out = []
19+ for _ in range(length):
20+ seed = (seed * 25214903917 + 11) % 2**48
21+ out.append((seed >> 16) % 256)
22+ return bytes(out)
23+
24+
25+# Deterministically generate many different AEAD inputs using exactly the same
26+# method that the test uses; encrypt them using an independent implementation of
27+# the algorithm; compute the checksum of all the resulting (ciphertext, authtag)
28+# pairs concatenated to each other; and print the checksum as a C struct.
29+def gen_monte_carlo_checksum(alg):
30+ blake2s = hashlib.blake2s()
31+ for data_len in range(1025):
32+ ad_len = data_len % 293
33+ pt = rand_bytes(data_len)
34+ ad = rand_bytes(ad_len)
35+ if alg == "aes-ccm":
36+ key_len = [16, 24, 32][data_len % 3]
37+ key = rand_bytes(key_len)
38+ nonce = rand_bytes([7, 8, 9, 10, 11, 12, 13][data_len % 7])
39+ tag_len = [4, 6, 8, 10, 12, 14, 16][data_len % 7]
40+ ccm = cryptography.hazmat.primitives.ciphers.aead.AESCCM(
41+ key, tag_length=tag_len
42+ )
43+ ct_and_tag = ccm.encrypt(nonce, pt, ad)
44+
45+ blake2s.update(ct_and_tag)
46+
47+ name = f"{alg.replace('-', '_')}_monte_carlo_checksum"
48+ value = blake2s.digest()
49+ print(f"static const u8 {name}[BLAKE2S_HASH_SIZE] = {{")
50+ for i in range(0, len(value), 11):
51+ line = "\t" + "".join(f"0x{b:02x}, " for b in value[i : i + 11])
52+ print(f"{line.rstrip()}")
53+ print("};")
54+
55+
56+if len(sys.argv) != 2 or sys.argv[1] not in ("aes-ccm"):
57+ sys.stderr.write("Usage: gen-aead-testvecs.py [aes-ccm]\n")
58+ sys.exit(1)
59+
60+gen_monte_carlo_checksum(sys.argv[1])