mirror of
https://github.com/libssh2/libssh2.git
synced 2026-10-24 03:48:58 +03:00
Hybrid ML-KEM KEX algorithms support with OpenSSL 3.5 (#1644)
Notes: Adds mlkem768x25519-sha256, mlkem768nistp256-sha256 and mlkem1024nistp384-sha384 according to [draft-ietf-sshm-mlkem-hybrid-kex](https://datatracker.ietf.org/doc/draft-ietf-sshm-mlkem-hybrid-kex/) if libssh2 built against OpenSSL 3.5 or newer. Reduces code duplication in `kex.c` by moving code blocks that are common for all KEXes into `process_host_key` and `finish_kex`. --------- Credit: Yaroslav Rosomakho Co-authored-by: Viktor Szakats <vszakats@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
3186a1a7ce
commit
3ba252e2a6
27
src/crypto.h
27
src/crypto.h
@@ -79,6 +79,18 @@ void _libssh2_hmac_cleanup(libssh2_hmac_ctx *ctx);
|
||||
#define LIBSSH2_ED25519_KEY_LEN 32
|
||||
#define LIBSSH2_ED25519_PRIVATE_KEY_LEN 64
|
||||
#define LIBSSH2_ED25519_SIG_LEN 64
|
||||
#define LIBSSH2_EC_P256_PUBLIC_KEY_LEN 65
|
||||
#define LIBSSH2_EC_P384_PUBLIC_KEY_LEN 97
|
||||
#define LIBSSH2_MLKEM_SHARED_SECRET_LEN 32
|
||||
#define LIBSSH2_MLKEM_512_PRIVATE_KEY_LEN 1632
|
||||
#define LIBSSH2_MLKEM_512_PUBLIC_KEY_LEN 800
|
||||
#define LIBSSH2_MLKEM_512_CIPHERTEXT 768
|
||||
#define LIBSSH2_MLKEM_768_PRIVATE_KEY_LEN 2400
|
||||
#define LIBSSH2_MLKEM_768_PUBLIC_KEY_LEN 1184
|
||||
#define LIBSSH2_MLKEM_768_CIPHERTEXT 1088
|
||||
#define LIBSSH2_MLKEM_1024_PRIVATE_KEY_LEN 3168
|
||||
#define LIBSSH2_MLKEM_1024_PUBLIC_KEY_LEN 1568
|
||||
#define LIBSSH2_MLKEM_1024_CIPHERTEXT 1568
|
||||
|
||||
#if LIBSSH2_RSA
|
||||
int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
|
||||
@@ -293,6 +305,21 @@ _libssh2_ed25519_new_private_frommemory_sk(libssh2_ed25519_ctx **ed_ctx,
|
||||
|
||||
#endif /* LIBSSH2_ED25519 */
|
||||
|
||||
#if LIBSSH2_MLKEM
|
||||
int
|
||||
_libssh2_mlkem_new(LIBSSH2_SESSION *session,
|
||||
int ml_kem_size,
|
||||
unsigned char **out_public_key,
|
||||
unsigned char **out_private_key);
|
||||
|
||||
int
|
||||
_libssh2_mlkem_get_sk(unsigned char *out_shared_key,
|
||||
int ml_kem_size,
|
||||
uint8_t *private_key,
|
||||
uint8_t *server_ciphertext);
|
||||
|
||||
#endif /* LIBSSH2_MLKEM */
|
||||
|
||||
int _libssh2_cipher_init(_libssh2_cipher_ctx * h,
|
||||
_libssh2_cipher_type(algo),
|
||||
unsigned char *iv,
|
||||
|
||||
@@ -45,6 +45,11 @@
|
||||
#define LIBSSH2_ED25519 0
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_NO_MLKEM
|
||||
#undef LIBSSH2_MLKEM
|
||||
#define LIBSSH2_MLKEM 0
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_NO_AES_CTR
|
||||
#undef LIBSSH2_AES_CTR
|
||||
#define LIBSSH2_AES_CTR 0
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
#define LIBSSH2_DSA 1
|
||||
#define LIBSSH2_ECDSA 0
|
||||
#define LIBSSH2_ED25519 0
|
||||
#define LIBSSH2_MLKEM 0
|
||||
|
||||
#include "crypto_config.h"
|
||||
|
||||
|
||||
@@ -361,7 +361,9 @@ typedef struct key_exchange_state_low_t
|
||||
kmdhgGPshakex_state_t exchange_state;
|
||||
_libssh2_bn *p; /* SSH2 defined value (p_value) */
|
||||
_libssh2_bn *g; /* SSH2 defined value (2) */
|
||||
unsigned char request[256]; /* Must fit EC_MAX_POINT_LEN + data */
|
||||
/* Request must fit mlkem1024nistp384 keys + 5 bytes overhead */
|
||||
unsigned char request[LIBSSH2_MLKEM_1024_PUBLIC_KEY_LEN +
|
||||
LIBSSH2_EC_P384_PUBLIC_KEY_LEN + 5];
|
||||
unsigned char *data;
|
||||
size_t request_len;
|
||||
size_t data_len;
|
||||
@@ -373,6 +375,8 @@ typedef struct key_exchange_state_low_t
|
||||
bytes */
|
||||
unsigned char *curve25519_private_key; /* curve25519 private key, 32
|
||||
bytes */
|
||||
unsigned char *mlkem_public_key; /* ML-KEM public key */
|
||||
unsigned char *mlkem_private_key; /* ML-KEM private key */
|
||||
} key_exchange_state_low_t;
|
||||
|
||||
typedef struct key_exchange_state_t
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
# define LIBSSH2_ECDSA 0
|
||||
#endif
|
||||
#define LIBSSH2_ED25519 0
|
||||
#define LIBSSH2_MLKEM 0
|
||||
|
||||
#include "crypto_config.h"
|
||||
|
||||
|
||||
26
src/misc.c
26
src/misc.c
@@ -296,6 +296,32 @@ int _libssh2_store_str(unsigned char **buf, const char *str, size_t len)
|
||||
return len_stored == len;
|
||||
}
|
||||
|
||||
/* _libssh2_store_str */
|
||||
int _libssh2_store_hybrid_str(unsigned char **buf, const char *str_1,
|
||||
size_t len_1, const char *str_2, size_t len_2)
|
||||
{
|
||||
uint32_t len_stored;
|
||||
|
||||
if(len_1 > UINT32_MAX - len_2)
|
||||
return 0;
|
||||
|
||||
len_stored = (uint32_t)len_1 + (uint32_t)len_2;
|
||||
|
||||
_libssh2_store_u32(buf, len_stored);
|
||||
if(len_1) {
|
||||
memcpy(*buf, str_1, len_1);
|
||||
*buf += len_1;
|
||||
}
|
||||
|
||||
if(len_2) {
|
||||
memcpy(*buf, str_2, len_2);
|
||||
*buf += len_2;
|
||||
}
|
||||
|
||||
assert(len_stored == len_1 + len_2);
|
||||
return len_stored == len_1 + len_2;
|
||||
}
|
||||
|
||||
/* _libssh2_store_bignum2_bytes */
|
||||
int _libssh2_store_bignum2_bytes(unsigned char **buf,
|
||||
const unsigned char *bytes,
|
||||
|
||||
@@ -114,6 +114,8 @@ void _libssh2_htonu32(unsigned char *buf, uint32_t val);
|
||||
void _libssh2_store_u32(unsigned char **buf, uint32_t value);
|
||||
void _libssh2_store_u64(unsigned char **buf, libssh2_uint64_t value);
|
||||
int _libssh2_store_str(unsigned char **buf, const char *str, size_t len);
|
||||
int _libssh2_store_hybrid_str(unsigned char **buf, const char *str_1,
|
||||
size_t len_1, const char *str_2, size_t len_2);
|
||||
int _libssh2_store_bignum2_bytes(unsigned char **buf,
|
||||
const unsigned char *bytes,
|
||||
size_t len);
|
||||
|
||||
168
src/openssl.c
168
src/openssl.c
@@ -2745,6 +2745,174 @@ _libssh2_ed25519_new_public(libssh2_ed25519_ctx ** ed_ctx,
|
||||
}
|
||||
#endif /* LIBSSH2_ED25519 */
|
||||
|
||||
#if LIBSSH2_MLKEM
|
||||
|
||||
int
|
||||
_libssh2_mlkem_new(LIBSSH2_SESSION *session,
|
||||
int ml_kem_size,
|
||||
unsigned char **out_public_key,
|
||||
unsigned char **out_private_key)
|
||||
{
|
||||
EVP_PKEY *key = NULL;
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
unsigned char *priv = NULL, *pub = NULL;
|
||||
const char *evp_name;
|
||||
size_t privLen, actualPrivLen, pubLen, actualPubLen;
|
||||
int rc = -1;
|
||||
|
||||
switch(ml_kem_size) {
|
||||
case 512:
|
||||
evp_name = "ML-KEM-512";
|
||||
privLen = LIBSSH2_MLKEM_512_PRIVATE_KEY_LEN;
|
||||
pubLen = LIBSSH2_MLKEM_512_PUBLIC_KEY_LEN;
|
||||
break;
|
||||
case 768:
|
||||
evp_name = "ML-KEM-768";
|
||||
privLen = LIBSSH2_MLKEM_768_PRIVATE_KEY_LEN;
|
||||
pubLen = LIBSSH2_MLKEM_768_PUBLIC_KEY_LEN;
|
||||
break;
|
||||
case 1024:
|
||||
evp_name = "ML-KEM-1024";
|
||||
privLen = LIBSSH2_MLKEM_1024_PRIVATE_KEY_LEN;
|
||||
pubLen = LIBSSH2_MLKEM_1024_PUBLIC_KEY_LEN;
|
||||
break;
|
||||
default:
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
pctx = EVP_PKEY_CTX_new_from_name(NULL, evp_name, NULL);
|
||||
if(!pctx)
|
||||
return -1;
|
||||
|
||||
if(EVP_PKEY_keygen_init(pctx) != 1 ||
|
||||
EVP_PKEY_keygen(pctx, &key) != 1) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if(out_private_key) {
|
||||
priv = LIBSSH2_ALLOC(session, privLen);
|
||||
actualPrivLen = privLen;
|
||||
if(!priv)
|
||||
goto clean_exit;
|
||||
if(EVP_PKEY_get_raw_private_key(key, priv, &actualPrivLen) != 1 ||
|
||||
privLen != actualPrivLen) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
*out_private_key = priv;
|
||||
priv = NULL;
|
||||
}
|
||||
|
||||
if(out_public_key) {
|
||||
pub = LIBSSH2_ALLOC(session, pubLen);
|
||||
if(!pub)
|
||||
goto clean_exit;
|
||||
|
||||
if(EVP_PKEY_get_raw_public_key(key, pub, &actualPubLen) != 1 ||
|
||||
pubLen != actualPubLen) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
*out_public_key = pub;
|
||||
pub = NULL;
|
||||
}
|
||||
|
||||
/* success */
|
||||
rc = 0;
|
||||
|
||||
clean_exit:
|
||||
|
||||
if(pctx)
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
if(key)
|
||||
EVP_PKEY_free(key);
|
||||
if(priv)
|
||||
LIBSSH2_FREE(session, priv);
|
||||
if(pub)
|
||||
LIBSSH2_FREE(session, pub);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
_libssh2_mlkem_get_sk(unsigned char *out_shared_key,
|
||||
int ml_kem_size,
|
||||
uint8_t *private_key,
|
||||
uint8_t *server_ciphertext)
|
||||
{
|
||||
int rc = -1;
|
||||
EVP_PKEY *client_key = NULL;
|
||||
EVP_PKEY_CTX *client_key_ctx = NULL;
|
||||
size_t out_len = 0;
|
||||
const char *evp_name;
|
||||
size_t privLen, ciphertextLen;
|
||||
|
||||
switch(ml_kem_size) {
|
||||
case 512:
|
||||
evp_name = "ML-KEM-512";
|
||||
privLen = LIBSSH2_MLKEM_512_PRIVATE_KEY_LEN;
|
||||
ciphertextLen = LIBSSH2_MLKEM_512_CIPHERTEXT;
|
||||
break;
|
||||
case 768:
|
||||
evp_name = "ML-KEM-768";
|
||||
privLen = LIBSSH2_MLKEM_768_PRIVATE_KEY_LEN;
|
||||
ciphertextLen = LIBSSH2_MLKEM_768_CIPHERTEXT;
|
||||
break;
|
||||
case 1024:
|
||||
evp_name = "ML-KEM-1024";
|
||||
privLen = LIBSSH2_MLKEM_1024_PRIVATE_KEY_LEN;
|
||||
ciphertextLen = LIBSSH2_MLKEM_1024_CIPHERTEXT;
|
||||
break;
|
||||
default:
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if(!out_shared_key)
|
||||
return -1;
|
||||
|
||||
client_key = EVP_PKEY_new_raw_private_key_ex(NULL, evp_name,
|
||||
NULL, private_key, privLen);
|
||||
|
||||
if(!client_key) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
client_key_ctx = EVP_PKEY_CTX_new(client_key, NULL);
|
||||
if(!client_key_ctx) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_decapsulate_init(client_key_ctx, NULL);
|
||||
if(rc <= 0) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_decapsulate(client_key_ctx, NULL, &out_len,
|
||||
server_ciphertext, ciphertextLen);
|
||||
if(rc <= 0) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if(out_len != LIBSSH2_MLKEM_SHARED_SECRET_LEN) {
|
||||
rc = -1;
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_decapsulate(client_key_ctx, out_shared_key, &out_len,
|
||||
server_ciphertext, ciphertextLen);
|
||||
|
||||
clean_exit:
|
||||
|
||||
if(client_key_ctx)
|
||||
EVP_PKEY_CTX_free(client_key_ctx);
|
||||
if(client_key)
|
||||
EVP_PKEY_free(client_key);
|
||||
|
||||
return (rc == 1) ? 0 : -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if LIBSSH2_RSA
|
||||
int
|
||||
_libssh2_rsa_sha2_sign(LIBSSH2_SESSION * session,
|
||||
|
||||
@@ -155,6 +155,13 @@
|
||||
# define LIBSSH2_ED25519 0
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30500000L && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER)
|
||||
# define LIBSSH2_MLKEM 1
|
||||
#else
|
||||
# define LIBSSH2_MLKEM 0
|
||||
#endif
|
||||
|
||||
#ifdef OPENSSL_NO_MD5
|
||||
# define LIBSSH2_MD5 0
|
||||
#else
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
#define LIBSSH2_DSA 0
|
||||
#define LIBSSH2_ECDSA 0
|
||||
#define LIBSSH2_ED25519 0
|
||||
#define LIBSSH2_MLKEM 0
|
||||
|
||||
#include "crypto_config.h"
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
#define LIBSSH2_RSA_SHA2 1
|
||||
#define LIBSSH2_DSA 1
|
||||
#define LIBSSH2_ED25519 0
|
||||
#define LIBSSH2_MLKEM 0
|
||||
|
||||
/*
|
||||
* Conditionally enable ECDSA support.
|
||||
|
||||
Reference in New Issue
Block a user