Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

nvme-auth: common: add KUnit tests for TLS key derivation

Unit-test the sequence of function calls that derive tls_psk, so that we
can be more confident that changes in the implementation don't break it.

Since the NVMe specification doesn't seem to include any test vectors
for this (nor does its description of the algorithm seem to match what
was actually implemented, for that matter), I just set the expected
values to the values that the code currently produces. In the case
of SHA-512, nvme_auth_generate_digest() currently returns -EINVAL, so
for now the test tests for that too. If it is later determined that
some other behavior is needed, the test can be updated accordingly.

Tested with:

tools/testing/kunit/kunit.py run --kunitconfig drivers/nvme/common/

Acked-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Keith Busch <kbusch@kernel.org>

authored by

Eric Biggers and committed by
Keith Busch
f990ad67 bf0e2567

+191
+6
drivers/nvme/common/.kunitconfig
··· 1 + CONFIG_KUNIT=y 2 + CONFIG_PCI=y 3 + CONFIG_BLOCK=y 4 + CONFIG_BLK_DEV_NVME=y 5 + CONFIG_NVME_HOST_AUTH=y 6 + CONFIG_NVME_AUTH_KUNIT_TEST=y
+8
drivers/nvme/common/Kconfig
··· 13 13 select CRYPTO_DH 14 14 select CRYPTO_DH_RFC7919_GROUPS 15 15 select CRYPTO_HKDF 16 + 17 + config NVME_AUTH_KUNIT_TEST 18 + tristate "KUnit tests for NVMe authentication" if !KUNIT_ALL_TESTS 19 + depends on KUNIT && NVME_AUTH 20 + default KUNIT_ALL_TESTS 21 + help 22 + Enable KUnit tests for some of the common code for NVMe over Fabrics 23 + In-Band Authentication.
+2
drivers/nvme/common/Makefile
··· 7 7 8 8 nvme-auth-y += auth.o 9 9 nvme-keyring-y += keyring.o 10 + 11 + obj-$(CONFIG_NVME_AUTH_KUNIT_TEST) += tests/auth_kunit.o
+175
drivers/nvme/common/tests/auth_kunit.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Unit tests for NVMe authentication functions 4 + * 5 + * Copyright 2026 Google LLC 6 + */ 7 + 8 + #include <crypto/sha2.h> 9 + #include <kunit/test.h> 10 + #include <linux/nvme.h> 11 + #include <linux/nvme-auth.h> 12 + #include <linux/slab.h> 13 + 14 + struct nvme_auth_test_values { 15 + u8 hmac_id; 16 + size_t hash_len; 17 + u8 expected_psk[NVME_AUTH_MAX_DIGEST_SIZE]; 18 + char *expected_psk_digest; 19 + u8 expected_tls_psk[NVME_AUTH_MAX_DIGEST_SIZE]; 20 + }; 21 + 22 + static void kfree_action(void *ptr) 23 + { 24 + kfree(ptr); 25 + } 26 + 27 + static void kunit_add_kfree_action(struct kunit *test, void *ptr) 28 + { 29 + KUNIT_ASSERT_EQ(test, 0, 30 + kunit_add_action_or_reset(test, kfree_action, ptr)); 31 + } 32 + 33 + /* 34 + * Test the derivation of a TLS PSK from the initial skey. The vals parameter 35 + * gives the expected value of tls_psk as well as the intermediate values psk 36 + * and psk_digest. The inputs are implicitly the fixed values set below. 37 + */ 38 + static void 39 + test_nvme_auth_derive_tls_psk(struct kunit *test, 40 + const struct nvme_auth_test_values *vals) 41 + { 42 + const u8 hmac_id = vals->hmac_id; 43 + const size_t hash_len = vals->hash_len; 44 + const size_t skey_len = hash_len; 45 + u8 skey[NVME_AUTH_MAX_DIGEST_SIZE]; 46 + u8 c1[NVME_AUTH_MAX_DIGEST_SIZE]; 47 + u8 c2[NVME_AUTH_MAX_DIGEST_SIZE]; 48 + const char *subsysnqn = "subsysnqn"; 49 + const char *hostnqn = "hostnqn"; 50 + u8 *psk = NULL, *tls_psk = NULL; 51 + char *psk_digest = NULL; 52 + size_t psk_len; 53 + int ret; 54 + 55 + for (int i = 0; i < NVME_AUTH_MAX_DIGEST_SIZE; i++) { 56 + skey[i] = 'A' + i; 57 + c1[i] = i; 58 + c2[i] = 0xff - i; 59 + } 60 + 61 + ret = nvme_auth_generate_psk(hmac_id, skey, skey_len, c1, c2, hash_len, 62 + &psk, &psk_len); 63 + kunit_add_kfree_action(test, psk); 64 + KUNIT_ASSERT_EQ(test, 0, ret); 65 + KUNIT_ASSERT_EQ(test, hash_len, psk_len); 66 + KUNIT_ASSERT_MEMEQ(test, vals->expected_psk, psk, psk_len); 67 + 68 + ret = nvme_auth_generate_digest(hmac_id, psk, psk_len, subsysnqn, 69 + hostnqn, &psk_digest); 70 + kunit_add_kfree_action(test, psk_digest); 71 + if (vals->expected_psk_digest == NULL) { 72 + /* 73 + * Algorithm has an ID assigned but is not supported by 74 + * nvme_auth_generate_digest(). 75 + */ 76 + KUNIT_ASSERT_EQ(test, -EINVAL, ret); 77 + return; 78 + } 79 + KUNIT_ASSERT_EQ(test, 0, ret); 80 + KUNIT_ASSERT_STREQ(test, vals->expected_psk_digest, psk_digest); 81 + 82 + ret = nvme_auth_derive_tls_psk(hmac_id, psk, psk_len, psk_digest, 83 + &tls_psk); 84 + kunit_add_kfree_action(test, tls_psk); 85 + KUNIT_ASSERT_EQ(test, 0, ret); 86 + KUNIT_ASSERT_MEMEQ(test, vals->expected_tls_psk, tls_psk, psk_len); 87 + } 88 + 89 + static void test_nvme_auth_derive_tls_psk_hmac_sha256(struct kunit *test) 90 + { 91 + static const struct nvme_auth_test_values vals = { 92 + .hmac_id = NVME_AUTH_HASH_SHA256, 93 + .hash_len = SHA256_DIGEST_SIZE, 94 + .expected_psk = { 95 + 0x17, 0x33, 0xc5, 0x9f, 0xa7, 0xf4, 0x8f, 0xcf, 96 + 0x37, 0xf5, 0xf2, 0x6f, 0xc4, 0xff, 0x02, 0x68, 97 + 0xad, 0x4f, 0x78, 0xe0, 0x30, 0xf4, 0xf3, 0xb0, 98 + 0xbf, 0xd1, 0xd4, 0x7e, 0x7b, 0xb1, 0x44, 0x7a, 99 + }, 100 + .expected_psk_digest = "OldoKuTfKddMuyCznAZojkWD7P4D9/AtzDzLimtOxqI=", 101 + .expected_tls_psk = { 102 + 0x3c, 0x17, 0xda, 0x62, 0x84, 0x74, 0xa0, 0x4d, 103 + 0x22, 0x47, 0xc4, 0xca, 0xb4, 0x79, 0x68, 0xc9, 104 + 0x15, 0x38, 0x81, 0x93, 0xf7, 0xc0, 0x71, 0xbd, 105 + 0x94, 0x89, 0xcc, 0x36, 0x66, 0xcd, 0x7c, 0xc8, 106 + }, 107 + }; 108 + 109 + test_nvme_auth_derive_tls_psk(test, &vals); 110 + } 111 + 112 + static void test_nvme_auth_derive_tls_psk_hmac_sha384(struct kunit *test) 113 + { 114 + static const struct nvme_auth_test_values vals = { 115 + .hmac_id = NVME_AUTH_HASH_SHA384, 116 + .hash_len = SHA384_DIGEST_SIZE, 117 + .expected_psk = { 118 + 0xf1, 0x4b, 0x2d, 0xd3, 0x23, 0x4c, 0x45, 0x96, 119 + 0x94, 0xd3, 0xbc, 0x63, 0xf8, 0x96, 0x8b, 0xd6, 120 + 0xb3, 0x7c, 0x2c, 0x6d, 0xe8, 0x49, 0xe2, 0x2e, 121 + 0x11, 0x87, 0x49, 0x00, 0x1c, 0xe4, 0xbb, 0xe8, 122 + 0x64, 0x0b, 0x9e, 0x3a, 0x74, 0x8c, 0xb1, 0x1c, 123 + 0xe4, 0xb1, 0xd7, 0x1d, 0x35, 0x9c, 0xce, 0x39, 124 + }, 125 + .expected_psk_digest = "cffMWk8TSS7HOQebjgYEIkrPrjWPV4JE5cdPB8WhEvY4JBW5YynKyv66XscN4A9n", 126 + .expected_tls_psk = { 127 + 0x27, 0x74, 0x75, 0x32, 0x33, 0x53, 0x7b, 0x3f, 128 + 0xa5, 0x0e, 0xb7, 0xd1, 0x6a, 0x8e, 0x43, 0x45, 129 + 0x7d, 0x85, 0xf4, 0x90, 0x6c, 0x00, 0x5b, 0x22, 130 + 0x36, 0x61, 0x6c, 0x5d, 0x80, 0x93, 0x9d, 0x08, 131 + 0x98, 0xff, 0xf1, 0x5b, 0xb8, 0xb7, 0x71, 0x19, 132 + 0xd2, 0xbe, 0x0a, 0xac, 0x42, 0x3e, 0x75, 0x90, 133 + }, 134 + }; 135 + 136 + test_nvme_auth_derive_tls_psk(test, &vals); 137 + } 138 + 139 + static void test_nvme_auth_derive_tls_psk_hmac_sha512(struct kunit *test) 140 + { 141 + static const struct nvme_auth_test_values vals = { 142 + .hmac_id = NVME_AUTH_HASH_SHA512, 143 + .hash_len = SHA512_DIGEST_SIZE, 144 + .expected_psk = { 145 + 0x9c, 0x9f, 0x08, 0x9a, 0x61, 0x8b, 0x47, 0xd2, 146 + 0xd7, 0x5f, 0x4b, 0x6c, 0x28, 0x07, 0x04, 0x24, 147 + 0x48, 0x7b, 0x44, 0x5d, 0xd9, 0x6e, 0x70, 0xc4, 148 + 0xc0, 0x9b, 0x55, 0xe8, 0xb6, 0x00, 0x01, 0x52, 149 + 0xa3, 0x36, 0x3c, 0x34, 0x54, 0x04, 0x3f, 0x38, 150 + 0xf0, 0xb8, 0x50, 0x36, 0xde, 0xd4, 0x06, 0x55, 151 + 0x35, 0x0a, 0xa8, 0x7b, 0x8b, 0x6a, 0x28, 0x2b, 152 + 0x5c, 0x1a, 0xca, 0xe1, 0x62, 0x33, 0xdd, 0x5b, 153 + }, 154 + /* nvme_auth_generate_digest() doesn't support SHA-512 yet. */ 155 + .expected_psk_digest = NULL, 156 + }; 157 + 158 + test_nvme_auth_derive_tls_psk(test, &vals); 159 + } 160 + 161 + static struct kunit_case nvme_auth_test_cases[] = { 162 + KUNIT_CASE(test_nvme_auth_derive_tls_psk_hmac_sha256), 163 + KUNIT_CASE(test_nvme_auth_derive_tls_psk_hmac_sha384), 164 + KUNIT_CASE(test_nvme_auth_derive_tls_psk_hmac_sha512), 165 + {}, 166 + }; 167 + 168 + static struct kunit_suite nvme_auth_test_suite = { 169 + .name = "nvme-auth", 170 + .test_cases = nvme_auth_test_cases, 171 + }; 172 + kunit_test_suite(nvme_auth_test_suite); 173 + 174 + MODULE_DESCRIPTION("Unit tests for NVMe authentication functions"); 175 + MODULE_LICENSE("GPL");