[PATCH 5/5] lib/crypto: xwing: Add KUnit tests for X-Wing KEM
From: Eric Biggers
Date: Mon May 25 2026 - 14:46:58 EST
Add a KUnit test suite for the X-Wing key encapsulation mechanism.
This includes:
- Test key generation, encapsulation, and decapsulation against the
test vectors from the X-Wing specification
- Test encapsulation/decapsulation round trips
- Benchmark key generation, encapsulation, and decapsulation
This isn't intended to fully test the underlying KEMs. Those already
have their own KUnit tests.
Signed-off-by: Eric Biggers <ebiggers@xxxxxxxxxx>
---
lib/crypto/.kunitconfig | 1 +
lib/crypto/tests/Kconfig | 9 ++
lib/crypto/tests/Makefile | 1 +
lib/crypto/tests/xwing-testvecs.h | 138 ++++++++++++++++++++++++
lib/crypto/tests/xwing_kunit.c | 129 ++++++++++++++++++++++
scripts/crypto/import-xwing-testvecs.py | 111 +++++++++++++++++++
6 files changed, 389 insertions(+)
create mode 100644 lib/crypto/tests/xwing-testvecs.h
create mode 100644 lib/crypto/tests/xwing_kunit.c
create mode 100755 scripts/crypto/import-xwing-testvecs.py
diff --git a/lib/crypto/.kunitconfig b/lib/crypto/.kunitconfig
index 32e5b4471da8..b42d8aaed244 100644
--- a/lib/crypto/.kunitconfig
+++ b/lib/crypto/.kunitconfig
@@ -17,5 +17,6 @@ CONFIG_CRYPTO_LIB_POLYVAL_KUNIT_TEST=y
CONFIG_CRYPTO_LIB_SHA1_KUNIT_TEST=y
CONFIG_CRYPTO_LIB_SHA256_KUNIT_TEST=y
CONFIG_CRYPTO_LIB_SHA512_KUNIT_TEST=y
CONFIG_CRYPTO_LIB_SHA3_KUNIT_TEST=y
CONFIG_CRYPTO_LIB_SM3_KUNIT_TEST=y
+CONFIG_CRYPTO_LIB_XWING_KUNIT_TEST=y
diff --git a/lib/crypto/tests/Kconfig b/lib/crypto/tests/Kconfig
index 0a110a0733d2..485b5fd81539 100644
--- a/lib/crypto/tests/Kconfig
+++ b/lib/crypto/tests/Kconfig
@@ -147,10 +147,18 @@ config CRYPTO_LIB_SM3_KUNIT_TEST
default KUNIT_ALL_TESTS
select CRYPTO_LIB_BENCHMARK_VISIBLE
help
KUnit tests for the SM3 cryptographic hash function.
+config CRYPTO_LIB_XWING_KUNIT_TEST
+ tristate "KUnit tests for X-Wing" if !KUNIT_ALL_TESTS
+ depends on KUNIT && CRYPTO_LIB_XWING
+ default KUNIT_ALL_TESTS
+ select CRYPTO_LIB_BENCHMARK_VISIBLE
+ help
+ KUnit tests for the X-Wing key encapsulation mechanism.
+
config CRYPTO_LIB_ENABLE_ALL_FOR_KUNIT
tristate "Enable all crypto library code for KUnit tests"
depends on KUNIT
select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_BLAKE2B
@@ -165,10 +173,11 @@ config CRYPTO_LIB_ENABLE_ALL_FOR_KUNIT
select CRYPTO_LIB_SHA1
select CRYPTO_LIB_SHA256
select CRYPTO_LIB_SHA512
select CRYPTO_LIB_SHA3
select CRYPTO_LIB_SM3
+ select CRYPTO_LIB_XWING
help
Enable all the crypto library code that has KUnit tests.
Enable this only if you'd like to test all the crypto library code,
even code that wouldn't otherwise need to be built.
diff --git a/lib/crypto/tests/Makefile b/lib/crypto/tests/Makefile
index 3a73d2f33f75..acc8ddc19978 100644
--- a/lib/crypto/tests/Makefile
+++ b/lib/crypto/tests/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_CRYPTO_LIB_POLYVAL_KUNIT_TEST) += polyval_kunit.o
obj-$(CONFIG_CRYPTO_LIB_SHA1_KUNIT_TEST) += sha1_kunit.o
obj-$(CONFIG_CRYPTO_LIB_SHA256_KUNIT_TEST) += sha224_kunit.o sha256_kunit.o
obj-$(CONFIG_CRYPTO_LIB_SHA512_KUNIT_TEST) += sha384_kunit.o sha512_kunit.o
obj-$(CONFIG_CRYPTO_LIB_SHA3_KUNIT_TEST) += sha3_kunit.o
obj-$(CONFIG_CRYPTO_LIB_SM3_KUNIT_TEST) += sm3_kunit.o
+obj-$(CONFIG_CRYPTO_LIB_XWING_KUNIT_TEST) += xwing_kunit.o
diff --git a/lib/crypto/tests/xwing-testvecs.h b/lib/crypto/tests/xwing-testvecs.h
new file mode 100644
index 000000000000..057ca37c7ef7
--- /dev/null
+++ b/lib/crypto/tests/xwing-testvecs.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* This file was generated by import-xwing-testvecs.py */
+
+static const struct xwing_testvec {
+ u8 seed[XWING_SEED_BYTES];
+ u8 pk_hash[SHA3_256_DIGEST_SIZE];
+ u8 sk_hash[SHA3_256_DIGEST_SIZE];
+ u8 eseed[XWING_ESEED_BYTES];
+ u8 ct_hash[SHA3_256_DIGEST_SIZE];
+ u8 ss[XWING_SHARED_SECRET_BYTES];
+} xwing_testvecs[] = {
+ {
+ .seed = {
+ 0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d,
+ 0x61, 0x60, 0x45, 0x50, 0x76, 0x05, 0x85, 0x3e,
+ 0xd7, 0x3b, 0x80, 0x93, 0xf6, 0xef, 0xbc, 0x88,
+ 0xeb, 0x1a, 0x6e, 0xac, 0xfa, 0x66, 0xef, 0x26,
+ },
+ .pk_hash = {
+ 0x51, 0x21, 0x74, 0x59, 0x04, 0x64, 0x3a, 0xd9,
+ 0xdf, 0xac, 0xca, 0x78, 0x69, 0x29, 0x2c, 0x19,
+ 0xa8, 0xa6, 0x95, 0x33, 0xb5, 0x3e, 0x60, 0x66,
+ 0x6b, 0x7d, 0xb9, 0x10, 0xb4, 0xad, 0x63, 0x67,
+ },
+ .sk_hash = {
+ 0x1f, 0x32, 0xc4, 0xb1, 0xa9, 0x65, 0xfa, 0x90,
+ 0x33, 0xb7, 0x85, 0xc2, 0xa2, 0x4b, 0x81, 0xdc,
+ 0xd1, 0xbc, 0x81, 0xe9, 0xbe, 0x0c, 0x20, 0x4f,
+ 0x20, 0xfd, 0xd1, 0x50, 0x92, 0xbb, 0x6d, 0x4a,
+ },
+ .eseed = {
+ 0x3c, 0xb1, 0xee, 0xa9, 0x88, 0x00, 0x4b, 0x93,
+ 0x10, 0x3c, 0xfb, 0x0a, 0xee, 0xfd, 0x2a, 0x68,
+ 0x6e, 0x01, 0xfa, 0x4a, 0x58, 0xe8, 0xa3, 0x63,
+ 0x9c, 0xa8, 0xa1, 0xe3, 0xf9, 0xae, 0x57, 0xe2,
+ 0x35, 0xb8, 0xcc, 0x87, 0x3c, 0x23, 0xdc, 0x62,
+ 0xb8, 0xd2, 0x60, 0x16, 0x9a, 0xfa, 0x2f, 0x75,
+ 0xab, 0x91, 0x6a, 0x58, 0xd9, 0x74, 0x91, 0x88,
+ 0x35, 0xd2, 0x5e, 0x6a, 0x43, 0x50, 0x85, 0xb2,
+ },
+ .ct_hash = {
+ 0xc0, 0xab, 0xd1, 0x49, 0xf8, 0x3f, 0x45, 0x32,
+ 0x4a, 0xc3, 0xa7, 0xdd, 0xc7, 0x60, 0x6c, 0x71,
+ 0xf2, 0x57, 0xe5, 0xea, 0x86, 0x11, 0x35, 0x22,
+ 0x83, 0x4a, 0x0e, 0xe1, 0xbc, 0xb3, 0x4e, 0x3e,
+ },
+ .ss = {
+ 0xd2, 0xdf, 0x05, 0x22, 0x12, 0x8f, 0x09, 0xdd,
+ 0x8e, 0x2c, 0x92, 0xb1, 0xe9, 0x05, 0xc7, 0x93,
+ 0xd8, 0xf5, 0x7a, 0x54, 0xc3, 0xda, 0x25, 0x86,
+ 0x1f, 0x10, 0xbf, 0x4c, 0xa6, 0x13, 0xe3, 0x84,
+ },
+ },
+ {
+ .seed = {
+ 0xba, 0xdf, 0xd6, 0xdf, 0xaa, 0xc3, 0x59, 0xa5,
+ 0xef, 0xbb, 0x7b, 0xcc, 0x4b, 0x59, 0xd5, 0x38,
+ 0xdf, 0x9a, 0x04, 0x30, 0x2e, 0x10, 0xc8, 0xbc,
+ 0x1c, 0xbf, 0x1a, 0x0b, 0x3a, 0x51, 0x20, 0xea,
+ },
+ .pk_hash = {
+ 0x79, 0x9b, 0x60, 0x16, 0xe5, 0xda, 0xa5, 0x6f,
+ 0xfa, 0x1b, 0x5e, 0x79, 0xf7, 0xca, 0xf7, 0x34,
+ 0x13, 0xce, 0xec, 0xd6, 0xdf, 0x42, 0x86, 0x42,
+ 0x40, 0x4c, 0xac, 0x41, 0xdd, 0xee, 0x48, 0x53,
+ },
+ .sk_hash = {
+ 0x1d, 0x31, 0x12, 0xef, 0x3c, 0xb6, 0x68, 0x10,
+ 0xd3, 0xad, 0x64, 0x18, 0x57, 0x9a, 0x5c, 0x8c,
+ 0xd9, 0xd1, 0xfa, 0x13, 0xf6, 0xdd, 0xc7, 0xc1,
+ 0x29, 0x7c, 0x09, 0x63, 0xe8, 0xbb, 0xb0, 0x24,
+ },
+ .eseed = {
+ 0x17, 0xcd, 0xa7, 0xcf, 0xad, 0x76, 0x5f, 0x56,
+ 0x23, 0x47, 0x4d, 0x36, 0x8c, 0xcc, 0xa8, 0xaf,
+ 0x00, 0x07, 0xcd, 0x9f, 0x5e, 0x4c, 0x84, 0x9f,
+ 0x16, 0x7a, 0x58, 0x0b, 0x14, 0xaa, 0xbd, 0xef,
+ 0xae, 0xe7, 0xee, 0xf4, 0x7c, 0xb0, 0xfc, 0xa9,
+ 0x76, 0x7b, 0xe1, 0xfd, 0xa6, 0x94, 0x19, 0xdf,
+ 0xb9, 0x27, 0xe9, 0xdf, 0x07, 0x34, 0x8b, 0x19,
+ 0x66, 0x91, 0xab, 0xae, 0xb5, 0x80, 0xb3, 0x2d,
+ },
+ .ct_hash = {
+ 0x76, 0x80, 0xb7, 0xba, 0x47, 0xae, 0x09, 0xba,
+ 0xc4, 0xb4, 0x30, 0x01, 0xed, 0xce, 0xf9, 0xd9,
+ 0x8e, 0x50, 0xdf, 0x20, 0x02, 0x6e, 0x70, 0xba,
+ 0x6a, 0x42, 0x44, 0x47, 0xe1, 0xf2, 0xb9, 0x61,
+ },
+ .ss = {
+ 0xf2, 0xe8, 0x62, 0x41, 0xc6, 0x4d, 0x60, 0xf6,
+ 0x64, 0x9f, 0xbc, 0x6c, 0x5b, 0x7d, 0x17, 0x18,
+ 0x0b, 0x78, 0x0a, 0x3f, 0x34, 0x35, 0x5e, 0x64,
+ 0xa8, 0x57, 0x49, 0x94, 0x9c, 0x45, 0xf1, 0x50,
+ },
+ },
+ {
+ .seed = {
+ 0xef, 0x58, 0x53, 0x8b, 0x8d, 0x23, 0xf8, 0x77,
+ 0x32, 0xea, 0x63, 0xb0, 0x2b, 0x4f, 0xa0, 0xf4,
+ 0x87, 0x33, 0x60, 0xe2, 0x84, 0x19, 0x28, 0xcd,
+ 0x60, 0xdd, 0x4c, 0xee, 0x8c, 0xc0, 0xd4, 0xc9,
+ },
+ .pk_hash = {
+ 0x1e, 0xf0, 0xc9, 0x9a, 0x06, 0x02, 0x64, 0x50,
+ 0x56, 0x49, 0x57, 0xa5, 0x40, 0x2a, 0x78, 0x8f,
+ 0xef, 0xfb, 0xbe, 0xfd, 0xcc, 0xe5, 0x5d, 0x25,
+ 0xde, 0x25, 0x4d, 0x0a, 0x49, 0xeb, 0x09, 0x5a,
+ },
+ .sk_hash = {
+ 0x75, 0x1b, 0x92, 0x96, 0x9e, 0xc1, 0x7a, 0x6a,
+ 0x5b, 0x4f, 0x95, 0xc0, 0xd2, 0xd9, 0x7d, 0xbc,
+ 0x0d, 0xa7, 0xb0, 0x98, 0x96, 0xaf, 0x69, 0xbd,
+ 0x69, 0xac, 0x7a, 0xb6, 0x19, 0x9e, 0x40, 0x3d,
+ },
+ .eseed = {
+ 0x22, 0xa9, 0x61, 0x88, 0xd0, 0x32, 0x67, 0x5c,
+ 0x8a, 0xc8, 0x50, 0x93, 0x3c, 0x7a, 0xff, 0x15,
+ 0x33, 0xb9, 0x4c, 0x83, 0x4a, 0xdb, 0xb6, 0x9c,
+ 0x61, 0x15, 0xba, 0xd4, 0x69, 0x2d, 0x86, 0x19,
+ 0xf9, 0x0b, 0x0c, 0xdf, 0x8a, 0x7b, 0x9c, 0x26,
+ 0x40, 0x29, 0xac, 0x18, 0x5b, 0x70, 0xb8, 0x3f,
+ 0x28, 0x01, 0xf2, 0xf4, 0xb3, 0xf7, 0x0c, 0x59,
+ 0x3e, 0xa3, 0xae, 0xeb, 0x61, 0x3a, 0x7f, 0x1b,
+ },
+ .ct_hash = {
+ 0x30, 0x88, 0x68, 0x8d, 0x63, 0x20, 0x1d, 0x5d,
+ 0x84, 0x41, 0x70, 0xb7, 0x9f, 0x14, 0x8b, 0x27,
+ 0x91, 0xc1, 0x5f, 0x34, 0x6f, 0xf6, 0xf8, 0xbd,
+ 0x55, 0x98, 0x07, 0xfb, 0xd4, 0x42, 0xf9, 0x1f,
+ },
+ .ss = {
+ 0x95, 0x3f, 0x7f, 0x4e, 0x8c, 0x5b, 0x50, 0x49,
+ 0xbd, 0xc7, 0x71, 0xd1, 0xdf, 0xfa, 0xda, 0x0d,
+ 0xd9, 0x61, 0x47, 0x7d, 0x1a, 0x2a, 0xe0, 0x98,
+ 0x8b, 0xaa, 0x7e, 0xa6, 0x89, 0x8d, 0x89, 0x3f,
+ },
+ },
+};
diff --git a/lib/crypto/tests/xwing_kunit.c b/lib/crypto/tests/xwing_kunit.c
new file mode 100644
index 000000000000..81c4f41e9cab
--- /dev/null
+++ b/lib/crypto/tests/xwing_kunit.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit tests for X-Wing
+ *
+ * This should be run together with the mlkem and curve25519 KUnit tests.
+ *
+ * Copyright 2026 Google LLC
+ */
+#include <crypto/sha3.h>
+#include <crypto/xwing.h>
+#include <kunit/test.h>
+
+#include "xwing-testvecs.h"
+
+struct xwing_bufs {
+ u8 pk[XWING_PUBLIC_KEY_BYTES];
+ u8 sk[XWING_SECRET_KEY_BYTES];
+ u8 ct[XWING_CIPHERTEXT_BYTES];
+ u8 ss[XWING_SHARED_SECRET_BYTES];
+ u8 pk_hash[SHA3_256_DIGEST_SIZE];
+ u8 sk_hash[SHA3_256_DIGEST_SIZE];
+ u8 ct_hash[SHA3_256_DIGEST_SIZE];
+};
+
+static struct xwing_bufs *alloc_bufs(struct kunit *test)
+{
+ struct xwing_bufs *bufs =
+ kunit_kmalloc(test, sizeof(*bufs), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, bufs);
+ return bufs;
+}
+
+static void test_xwing_rfc_testvecs(struct kunit *test)
+{
+ struct xwing_bufs *bufs = alloc_bufs(test);
+
+ for (size_t i = 0; i < ARRAY_SIZE(xwing_testvecs); i++) {
+ const struct xwing_testvec *tv = &xwing_testvecs[i];
+
+ KUNIT_ASSERT_EQ(test, 0,
+ xwing_keygen_internal(bufs->pk, bufs->sk,
+ tv->seed));
+ sha3_256(bufs->pk, sizeof(bufs->pk), bufs->pk_hash);
+ sha3_256(bufs->sk, sizeof(bufs->sk), bufs->sk_hash);
+ KUNIT_ASSERT_MEMEQ(test, tv->pk_hash, bufs->pk_hash,
+ sizeof(tv->pk_hash));
+ KUNIT_ASSERT_MEMEQ(test, tv->sk_hash, bufs->sk_hash,
+ sizeof(tv->sk_hash));
+
+ KUNIT_ASSERT_EQ(test, 0,
+ xwing_encaps_internal(bufs->ct, bufs->ss,
+ bufs->pk, tv->eseed));
+ sha3_256(bufs->ct, sizeof(bufs->ct), bufs->ct_hash);
+ KUNIT_ASSERT_MEMEQ(test, tv->ct_hash, bufs->ct_hash,
+ sizeof(tv->ct_hash));
+ KUNIT_ASSERT_MEMEQ(test, tv->ss, bufs->ss, sizeof(bufs->ss));
+
+ memset(bufs->ss, 0xff, sizeof(bufs->ss));
+ KUNIT_ASSERT_EQ(test, 0,
+ xwing_decaps(bufs->ss, bufs->ct, bufs->sk));
+ KUNIT_ASSERT_MEMEQ(test, tv->ss, bufs->ss, sizeof(bufs->ss));
+ }
+}
+
+static void test_xwing_round_trip(struct kunit *test)
+{
+ struct xwing_bufs *bufs = alloc_bufs(test);
+ u8 ss2[XWING_SHARED_SECRET_BYTES];
+
+ for (int i = 0; i < 20; i++) {
+ KUNIT_ASSERT_EQ(test, 0, xwing_keygen(bufs->pk, bufs->sk));
+ KUNIT_ASSERT_EQ(test, 0,
+ xwing_encaps(bufs->ct, bufs->ss, bufs->pk));
+ KUNIT_ASSERT_EQ(test, 0, xwing_decaps(ss2, bufs->ct, bufs->sk));
+ KUNIT_ASSERT_MEMEQ(test, bufs->ss, ss2, sizeof(bufs->ss));
+ }
+}
+
+/* Benchmark X-Wing performance. */
+static void benchmark_xwing(struct kunit *test)
+{
+ struct xwing_bufs *bufs = alloc_bufs(test);
+ const int iterations = 100;
+ ktime_t start, end;
+
+ if (!IS_ENABLED(CONFIG_CRYPTO_LIB_BENCHMARK))
+ kunit_skip(test, "not enabled");
+
+ start = ktime_get();
+ for (int i = 0; i < iterations; i++)
+ KUNIT_ASSERT_EQ(test, 0, xwing_keygen(bufs->pk, bufs->sk));
+ end = ktime_get();
+ kunit_info(test, "XWing_KeyGen: %llu ns/op\n",
+ div64_u64(ktime_to_ns(ktime_sub(end, start)), iterations));
+
+ start = ktime_get();
+ for (int i = 0; i < iterations; i++)
+ KUNIT_ASSERT_EQ(test, 0,
+ xwing_encaps(bufs->ct, bufs->ss, bufs->pk));
+ end = ktime_get();
+ kunit_info(test, "XWing_Encaps: %llu ns/op\n",
+ div64_u64(ktime_to_ns(ktime_sub(end, start)), iterations));
+
+ start = ktime_get();
+ for (int i = 0; i < iterations; i++)
+ KUNIT_ASSERT_EQ(test, 0,
+ xwing_decaps(bufs->ss, bufs->ct, bufs->sk));
+ end = ktime_get();
+ kunit_info(test, "XWing_Decaps: %llu ns/op\n",
+ div64_u64(ktime_to_ns(ktime_sub(end, start)), iterations));
+}
+
+static struct kunit_case xwing_test_cases[] = {
+ KUNIT_CASE(test_xwing_rfc_testvecs),
+ KUNIT_CASE(test_xwing_round_trip),
+ KUNIT_CASE(benchmark_xwing),
+ {}
+};
+
+static struct kunit_suite xwing_test_suite = {
+ .name = "xwing",
+ .test_cases = xwing_test_cases,
+};
+kunit_test_suite(xwing_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for X-Wing");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_LICENSE("GPL");
diff --git a/scripts/crypto/import-xwing-testvecs.py b/scripts/crypto/import-xwing-testvecs.py
new file mode 100755
index 000000000000..380685212838
--- /dev/null
+++ b/scripts/crypto/import-xwing-testvecs.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# This script imports test vectors from the X-Wing draft. To use:
+#
+# wget https://www.ietf.org/archive/id/draft-connolly-cfrg-xwing-kem-10.txt
+# $PATH_TO_THIS_SCRIPT draft-connolly-cfrg-xwing-kem-10.txt > lib/crypto/tests/xwing-testvecs.h
+
+import hashlib
+import os
+import sys
+
+SCRIPT_NAME = os.path.basename(__file__)
+FIELD_NAMES = set(["seed", "sk", "pk", "eseed", "ct", "ss"])
+FIRST_FIELD = "seed"
+
+XWING_SEED_BYTES = 32
+XWING_PUBLIC_KEY_BYTES = 1216
+XWING_SECRET_KEY_BYTES = 32
+XWING_ESEED_BYTES = 64
+XWING_CIPHERTEXT_BYTES = 1120
+XWING_SHARED_SECRET_BYTES = 32
+
+
+def check_length(val, expected_len):
+ assert len(val) == expected_len
+ return val
+
+
+class Testvec:
+ def __init__(self, tv):
+ self.seed = check_length(tv["seed"], XWING_SEED_BYTES)
+ self.pk = check_length(tv["pk"], XWING_PUBLIC_KEY_BYTES)
+ self.sk = check_length(tv["sk"], XWING_SECRET_KEY_BYTES)
+ self.eseed = check_length(tv["eseed"], XWING_ESEED_BYTES)
+ self.ct = check_length(tv["ct"], XWING_CIPHERTEXT_BYTES)
+ self.ss = check_length(tv["ss"], XWING_SHARED_SECRET_BYTES)
+
+
+def load_testvecs(file):
+ testvecs = []
+ with open(file) as file:
+ in_appendix = False
+ tv = None
+ cur_field = None
+ for line in file:
+ if line.startswith("Appendix C."):
+ in_appendix = True
+ elif line.startswith("Appendix D."):
+ break
+ fields = line.split()
+ if len(fields) == 0 or not in_appendix:
+ continue
+ if fields[0].strip() in FIELD_NAMES:
+ cur_field = fields[0].strip()
+ if cur_field == FIRST_FIELD:
+ if tv:
+ testvecs.append(Testvec(tv))
+ tv = {}
+ tv[cur_field] = b""
+ if len(fields) == 1:
+ continue
+ possible_data = fields[1].strip()
+ elif not cur_field:
+ continue
+ else:
+ possible_data = fields[0].strip()
+ try:
+ data = bytes.fromhex(possible_data)
+ tv[cur_field] += data
+ except ValueError:
+ pass
+ testvecs.append(Testvec(tv))
+ return testvecs
+
+
+def print_bytes(prefix, value, bytes_per_line):
+ for i in range(0, len(value), bytes_per_line):
+ line = prefix + "".join(f"0x{b:02x}, " for b in value[i : i + bytes_per_line])
+ print(f"{line.rstrip()}")
+
+
+def print_c_struct_u8_array_field(name, value):
+ print(f"\t\t.{name} = {{")
+ print_bytes("\t\t\t", value, 8)
+ print("\t\t},")
+
+
+testvecs = load_testvecs(sys.argv[1])
+
+print(f"""/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* This file was generated by {SCRIPT_NAME} */
+
+static const struct xwing_testvec {{
+\tu8 seed[XWING_SEED_BYTES];
+\tu8 pk_hash[SHA3_256_DIGEST_SIZE];
+\tu8 sk_hash[SHA3_256_DIGEST_SIZE];
+\tu8 eseed[XWING_ESEED_BYTES];
+\tu8 ct_hash[SHA3_256_DIGEST_SIZE];
+\tu8 ss[XWING_SHARED_SECRET_BYTES];
+}} xwing_testvecs[] = {{""")
+for tv in testvecs:
+ print("\t{")
+ print_c_struct_u8_array_field("seed", tv.seed)
+ print_c_struct_u8_array_field("pk_hash", hashlib.sha3_256(tv.pk).digest())
+ print_c_struct_u8_array_field("sk_hash", hashlib.sha3_256(tv.sk).digest())
+ print_c_struct_u8_array_field("eseed", tv.eseed)
+ print_c_struct_u8_array_field("ct_hash", hashlib.sha3_256(tv.ct).digest())
+ print_c_struct_u8_array_field("ss", tv.ss)
+ print("\t},")
+print("};")
--
2.54.0