@@ -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+
786888static 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,
10741244static 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);
11731383out_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,
13401550static 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
14031637long 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