Skip to content

Commit 27043ad

Browse files
cleechhreinecke
authored andcommitted
libnvme: TLS PSK derivation fixes
There are issues with the Retained and TLS PSK derivations due to the implementation not adhering to the RFC 8446 definition of the HKDF-Expand-Label function. 1) The 16-bit HkdfLabel.length value must be converted to network byte order. 2) The variable length HkdfLabel.label and HkdfLabel.context vectors must be prefixed with a length byte. To not break existing implementations keep the original algorithms with as compat versions and update the 'psk' testcase to cover both versions. Signed-off-by: Chris Leech <[email protected]> Signed-off-by: Hannes Reinecke <[email protected]>
1 parent 0f6ecdb commit 27043ad

4 files changed

Lines changed: 380 additions & 8 deletions

File tree

src/libnvme.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ LIBNVME_UNRELEASED {
33
global:
44
nvme_set_etdas;
55
nvme_clear_etdas;
6+
nvme_insert_tls_key_compat;
7+
nvme_generate_tls_key_identity_compat;
68
};
79

810
LIBNVME_1_14 {

src/nvme/linux.c

Lines changed: 268 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,17 @@ static int derive_retained_key(int hmac, const char *hostnqn,
600600
return -1;
601601
}
602602

603+
static int derive_retained_key_compat(int hmac, const char *hostnqn,
604+
unsigned char *generated,
605+
unsigned char *retained,
606+
size_t key_len)
607+
{
608+
nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; "
609+
"recompile with OpenSSL support.\n");
610+
errno = ENOTSUP;
611+
return -1;
612+
}
613+
603614
static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
604615
int version, int cipher,
605616
unsigned char *retained, size_t key_len,
@@ -620,6 +631,16 @@ static int derive_tls_key(int version, int cipher, const char *context,
620631
errno = ENOTSUP;
621632
return -1;
622633
}
634+
635+
static int derive_tls_key_compat(int version, int cipher, const char *context,
636+
unsigned char *retained,
637+
unsigned char *psk, size_t key_len)
638+
{
639+
nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; "
640+
"recompile with OpenSSL support.\n");
641+
errno = ENOTSUP;
642+
return -1;
643+
}
623644
#else /* CONFIG_OPENSSL */
624645
static unsigned char default_hmac(size_t key_len)
625646
{
@@ -665,6 +686,46 @@ static DEFINE_CLEANUP_FUNC(
665686
cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free)
666687
#define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx)
667688

689+
/*
690+
* hkdf_info_printf()
691+
*
692+
* Helper function to append variable length label and context to an HkdfLabel
693+
*
694+
* RFC 8446 (TLS 1.3) Section 7.1 defines the HKDF-Expand-Label function as a
695+
* specialization of the HKDF-Expand function (RFC 5869), where the info
696+
* parameter is structured as an HkdfLabel.
697+
*
698+
* An HkdfLabel structure includes two variable length vectors (label and
699+
* context) which must be preceded by their content length as per RFC 8446
700+
* Section 3.4 (and not NUL terminated as per Section 7.1). Additionally,
701+
* HkdfLabel.label must begin with "tls13 "
702+
*
703+
* Returns the number of bytes appended to the HKDF info buffer, or -1 on an
704+
* error.
705+
*/
706+
__attribute__((format(printf, 2, 3)))
707+
static int hkdf_info_printf(EVP_PKEY_CTX *ctx, char *fmt, ...)
708+
{
709+
_cleanup_free_ char *str;
710+
uint8_t len;
711+
int ret;
712+
va_list myargs;
713+
714+
va_start(myargs, fmt);
715+
ret = vasprintf(&str, fmt, myargs);
716+
va_end(myargs);
717+
if (ret < 0)
718+
return ret;
719+
if (ret > 255)
720+
return -1;
721+
len = ret;
722+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (unsigned char *)&len, 1) <= 0)
723+
return -1;
724+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (unsigned char *)str, len) <= 0)
725+
return -1;
726+
return (ret + 1);
727+
}
728+
668729
/*
669730
* derive_retained_key()
670731
*
@@ -697,6 +758,67 @@ static int derive_retained_key(int hmac, const char *hostnqn,
697758
unsigned char *configured,
698759
unsigned char *retained,
699760
size_t key_len)
761+
{
762+
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
763+
uint16_t length = htons(key_len & 0xFFFF);
764+
const EVP_MD *md;
765+
size_t hmac_len;
766+
767+
if (hmac == NVME_HMAC_ALG_NONE) {
768+
memcpy(retained, configured, key_len);
769+
return key_len;
770+
}
771+
772+
md = select_hmac(hmac, &hmac_len);
773+
if (!md || !hmac_len) {
774+
errno = EINVAL;
775+
return -1;
776+
}
777+
778+
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
779+
if (!ctx) {
780+
errno = ENOMEM;
781+
return -1;
782+
}
783+
784+
if (EVP_PKEY_derive_init(ctx) <= 0) {
785+
errno = ENOMEM;
786+
return -1;
787+
}
788+
if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) {
789+
errno = ENOKEY;
790+
return -1;
791+
}
792+
if (EVP_PKEY_CTX_set1_hkdf_key(ctx, configured, key_len) <= 0) {
793+
errno = ENOKEY;
794+
return -1;
795+
}
796+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
797+
(const unsigned char *)&length, 2) <= 0) {
798+
errno = ENOKEY;
799+
return -1;
800+
}
801+
if (hkdf_info_printf(ctx, "tls13 HostNQN") <= 0) {
802+
errno = ENOKEY;
803+
return -1;
804+
}
805+
if (hkdf_info_printf(ctx, "%s", hostnqn) <= 0) {
806+
errno = ENOKEY;
807+
return -1;
808+
}
809+
810+
if (EVP_PKEY_derive(ctx, retained, &key_len) <= 0) {
811+
errno = ENOKEY;
812+
return -1;
813+
}
814+
815+
return key_len;
816+
}
817+
818+
static int derive_retained_key_compat(int hmac, const char *hostnqn,
819+
unsigned char *configured,
820+
unsigned char *retained,
821+
size_t key_len)
700822
{
701823
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
702824
uint16_t length = key_len & 0xFFFF;
@@ -783,9 +905,78 @@ static int derive_retained_key(int hmac, const char *hostnqn,
783905
*
784906
* and the value '0' is invalid here.
785907
*/
908+
786909
static int derive_tls_key(int version, unsigned char cipher,
787910
const char *context, unsigned char *retained,
788911
unsigned char *psk, size_t key_len)
912+
{
913+
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
914+
uint16_t length = htons(key_len & 0xFFFF);
915+
const EVP_MD *md;
916+
size_t hmac_len;
917+
918+
md = select_hmac(cipher, &hmac_len);
919+
if (!md || !hmac_len) {
920+
errno = EINVAL;
921+
return -1;
922+
}
923+
924+
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
925+
if (!ctx) {
926+
errno = ENOMEM;
927+
return -1;
928+
}
929+
930+
if (EVP_PKEY_derive_init(ctx) <= 0) {
931+
errno = ENOMEM;
932+
return -1;
933+
}
934+
if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) {
935+
errno = ENOKEY;
936+
return -1;
937+
}
938+
if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) {
939+
errno = ENOKEY;
940+
return -1;
941+
}
942+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
943+
(const unsigned char *)&length, 2) <= 0) {
944+
errno = ENOKEY;
945+
return -1;
946+
}
947+
if (hkdf_info_printf(ctx, "tls13 nvme-tls-psk") <= 0) {
948+
errno = ENOKEY;
949+
return -1;
950+
}
951+
switch (version) {
952+
case 0:
953+
if (hkdf_info_printf(ctx, "%s", context) <= 0) {
954+
errno = ENOKEY;
955+
return -1;
956+
}
957+
break;
958+
case 1:
959+
if (hkdf_info_printf(ctx, "%02d %s", cipher, context) <= 0) {
960+
errno = ENOKEY;
961+
return -1;
962+
}
963+
break;
964+
default:
965+
errno = ENOKEY;
966+
return -1;
967+
}
968+
969+
if (EVP_PKEY_derive(ctx, psk, &key_len) <= 0) {
970+
errno = ENOKEY;
971+
return -1;
972+
}
973+
974+
return key_len;
975+
}
976+
977+
static int derive_tls_key_compat(int version, unsigned char cipher,
978+
const char *context, unsigned char *retained,
979+
unsigned char *psk, size_t key_len)
789980
{
790981
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
791982
uint16_t length = key_len & 0xFFFF;
@@ -1074,7 +1265,7 @@ static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
10741265
static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
10751266
char *identity, int version,
10761267
int hmac, unsigned char *configured,
1077-
unsigned char *psk, int key_len)
1268+
unsigned char *psk, int key_len, bool compat)
10781269
{
10791270
_cleanup_free_ unsigned char *retained = NULL;
10801271
_cleanup_free_ char *digest = NULL;
@@ -1092,7 +1283,12 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
10921283
errno = ENOMEM;
10931284
return -1;
10941285
}
1095-
ret = derive_retained_key(hmac, hostnqn, configured, retained, key_len);
1286+
if (compat)
1287+
ret = derive_retained_key_compat(hmac, hostnqn, configured,
1288+
retained, key_len);
1289+
else
1290+
ret = derive_retained_key(hmac, hostnqn, configured,
1291+
retained, key_len);
10961292
if (ret < 0)
10971293
return ret;
10981294

@@ -1120,6 +1316,9 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
11201316
digest, identity);
11211317
if (ret < 0)
11221318
return ret;
1319+
if (compat)
1320+
return derive_tls_key_compat(version, cipher, context, retained,
1321+
psk, key_len);
11231322
return derive_tls_key(version, cipher, context, retained,
11241323
psk, key_len);
11251324
}
@@ -1175,10 +1374,47 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn,
11751374

11761375
memset(psk, 0, key_len);
11771376
ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
1178-
configured_key, psk, key_len);
1377+
configured_key, psk, key_len, false);
1378+
out_free_identity:
1379+
if (ret < 0) {
1380+
free(identity);
1381+
identity = NULL;
1382+
}
1383+
return identity;
1384+
}
1385+
1386+
char *nvme_generate_tls_key_identity_compat(const char *hostnqn, const char *subsysnqn,
1387+
int version, int hmac,
1388+
unsigned char *configured_key, int key_len)
1389+
{
1390+
_cleanup_free_ unsigned char *psk = NULL;
1391+
char *identity;
1392+
ssize_t identity_len;
1393+
int ret = -1;
1394+
1395+
identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn);
1396+
if (identity_len < 0) {
1397+
errno = EINVAL;
1398+
return NULL;
1399+
}
1400+
1401+
identity = malloc(identity_len);
1402+
if (!identity) {
1403+
errno = ENOMEM;
1404+
return NULL;
1405+
}
1406+
1407+
psk = malloc(key_len);
1408+
if (!psk) {
1409+
errno = ENOMEM;
1410+
goto out_free_identity;
1411+
}
1412+
1413+
memset(psk, 0, key_len);
1414+
ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
1415+
configured_key, psk, key_len, true);
11791416
out_free_identity:
11801417
if (ret < 0) {
1181-
errno = -ret;
11821418
free(identity);
11831419
identity = NULL;
11841420
}
@@ -1347,7 +1583,8 @@ int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb,
13471583
static long __nvme_insert_tls_key(key_serial_t keyring_id, const char *key_type,
13481584
const char *hostnqn, const char *subsysnqn,
13491585
int version, int hmac,
1350-
unsigned char *configured_key, int key_len)
1586+
unsigned char *configured_key, int key_len,
1587+
bool compat)
13511588
{
13521589
_cleanup_free_ unsigned char *psk = NULL;
13531590
_cleanup_free_ char *identity = NULL;
@@ -1373,7 +1610,7 @@ static long __nvme_insert_tls_key(key_serial_t keyring_id, const char *key_type,
13731610
}
13741611
memset(psk, 0, key_len);
13751612
ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
1376-
configured_key, psk, key_len);
1613+
configured_key, psk, key_len, compat);
13771614
if (ret != key_len) {
13781615
errno = ENOKEY;
13791616
return 0;
@@ -1404,7 +1641,30 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
14041641
return __nvme_insert_tls_key(keyring_id, key_type,
14051642
hostnqn, subsysnqn,
14061643
version, hmac,
1407-
configured_key, key_len);
1644+
configured_key, key_len, false);
1645+
}
1646+
1647+
long nvme_insert_tls_key_compat(const char *keyring, const char *key_type,
1648+
const char *hostnqn, const char *subsysnqn,
1649+
int version, int hmac,
1650+
unsigned char *configured_key, int key_len)
1651+
{
1652+
key_serial_t keyring_id;
1653+
int ret;
1654+
1655+
keyring_id = nvme_lookup_keyring(keyring);
1656+
if (keyring_id == 0) {
1657+
errno = ENOKEY;
1658+
return 0;
1659+
}
1660+
1661+
ret = nvme_set_keyring(keyring_id);
1662+
if (ret < 0)
1663+
return 0;
1664+
return __nvme_insert_tls_key(keyring_id, key_type,
1665+
hostnqn, subsysnqn,
1666+
version, hmac,
1667+
configured_key, key_len, true);
14081668
}
14091669

14101670
long nvme_revoke_tls_key(const char *keyring, const char *key_type,
@@ -1450,7 +1710,7 @@ static long __nvme_import_tls_key(long keyring_id,
14501710
return __nvme_insert_tls_key(keyring_id, "psk",
14511711
hostnqn, subsysnqn,
14521712
version, hmac,
1453-
key_data, key_len);
1713+
key_data, key_len, false);
14541714
}
14551715

14561716
return nvme_update_key(keyring_id, "psk", identity,

0 commit comments

Comments
 (0)