Skip to content

Commit e0a763f

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 1a08580 commit e0a763f

4 files changed

Lines changed: 355 additions & 9 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: 241 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,46 @@ static DEFINE_CLEANUP_FUNC(
665665
cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free)
666666
#define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx)
667667

668+
/*
669+
* hkdf_info_printf()
670+
*
671+
* Helper function to append variable length label and context to an HkdfLabel
672+
*
673+
* RFC 8446 (TLS 1.3) Section 7.1 defines the HKDF-Expand-Label function as a
674+
* specialization of the HKDF-Expand function (RFC 5869), where the info
675+
* parameter is structured as an HkdfLabel.
676+
*
677+
* An HkdfLabel structure includes two variable length vectors (label and
678+
* context) which must be preceded by their content length as per RFC 8446
679+
* Section 3.4 (and not NUL terminated as per Section 7.1). Additionally,
680+
* HkdfLabel.label must begin with "tls13 "
681+
*
682+
* Returns the number of bytes appended to the HKDF info buffer, or -1 on an
683+
* error.
684+
*/
685+
__attribute__((format(printf, 2, 3)))
686+
static int hkdf_info_printf(EVP_PKEY_CTX *ctx, char *fmt, ...)
687+
{
688+
_cleanup_free_ char *str;
689+
uint8_t len;
690+
int ret;
691+
va_list myargs;
692+
693+
va_start(myargs, fmt);
694+
ret = vasprintf(&str, fmt, myargs);
695+
va_end(myargs);
696+
if (ret < 0)
697+
return ret;
698+
if (ret > 255)
699+
return -1;
700+
len = ret;
701+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (unsigned char *)&len, 1) <= 0)
702+
return -1;
703+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (unsigned char *)str, len) <= 0)
704+
return -1;
705+
return (ret + 1);
706+
}
707+
668708
/*
669709
* derive_retained_key()
670710
*
@@ -697,6 +737,67 @@ static int derive_retained_key(int hmac, const char *hostnqn,
697737
unsigned char *configured,
698738
unsigned char *retained,
699739
size_t key_len)
740+
{
741+
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
742+
uint16_t length = htons(key_len & 0xFFFF);
743+
const EVP_MD *md;
744+
size_t hmac_len;
745+
746+
if (hmac == NVME_HMAC_ALG_NONE) {
747+
memcpy(retained, configured, key_len);
748+
return key_len;
749+
}
750+
751+
md = select_hmac(hmac, &hmac_len);
752+
if (!md || !hmac_len) {
753+
errno = EINVAL;
754+
return -1;
755+
}
756+
757+
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
758+
if (!ctx) {
759+
errno = ENOMEM;
760+
return -1;
761+
}
762+
763+
if (EVP_PKEY_derive_init(ctx) <= 0) {
764+
errno = ENOMEM;
765+
return -1;
766+
}
767+
if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) {
768+
errno = ENOKEY;
769+
return -1;
770+
}
771+
if (EVP_PKEY_CTX_set1_hkdf_key(ctx, configured, key_len) <= 0) {
772+
errno = ENOKEY;
773+
return -1;
774+
}
775+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
776+
(const unsigned char *)&length, 2) <= 0) {
777+
errno = ENOKEY;
778+
return -1;
779+
}
780+
if (hkdf_info_printf(ctx, "tls13 HostNQN") <= 0) {
781+
errno = ENOKEY;
782+
return -1;
783+
}
784+
if (hkdf_info_printf(ctx, "%s", hostnqn) <= 0) {
785+
errno = ENOKEY;
786+
return -1;
787+
}
788+
789+
if (EVP_PKEY_derive(ctx, retained, &key_len) <= 0) {
790+
errno = ENOKEY;
791+
return -1;
792+
}
793+
794+
return key_len;
795+
}
796+
797+
static int derive_retained_key_compat(int hmac, const char *hostnqn,
798+
unsigned char *configured,
799+
unsigned char *retained,
800+
size_t key_len)
700801
{
701802
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
702803
uint16_t length = key_len & 0xFFFF;
@@ -783,9 +884,78 @@ static int derive_retained_key(int hmac, const char *hostnqn,
783884
*
784885
* and the value '0' is invalid here.
785886
*/
887+
786888
static int derive_tls_key(int version, unsigned char cipher,
787889
const char *context, unsigned char *retained,
788890
unsigned char *psk, size_t key_len)
891+
{
892+
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
893+
uint16_t length = htons(key_len & 0xFFFF);
894+
const EVP_MD *md;
895+
size_t hmac_len;
896+
897+
md = select_hmac(cipher, &hmac_len);
898+
if (!md || !hmac_len) {
899+
errno = EINVAL;
900+
return -1;
901+
}
902+
903+
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
904+
if (!ctx) {
905+
errno = ENOMEM;
906+
return -1;
907+
}
908+
909+
if (EVP_PKEY_derive_init(ctx) <= 0) {
910+
errno = ENOMEM;
911+
return -1;
912+
}
913+
if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) {
914+
errno = ENOKEY;
915+
return -1;
916+
}
917+
if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) {
918+
errno = ENOKEY;
919+
return -1;
920+
}
921+
if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
922+
(const unsigned char *)&length, 2) <= 0) {
923+
errno = ENOKEY;
924+
return -1;
925+
}
926+
if (hkdf_info_printf(ctx, "tls13 nvme-tls-psk") <= 0) {
927+
errno = ENOKEY;
928+
return -1;
929+
}
930+
switch (version) {
931+
case 0:
932+
if (hkdf_info_printf(ctx, "%s", context) <= 0) {
933+
errno = ENOKEY;
934+
return -1;
935+
}
936+
break;
937+
case 1:
938+
if (hkdf_info_printf(ctx, "%02d %s", cipher, context) <= 0) {
939+
errno = ENOKEY;
940+
return -1;
941+
}
942+
break;
943+
default:
944+
errno = ENOKEY;
945+
return -1;
946+
}
947+
948+
if (EVP_PKEY_derive(ctx, psk, &key_len) <= 0) {
949+
errno = ENOKEY;
950+
return -1;
951+
}
952+
953+
return key_len;
954+
}
955+
956+
static int derive_tls_key_compat(int version, unsigned char cipher,
957+
const char *context, unsigned char *retained,
958+
unsigned char *psk, size_t key_len)
789959
{
790960
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
791961
uint16_t length = key_len & 0xFFFF;
@@ -1074,7 +1244,7 @@ static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
10741244
static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
10751245
char *identity, int version,
10761246
int hmac, unsigned char *configured,
1077-
unsigned char *psk, int key_len)
1247+
unsigned char *psk, int key_len, bool compat)
10781248
{
10791249
_cleanup_free_ unsigned char *retained = NULL;
10801250
_cleanup_free_ char *digest = NULL;
@@ -1092,7 +1262,12 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
10921262
errno = ENOMEM;
10931263
return -1;
10941264
}
1095-
ret = derive_retained_key(hmac, hostnqn, configured, retained, key_len);
1265+
if (compat)
1266+
ret = derive_retained_key_compat(hmac, hostnqn, configured,
1267+
retained, key_len);
1268+
else
1269+
ret = derive_retained_key(hmac, hostnqn, configured,
1270+
retained, key_len);
10961271
if (ret < 0)
10971272
return ret;
10981273

@@ -1120,6 +1295,9 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
11201295
digest, identity);
11211296
if (ret < 0)
11221297
return ret;
1298+
if (compat)
1299+
return derive_tls_key_compat(version, cipher, context, retained,
1300+
psk, key_len);
11231301
return derive_tls_key(version, cipher, context, retained,
11241302
psk, key_len);
11251303
}
@@ -1169,7 +1347,39 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn,
11691347

11701348
memset(psk, 0, key_len);
11711349
ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
1172-
configured_key, psk, key_len);
1350+
configured_key, psk, key_len, false);
1351+
out_free_identity:
1352+
if (ret < 0) {
1353+
free(identity);
1354+
identity = NULL;
1355+
}
1356+
return identity;
1357+
}
1358+
1359+
char *nvme_generate_tls_key_identity_compat(const char *hostnqn, const char *subsysnqn,
1360+
int version, int hmac,
1361+
unsigned char *configured_key, int key_len)
1362+
{
1363+
_cleanup_free_ unsigned char *psk = NULL;
1364+
char *identity;
1365+
ssize_t identity_len;
1366+
int ret = -1;
1367+
1368+
identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn);
1369+
if (identity_len < 0)
1370+
return NULL;
1371+
1372+
identity = malloc(identity_len);
1373+
if (!identity)
1374+
return NULL;
1375+
1376+
psk = malloc(key_len);
1377+
if (!psk)
1378+
goto out_free_identity;
1379+
1380+
memset(psk, 0, key_len);
1381+
ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
1382+
configured_key, psk, key_len, true);
11731383
out_free_identity:
11741384
if (ret < 0) {
11751385
free(identity);
@@ -1340,7 +1550,8 @@ int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb,
13401550
static long __nvme_insert_tls_key(key_serial_t keyring_id, const char *key_type,
13411551
const char *hostnqn, const char *subsysnqn,
13421552
int version, int hmac,
1343-
unsigned char *configured_key, int key_len)
1553+
unsigned char *configured_key, int key_len,
1554+
bool compat)
13441555
{
13451556
_cleanup_free_ unsigned char *psk = NULL;
13461557
_cleanup_free_ char *identity = NULL;
@@ -1366,7 +1577,7 @@ static long __nvme_insert_tls_key(key_serial_t keyring_id, const char *key_type,
13661577
}
13671578
memset(psk, 0, key_len);
13681579
ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
1369-
configured_key, psk, key_len);
1580+
configured_key, psk, key_len, compat);
13701581
if (ret != key_len) {
13711582
errno = ENOKEY;
13721583
return 0;
@@ -1397,7 +1608,30 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
13971608
return __nvme_insert_tls_key(keyring_id, key_type,
13981609
hostnqn, subsysnqn,
13991610
version, hmac,
1400-
configured_key, key_len);
1611+
configured_key, key_len, false);
1612+
}
1613+
1614+
long nvme_insert_tls_key_compat(const char *keyring, const char *key_type,
1615+
const char *hostnqn, const char *subsysnqn,
1616+
int version, int hmac,
1617+
unsigned char *configured_key, int key_len)
1618+
{
1619+
key_serial_t keyring_id;
1620+
int ret;
1621+
1622+
keyring_id = nvme_lookup_keyring(keyring);
1623+
if (keyring_id == 0) {
1624+
errno = ENOKEY;
1625+
return 0;
1626+
}
1627+
1628+
ret = nvme_set_keyring(keyring_id);
1629+
if (ret < 0)
1630+
return 0;
1631+
return __nvme_insert_tls_key(keyring_id, key_type,
1632+
hostnqn, subsysnqn,
1633+
version, hmac,
1634+
configured_key, key_len, true);
14011635
}
14021636

14031637
long nvme_revoke_tls_key(const char *keyring, const char *key_type,
@@ -1443,7 +1677,7 @@ static long __nvme_import_tls_key(long keyring_id,
14431677
return __nvme_insert_tls_key(keyring_id, "psk",
14441678
hostnqn, subsysnqn,
14451679
version, hmac,
1446-
key_data, key_len);
1680+
key_data, key_len, false);
14471681
}
14481682

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

0 commit comments

Comments
 (0)