Native ML-KEM768 implementation

for cryptographic backends that do not have support for ML-KEM (old
OpenSSL and Gcrypt; MbedTLS).

Based on the libcrux implementation used in OpenSSH, taken from this
revision:

https://github.com/openssh/openssh-portable/blob/6aba700/libcrux_mlkem768_sha3.h

But refactored to separate C and header file to support testing and
removed unused functions (to make compiler happy).

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
This commit is contained in:
Jakub Jelen
2025-12-19 12:00:41 +01:00
parent 9780fa2f01
commit 34db488e4d
27 changed files with 9569 additions and 91 deletions

1
.clang-format-ignore Normal file
View File

@@ -0,0 +1 @@
src/external/*

View File

@@ -104,9 +104,10 @@ if (OPENSSL_FOUND)
check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES)
check_function_exists(EVP_chacha20 HAVE_OPENSSL_EVP_CHACHA20)
# Check for ML-KEM768 availability (OpenSSL 3.5+)
# Check for ML-KEM availability (OpenSSL 3.5+)
if (OPENSSL_VERSION VERSION_GREATER_EQUAL "3.5.0")
set(HAVE_MLKEM 1)
set(HAVE_OPENSSL_MLKEM 1)
set(HAVE_MLKEM1024 1)
endif ()
unset(CMAKE_REQUIRED_INCLUDES)
@@ -235,7 +236,8 @@ if (GCRYPT_FOUND)
set(HAVE_GCRYPT_CURVE25519 1)
endif (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
if (GCRYPT_VERSION VERSION_GREATER_EQUAL "1.10.1")
set(HAVE_MLKEM 1)
set(HAVE_GCRYPT_MLKEM 1)
set(HAVE_MLKEM1024 1)
endif ()
endif (GCRYPT_FOUND)

View File

@@ -194,8 +194,14 @@
/* Define to 1 if we have support for blowfish */
#cmakedefine HAVE_BLOWFISH 1
/* Define to 1 if we have support for ML-KEM */
#cmakedefine HAVE_MLKEM 1
/* Define to 1 if we have support for ML-KEM in libgcrypt */
#cmakedefine HAVE_GCRYPT_MLKEM 1
/* Define to 1 if we have support for ML-KEM in OpenSSL */
#cmakedefine HAVE_OPENSSL_MLKEM 1
/* Define to 1 if we have support for ML-KEM1024 in either backend */
#cmakedefine HAVE_MLKEM1024 1
/*************************** LIBRARIES ***************************/

View File

@@ -87,14 +87,14 @@ enum ssh_key_exchange_e {
SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM,
/* sntrup761x25519-sha512 */
SSH_KEX_SNTRUP761X25519_SHA512,
#ifdef HAVE_MLKEM
/* mlkem768x25519-sha256 */
SSH_KEX_MLKEM768X25519_SHA256,
/* mlkem768nistp256-sha256 */
SSH_KEX_MLKEM768NISTP256_SHA256,
#ifdef HAVE_MLKEM1024
/* mlkem1024nistp384-sha384 */
SSH_KEX_MLKEM1024NISTP384_SHA384,
#endif /* HAVE_MLKEM */
#endif /* HAVE_MLKEM1024 */
/* gss-group14-sha256-* */
SSH_GSS_KEX_DH_GROUP14_SHA256,
/* gss-group16-sha512-* */
@@ -159,16 +159,14 @@ struct ssh_crypto_struct {
ssh_curve25519_pubkey curve25519_client_pubkey;
ssh_curve25519_pubkey curve25519_server_pubkey;
#endif
#ifdef HAVE_MLKEM
#ifdef HAVE_LIBGCRYPT
#ifdef HAVE_OPENSSL_MLKEM
EVP_PKEY *mlkem_privkey;
#else
unsigned char *mlkem_privkey;
size_t mlkem_privkey_len;
#else
EVP_PKEY *mlkem_privkey;
#endif
ssh_string mlkem_client_pubkey;
ssh_string mlkem_ciphertext;
#endif
#ifdef HAVE_SNTRUP761
ssh_sntrup761_privkey sntrup761_privkey;
ssh_sntrup761_pubkey sntrup761_client_pubkey;

View File

@@ -36,16 +36,20 @@ extern "C" {
struct mlkem_type_info {
size_t pubkey_size;
size_t ciphertext_size;
#ifdef HAVE_LIBGCRYPT
#ifdef HAVE_GCRYPT_MLKEM
size_t privkey_size;
enum gcry_kem_algos alg;
#else
#elif defined(HAVE_OPENSSL_MLKEM)
const char *name;
#else
size_t privkey_size;
#endif
};
extern const struct mlkem_type_info MLKEM768_INFO;
#ifdef HAVE_MLKEM1024
extern const struct mlkem_type_info MLKEM1024_INFO;
#endif
#define MLKEM_SHARED_SECRET_SIZE 32

View File

@@ -0,0 +1,127 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2025 by Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1 of the License.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#ifndef MLKEM_NATIVE_H_
#define MLKEM_NATIVE_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
A monomorphic instance of libcrux_ml_kem.types.MlKemPrivateKey
with const generics
- $2400size_t
*/
typedef struct libcrux_ml_kem_types_MlKemPrivateKey_d9_s {
uint8_t value[2400U];
} libcrux_ml_kem_types_MlKemPrivateKey_d9;
/**
A monomorphic instance of libcrux_ml_kem.types.MlKemPublicKey
with const generics
- $1184size_t
*/
typedef struct libcrux_ml_kem_types_MlKemPublicKey_30_s {
uint8_t value[1184U];
} libcrux_ml_kem_types_MlKemPublicKey_30;
typedef struct libcrux_ml_kem_mlkem768_MlKem768KeyPair_s {
libcrux_ml_kem_types_MlKemPrivateKey_d9 sk;
libcrux_ml_kem_types_MlKemPublicKey_30 pk;
} libcrux_ml_kem_mlkem768_MlKem768KeyPair;
typedef struct libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s {
uint8_t value[1088U];
} libcrux_ml_kem_mlkem768_MlKem768Ciphertext;
/**
A monomorphic instance of K.
with types libcrux_ml_kem_types_MlKemCiphertext[[$1088size_t]],
uint8_t[32size_t]
*/
typedef struct tuple_c2_s {
libcrux_ml_kem_mlkem768_MlKem768Ciphertext fst;
uint8_t snd[32U];
} tuple_c2;
/**
Generate ML-KEM 768 Key Pair
*/
libcrux_ml_kem_mlkem768_MlKem768KeyPair
libcrux_ml_kem_mlkem768_portable_generate_key_pair(uint8_t randomness[64U]);
/**
Validate a public key.
Returns `true` if valid, and `false` otherwise.
*/
bool libcrux_ml_kem_mlkem768_portable_validate_public_key(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key);
/**
Encapsulate ML-KEM 768
Generates an ([`MlKem768Ciphertext`], [`MlKemSharedSecret`]) tuple.
The input is a reference to an [`MlKem768PublicKey`] and [`SHARED_SECRET_SIZE`]
bytes of `randomness`.
*/
tuple_c2 libcrux_ml_kem_mlkem768_portable_encapsulate(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
uint8_t randomness[32U]);
/**
Decapsulate ML-KEM 768
Generates an [`MlKemSharedSecret`].
The input is a reference to an [`MlKem768PrivateKey`] and an
[`MlKem768Ciphertext`].
*/
void libcrux_ml_kem_mlkem768_portable_decapsulate(
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
uint8_t ret[32U]);
/* rename some types to be a bit more ergonomic */
#define libcrux_mlkem768_keypair libcrux_ml_kem_mlkem768_MlKem768KeyPair_s
#define libcrux_mlkem768_pk libcrux_ml_kem_types_MlKemPublicKey_30_s
#define libcrux_mlkem768_sk libcrux_ml_kem_types_MlKemPrivateKey_d9_s
#define libcrux_mlkem768_ciphertext libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s
#define libcrux_mlkem768_enc_result tuple_c2_s
/* defines for PRNG inputs */
#define LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN 64U
#define LIBCRUX_ML_KEM_ENC_PRNG_LEN 32
#ifdef __cplusplus
}
#endif
#endif /* MLKEM_NATIVE_H_ */

View File

@@ -105,6 +105,7 @@ set(libssh_SRCS
error.c
getpass.c
gzip.c
hybrid_mlkem.c
init.c
kdf.c
kex.c
@@ -115,6 +116,7 @@ set(libssh_SRCS
match.c
messages.c
misc.c
mlkem.c
options.c
packet.c
packet_cb.c
@@ -196,12 +198,12 @@ if (WITH_GCRYPT)
)
endif(HAVE_GCRYPT_CURVE25519)
if (HAVE_MLKEM)
if (HAVE_GCRYPT_MLKEM)
set(libssh_SRCS
${libssh_SRCS}
mlkem_gcrypt.c
)
endif (HAVE_MLKEM)
endif (HAVE_GCRYPT_MLKEM)
elseif (WITH_MBEDTLS)
set(libssh_SRCS
${libssh_SRCS}
@@ -255,12 +257,12 @@ else (WITH_GCRYPT)
chachapoly.c
)
endif (NOT HAVE_OPENSSL_EVP_CHACHA20)
if (HAVE_MLKEM)
if (HAVE_OPENSSL_MLKEM)
set(libssh_SRCS
${libssh_SRCS}
mlkem_crypto.c
)
endif (HAVE_MLKEM)
endif (HAVE_OPENSSL_MLKEM)
endif (WITH_GCRYPT)
if (WITH_SFTP)
@@ -313,13 +315,18 @@ if (NOT WITH_NACL)
endif()
endif (NOT WITH_NACL)
if (HAVE_MLKEM)
set(libssh_SRCS
${libssh_SRCS}
hybrid_mlkem.c
mlkem.c
)
endif (HAVE_MLKEM)
if (NOT HAVE_MLKEM1024)
set(libssh_SRCS
${libssh_SRCS}
mlkem_native.c
external/libcrux_mlkem768_sha3.c
)
if (WITH_WERROR_DECLARATION_AFTER_STATEMENT_FLAG)
set_source_files_properties(external/libcrux_mlkem768_sha3.c
PROPERTIES
COMPILE_FLAGS -Wno-error=declaration-after-statement)
endif()
endif()
if (WITH_FIDO2)
set(libssh_SRCS

View File

@@ -47,9 +47,7 @@
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/kex.h"
#ifdef HAVE_MLKEM
#include "libssh/hybrid_mlkem.h"
#endif
#ifndef _WIN32
#ifdef HAVE_PTHREAD
@@ -308,13 +306,13 @@ int dh_handshake(ssh_session session)
rc = ssh_client_sntrup761x25519_init(session);
break;
#endif
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_client_hybrid_mlkem_init(session);
break;
#endif
default:
rc = SSH_ERROR;
}

View File

@@ -53,13 +53,11 @@ static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
#endif /* OPENSSL_VERSION_NUMBER */
switch (kex_type) {
case SSH_KEX_ECDH_SHA2_NISTP256:
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768NISTP256_SHA256:
#endif
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
return NISTP256;
case SSH_KEX_ECDH_SHA2_NISTP384:
#ifdef HAVE_MLKEM
#if HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
return NISTP384;

View File

@@ -41,12 +41,10 @@ static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
switch (kex_type) {
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768NISTP256_SHA256:
#endif
return "NIST P-256";
case SSH_KEX_ECDH_SHA2_NISTP384:
#ifdef HAVE_MLKEM
#if HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
return "NIST P-384";

View File

@@ -38,16 +38,21 @@
#ifdef HAVE_ECDH
static mbedtls_ecp_group_id ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256 ||
kex_type == SSH_GSS_KEX_ECDH_NISTP256_SHA256) {
static mbedtls_ecp_group_id
ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
{
switch (kex_type) {
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
return MBEDTLS_ECP_DP_SECP256R1;
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
case SSH_KEX_ECDH_SHA2_NISTP384:
return MBEDTLS_ECP_DP_SECP384R1;
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
case SSH_KEX_ECDH_SHA2_NISTP521:
return MBEDTLS_ECP_DP_SECP521R1;
default:
return MBEDTLS_ECP_DP_NONE;
}
return MBEDTLS_ECP_DP_NONE;
}

8896
src/external/libcrux_mlkem768_sha3.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -102,9 +102,11 @@ static ssh_string derive_ecdh_secret(ssh_session session)
case SSH_KEX_MLKEM768NISTP256_SHA256:
secret = derive_nist_curve_secret(session, NISTP256_SHARED_SECRET_SIZE);
break;
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
secret = derive_nist_curve_secret(session, NISTP384_SHARED_SECRET_SIZE);
break;
#endif
default:
ssh_set_error(session, SSH_FATAL, "Unsupported KEX type");
return NULL;
@@ -129,10 +131,12 @@ static int derive_hybrid_secret(ssh_session session,
digest = sha256;
digest_len = SHA256_DIGEST_LEN;
break;
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
digest = sha384;
digest_len = SHA384_DIGEST_LEN;
break;
#endif
default:
ssh_set_error(session, SSH_FATAL, "Unsupported KEX type");
goto cleanup;
@@ -244,7 +248,9 @@ int ssh_client_hybrid_mlkem_init(ssh_session session)
crypto->curve25519_client_pubkey);
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_ecdh_init(session);
if (rc != SSH_OK) {
ssh_set_error(session,
@@ -435,7 +441,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_hybrid_mlkem_reply)
#endif
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ecdh_server_pubkey_size = ssh_buffer_get_len(server_reply_buffer);
ssh_string_free(crypto->ecdh_server_pubkey);
crypto->ecdh_server_pubkey = ssh_string_new(ecdh_server_pubkey_size);
@@ -585,7 +593,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
#endif
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_ecdh_init(session);
if (rc != SSH_OK) {
ssh_set_error(session,
@@ -682,7 +692,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
#endif
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ecdh_client_pubkey_size = ssh_buffer_get_len(client_init_buffer);
ssh_string_free(crypto->ecdh_client_pubkey);
crypto->ecdh_client_pubkey = ssh_string_new(ecdh_client_pubkey_size);
@@ -760,7 +772,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
crypto->curve25519_server_pubkey);
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_buffer_pack(server_reply_buffer,
"PP",
ssh_string_len(crypto->mlkem_ciphertext),

View File

@@ -41,9 +41,7 @@
#include "libssh/string.h"
#include "libssh/curve25519.h"
#include "libssh/sntrup761.h"
#ifdef HAVE_MLKEM
#include "libssh/hybrid_mlkem.h"
#endif
#include "libssh/kex-gss.h"
#include "libssh/knownhosts.h"
#include "libssh/misc.h"
@@ -107,13 +105,14 @@
#define SNTRUP761X25519 ""
#endif /* HAVE_SNTRUP761 */
#ifdef HAVE_MLKEM
#ifdef HAVE_MLKEM1024
#define HYBRID_MLKEM "mlkem768x25519-sha256," \
"mlkem768nistp256-sha256," \
"mlkem1024nistp384-sha384,"
#else
#define HYBRID_MLKEM ""
#endif /* HAVE_MLKEM */
#define HYBRID_MLKEM "mlkem768x25519-sha256," \
"mlkem768nistp256-sha256,"
#endif /* HAVE_MLKEM1024 */
#ifdef HAVE_ECC
#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
@@ -996,11 +995,11 @@ kex_select_kex_type(const char *kex)
return SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM;
} else if (strcmp(kex, "sntrup761x25519-sha512") == 0) {
return SSH_KEX_SNTRUP761X25519_SHA512;
#ifdef HAVE_MLKEM
} else if (strcmp(kex, "mlkem768x25519-sha256") == 0) {
return SSH_KEX_MLKEM768X25519_SHA256;
} else if (strcmp(kex, "mlkem768nistp256-sha256") == 0) {
return SSH_KEX_MLKEM768NISTP256_SHA256;
#ifdef HAVE_MLKEM1024
} else if (strcmp(kex, "mlkem1024nistp384-sha384") == 0) {
return SSH_KEX_MLKEM1024NISTP384_SHA384;
#endif
@@ -1058,13 +1057,13 @@ static void revert_kex_callbacks(ssh_session session)
ssh_client_sntrup761x25519_remove_callbacks(session);
break;
#endif
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ssh_client_hybrid_mlkem_remove_callbacks(session);
break;
#endif
}
}
@@ -1664,10 +1663,11 @@ int ssh_make_sessionid(ssh_session session)
}
break;
#endif /* HAVE_SNTRUP761 */
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_buffer_pack(buf,
"SS",
session->next_crypto->hybrid_client_init,
@@ -1679,7 +1679,6 @@ int ssh_make_sessionid(ssh_session session)
goto error;
}
break;
#endif /* HAVE_MLKEM */
default:
/* Handle unsupported kex types - this should not happen in normal operation */
rc = SSH_ERROR;
@@ -1694,13 +1693,13 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->shared_secret,
SHA512_DIGEST_LEN);
break;
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_buffer_pack(buf, "S", session->next_crypto->hybrid_shared_secret);
break;
#endif /* HAVE_MLKEM */
default:
rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret);
break;
@@ -1737,10 +1736,8 @@ int ssh_make_sessionid(ssh_session session)
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_KEX_CURVE25519_SHA256:
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#endif
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
case SSH_GSS_KEX_CURVE25519_SHA256:
#ifdef WITH_GEX
@@ -1757,7 +1754,7 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->secret_hash);
break;
case SSH_KEX_ECDH_SHA2_NISTP384:
#ifdef HAVE_MLKEM
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
session->next_crypto->digest_len = SHA384_DIGEST_LENGTH;
@@ -1925,13 +1922,13 @@ int ssh_generate_session_keys(ssh_session session)
k_string = ssh_make_padded_bignum_string(crypto->shared_secret,
crypto->digest_len);
break;
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
k_string = ssh_string_copy(crypto->hybrid_shared_secret);
break;
#endif /* HAVE_MLKEM */
default:
k_string = ssh_make_bignum_string(crypto->shared_secret);
break;

View File

@@ -30,8 +30,10 @@ const struct mlkem_type_info *kex_type_to_mlkem_info(enum ssh_key_exchange_e kex
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
return &MLKEM768_INFO;
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
return &MLKEM1024_INFO;
#endif
default:
return NULL;
}

202
src/mlkem_native.c Normal file
View File

@@ -0,0 +1,202 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2025 by Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1 of the License.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/crypto.h"
#include "libssh/mlkem.h"
#include "libssh/mlkem_native.h"
#include "libssh/session.h"
#define crypto_kem_mlkem768_PUBLICKEYBYTES 1184
#define crypto_kem_mlkem768_SECRETKEYBYTES 2400
#define crypto_kem_mlkem768_CIPHERTEXTBYTES 1088
const struct mlkem_type_info MLKEM768_INFO = {
.pubkey_size = crypto_kem_mlkem768_PUBLICKEYBYTES,
.privkey_size = crypto_kem_mlkem768_SECRETKEYBYTES,
.ciphertext_size = crypto_kem_mlkem768_CIPHERTEXTBYTES,
};
int ssh_mlkem_init(ssh_session session)
{
int ret = SSH_ERROR;
struct ssh_crypto_struct *crypto = session->next_crypto;
const struct mlkem_type_info *mlkem_info = NULL;
unsigned char rnd[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN];
struct libcrux_mlkem768_keypair keypair;
int err;
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
goto cleanup;
}
err = ssh_get_random(rnd, sizeof(rnd), 0);
if (err != 1) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to generate random data for ML-KEM keygen");
goto cleanup;
}
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
if (ssh_string_len(crypto->mlkem_client_pubkey) < mlkem_info->pubkey_size) {
SSH_STRING_FREE(crypto->mlkem_client_pubkey);
}
if (crypto->mlkem_client_pubkey == NULL) {
crypto->mlkem_client_pubkey = ssh_string_new(mlkem_info->pubkey_size);
if (crypto->mlkem_client_pubkey == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
}
err = ssh_string_fill(crypto->mlkem_client_pubkey,
keypair.pk.value,
mlkem_info->pubkey_size);
if (err) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to fill the string with client pubkey");
goto cleanup;
}
if (crypto->mlkem_privkey == NULL) {
crypto->mlkem_privkey = malloc(mlkem_info->privkey_size);
if (crypto->mlkem_privkey == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
}
memcpy(crypto->mlkem_privkey, keypair.sk.value, mlkem_info->privkey_size);
crypto->mlkem_privkey_len = mlkem_info->privkey_size;
ret = SSH_OK;
cleanup:
ssh_burn(&keypair, sizeof(keypair));
ssh_burn(rnd, sizeof(rnd));
return ret;
}
int ssh_mlkem_encapsulate(ssh_session session,
ssh_mlkem_shared_secret shared_secret)
{
int ret = SSH_ERROR;
const struct mlkem_type_info *mlkem_info = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
const unsigned char *pubkey_data = NULL;
ssh_string pubkey = crypto->mlkem_client_pubkey;
struct libcrux_mlkem768_enc_result enc;
struct libcrux_mlkem768_pk mlkem_pub = {0};
unsigned char rnd[LIBCRUX_ML_KEM_ENC_PRNG_LEN];
int err;
if (pubkey == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing pubkey in session");
return SSH_ERROR;
}
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
return SSH_ERROR;
}
pubkey_data = ssh_string_data(pubkey);
memcpy(mlkem_pub.value, pubkey_data, mlkem_info->pubkey_size);
err = libcrux_ml_kem_mlkem768_portable_validate_public_key(&mlkem_pub);
if (err == 0) {
SSH_LOG(SSH_LOG_WARNING, "Invalid public key");
return SSH_ERROR;
}
err = ssh_get_random(rnd, sizeof(rnd), 0);
if (err != 1) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to generate random data for ML-KEM keygen");
goto cleanup;
}
enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&mlkem_pub, rnd);
if (ssh_string_len(crypto->mlkem_ciphertext) < mlkem_info->ciphertext_size) {
SSH_STRING_FREE(crypto->mlkem_ciphertext);
}
if (crypto->mlkem_ciphertext == NULL) {
crypto->mlkem_ciphertext = ssh_string_new(mlkem_info->ciphertext_size);
if (crypto->mlkem_ciphertext == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
}
err = ssh_string_fill(crypto->mlkem_ciphertext,
enc.fst.value,
sizeof(enc.fst.value));
if (err != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to fill the string with ciphertext");
goto cleanup;
}
memcpy(shared_secret, enc.snd, sizeof(enc.snd));
ret = SSH_OK;
cleanup:
ssh_burn(rnd, sizeof(rnd));
ssh_burn(&enc, sizeof(enc));
return ret;
}
int ssh_mlkem_decapsulate(const ssh_session session,
ssh_mlkem_shared_secret shared_secret)
{
const struct mlkem_type_info *mlkem_info = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
ssh_string ciphertext = NULL;
unsigned char *ciphertext_data = NULL;
struct libcrux_mlkem768_sk mlkem_priv = {0};
struct libcrux_mlkem768_ciphertext mlkem_ciphertext = {0};
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
return SSH_ERROR;
}
ciphertext = crypto->mlkem_ciphertext;
if (ciphertext == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing ciphertext in session");
return SSH_ERROR;
}
ciphertext_data = ssh_string_data(ciphertext);
memcpy(mlkem_ciphertext.value,
ciphertext_data,
sizeof(mlkem_ciphertext.value));
memcpy(mlkem_priv.value, crypto->mlkem_privkey, crypto->mlkem_privkey_len);
libcrux_ml_kem_mlkem768_portable_decapsulate(&mlkem_priv,
&mlkem_ciphertext,
shared_secret);
return SSH_OK;
}

View File

@@ -505,14 +505,14 @@ const char* ssh_get_kex_algo(ssh_session session) {
return "sntrup761x25519-sha512@openssh.com";
case SSH_KEX_SNTRUP761X25519_SHA512:
return "sntrup761x25519-sha512";
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
return "mlkem768x25519-sha256";
case SSH_KEX_MLKEM768NISTP256_SHA256:
return "mlkem768nistp256-sha256";
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
return "mlkem1024nistp384-sha384";
#endif /* HAVE_MLKEM */
#endif /* HAVE_MLKEM1024 */
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1:
return "diffie-hellman-group-exchange-sha1";

View File

@@ -51,10 +51,8 @@
#include "libssh/curve25519.h"
#include "libssh/kex-gss.h"
#include "libssh/ecdh.h"
#include "libssh/sntrup761.h"
#ifdef HAVE_MLKEM
#include "libssh/hybrid_mlkem.h"
#endif
#include "libssh/sntrup761.h"
static struct ssh_hmac_struct ssh_hmac_tab[] = {
{ "hmac-sha1", SSH_HMAC_SHA1, false },
@@ -230,15 +228,14 @@ void crypto_free(struct ssh_crypto_struct *crypto)
SAFE_FREE(crypto->kex_methods[i]);
}
#ifdef HAVE_MLKEM
#ifdef HAVE_LIBGCRYPT
#ifdef HAVE_OPENSSL_MLKEM
EVP_PKEY_free(crypto->mlkem_privkey);
#else
if (crypto->mlkem_privkey != NULL) {
ssh_burn(crypto->mlkem_privkey, crypto->mlkem_privkey_len);
SAFE_FREE(crypto->mlkem_privkey);
crypto->mlkem_privkey_len = 0;
}
#else
EVP_PKEY_free(crypto->mlkem_privkey);
#endif
ssh_string_burn(crypto->hybrid_shared_secret);
ssh_string_free(crypto->mlkem_client_pubkey);
@@ -246,7 +243,6 @@ void crypto_free(struct ssh_crypto_struct *crypto)
ssh_string_free(crypto->hybrid_client_init);
ssh_string_free(crypto->hybrid_server_reply);
ssh_string_free(crypto->hybrid_shared_secret);
#endif
ssh_burn(crypto, sizeof(struct ssh_crypto_struct));
@@ -629,13 +625,13 @@ int crypt_set_algorithms_server(ssh_session session){
ssh_server_sntrup761x25519_init(session);
break;
#endif
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ssh_server_hybrid_mlkem_init(session);
break;
#endif
default:
ssh_set_error(session,
SSH_FATAL,

View File

@@ -90,6 +90,11 @@ if (CLIENT_TESTING)
"-Wl,--wrap=sntrup761_keypair"
"-Wl,--wrap=sntrup761_enc"
"-Wl,--wrap=sntrup761_dec")
list(APPEND WRAP_SYMBOLS
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_generate_key_pair"
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_validate_public_key"
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_encapsulate"
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_decapsulate")
add_library(${TORTURE_SHARED_LIBRARY}
SHARED

View File

@@ -752,7 +752,7 @@ torture_algorithms_ecdh_sntrup761x25519_sha512(void **state)
}
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
#if defined(OPENSSH_MLKEM768X25519_SHA256)
static void torture_algorithms_ecdh_mlkem768x25519_sha256(void **state)
{
struct torture_state *s = *state;
@@ -766,9 +766,9 @@ static void torture_algorithms_ecdh_mlkem768x25519_sha256(void **state)
NULL /*cipher*/,
NULL /*hmac*/);
}
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768X25519_SHA256) */
#endif /* defined(OPENSSH_MLKEM768X25519_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
static void torture_algorithms_ecdh_mlkem768nistp256_sha256(void **state)
{
struct torture_state *s = *state;
@@ -782,9 +782,9 @@ static void torture_algorithms_ecdh_mlkem768nistp256_sha256(void **state)
NULL /*cipher*/,
NULL /*hmac*/);
}
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#endif /* defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
static void torture_algorithms_ecdh_mlkem1024nistp384_sha384(void **state)
{
struct torture_state *s = *state;
@@ -798,7 +798,7 @@ static void torture_algorithms_ecdh_mlkem1024nistp384_sha384(void **state)
NULL /*cipher*/,
NULL /*hmac*/);
}
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
#endif /* HAVE_MLKEM1024 && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
static void torture_algorithms_dh_group1(void **state) {
struct torture_state *s = *state;
@@ -1077,21 +1077,21 @@ int torture_run_tests(void) {
session_setup,
session_teardown),
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
#if defined(OPENSSH_MLKEM768X25519_SHA256)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem768x25519_sha256,
session_setup,
session_teardown),
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768X25519_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
#endif /* defined(OPENSSH_MLKEM768X25519_SHA256) */
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem768nistp256_sha256,
session_setup,
session_teardown),
#endif /* defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#endif /* defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem1024nistp384_sha384,
session_setup,
session_teardown),
#endif /* defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
#endif /* defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
#if defined(HAVE_ECC)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_sha2_nistp256,
session_setup,

View File

@@ -88,8 +88,19 @@ target_link_libraries(sntrup761_override
set(SNTRUP761_OVERRIDE_LIBRARY
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}sntrup761_override${CMAKE_SHARED_LIBRARY_SUFFIX})
# mlkem768_override
add_library(mlkem768_override SHARED
mlkem768_override.c
${libssh_SOURCE_DIR}/src/external/libcrux_mlkem768_sha3.c
${override_src}
)
target_link_libraries(mlkem768_override
PRIVATE ${override_libs})
set(MLKEM768_OVERRIDE_LIBRARY
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}mlkem768_override${CMAKE_SHARED_LIBRARY_SUFFIX})
set(OVERRIDE_LIBRARIES
${CHACHA20_OVERRIDE_LIBRARY}:${POLY1305_OVERRIDE_LIBRARY}:${ED25519_OVERRIDE_LIBRARY}:${CURVE25519_OVERRIDE_LIBRARY}:${SNTRUP761_OVERRIDE_LIBRARY}
${CHACHA20_OVERRIDE_LIBRARY}:${POLY1305_OVERRIDE_LIBRARY}:${ED25519_OVERRIDE_LIBRARY}:${CURVE25519_OVERRIDE_LIBRARY}:${SNTRUP761_OVERRIDE_LIBRARY}:${MLKEM768_OVERRIDE_LIBRARY}
)
if (WITH_MBEDTLS)
@@ -107,6 +118,7 @@ if (WITH_MBEDTLS)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=1")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
elseif (WITH_GCRYPT)
if (HAVE_GCRYPT_CHACHA_POLY)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
@@ -122,6 +134,11 @@ elseif (WITH_GCRYPT)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=0")
if(HAVE_GCRYPT_MLKEM)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=0")
else()
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
endif()
else ()
if (HAVE_OPENSSL_EVP_CHACHA20)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
@@ -131,6 +148,11 @@ else ()
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=0")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=0")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=1")
if(HAVE_OPENSSL_MLKEM)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=0")
else()
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
endif()
endif ()
if (NOT OSX)
@@ -162,6 +184,7 @@ foreach(_OVERRIDE_TEST ${LIBSSH_OVERRIDE_TESTS})
ed25519_override
curve25519_override
sntrup761_override
mlkem768_override
)
if (OSX)

View File

@@ -0,0 +1,83 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2021 - 2025 Red Hat, Inc.
*
* Authors: Anderson Toshiyuki Sasaki
* Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 2.1 of the License, or (at your option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the SSH Library; see the file COPYING. If not,
* see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libssh/priv.h>
#include "libssh/mlkem_native.h"
#include "mlkem768_override.h"
static bool internal_function_called = false;
libcrux_ml_kem_mlkem768_MlKem768KeyPair
__wrap_libcrux_ml_kem_mlkem768_portable_generate_key_pair(
uint8_t randomness[64U])
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_generate_key_pair(randomness);
}
bool __wrap_libcrux_ml_kem_mlkem768_portable_validate_public_key(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key)
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_validate_public_key(public_key);
}
tuple_c2 __wrap_libcrux_ml_kem_mlkem768_portable_encapsulate(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
uint8_t randomness[32U])
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_encapsulate(public_key, randomness);
}
void __wrap_libcrux_ml_kem_mlkem768_portable_decapsulate(
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
uint8_t ret[32U])
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_decapsulate(private_key,
ciphertext,
ret);
}
bool internal_mlkem768_function_called(void)
{
return internal_function_called;
}
void reset_mlkem768_function_called(void)
{
internal_function_called = false;
}

View File

@@ -0,0 +1,43 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2021 - 2025 Red Hat, Inc.
*
* Authors: Anderson Toshiyuki Sasaki
* Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 2.1 of the License, or (at your option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the SSH Library; see the file COPYING. If not,
* see <https://www.gnu.org/licenses/>.
*/
#include "libssh/mlkem_native.h"
libcrux_ml_kem_mlkem768_MlKem768KeyPair
__wrap_libcrux_ml_kem_mlkem768_portable_generate_key_pair(
uint8_t randomness[64U]);
bool __wrap_libcrux_ml_kem_mlkem768_portable_validate_public_key(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key);
tuple_c2 __wrap_libcrux_ml_kem_mlkem768_portable_encapsulate(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
uint8_t randomness[32U]);
void __wrap_libcrux_ml_kem_mlkem768_portable_decapsulate(
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
uint8_t ret[32U]);
bool internal_mlkem768_function_called(void);
void reset_mlkem768_function_called(void);

View File

@@ -32,6 +32,7 @@
#include "chacha20_override.h"
#include "curve25519_override.h"
#include "ed25519_override.h"
#include "mlkem768_override.h"
#include "poly1305_override.h"
#include "sntrup761_override.h"
@@ -119,6 +120,8 @@ static int session_setup(void **state)
reset_poly1305_function_called();
reset_curve25519_function_called();
reset_ed25519_function_called();
reset_sntrup761_function_called();
reset_mlkem768_function_called();
return 0;
}
@@ -324,6 +327,64 @@ torture_override_ecdh_sntrup761x25519_sha512(void **state)
}
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#ifdef OPENSSH_MLKEM768X25519_SHA256
static void torture_override_mlkem768x25519_sha256(void **state)
{
struct torture_state *s = *state;
bool internal_curve25519_called;
bool internal_mlkem768_called;
if (ssh_fips_mode()) {
skip();
}
test_algorithm(s->ssh.session,
"mlkem768x25519-sha256",
NULL, /* cipher */
NULL /* hostkey */);
internal_curve25519_called = internal_curve25519_function_called();
internal_mlkem768_called = internal_mlkem768_function_called();
#if SHOULD_CALL_INTERNAL_MLKEM
assert_true(internal_mlkem768_called);
#else
assert_false(internal_mlkem768_called);
#endif
#if SHOULD_CALL_INTERNAL_CURVE25519
assert_true(internal_curve25519_called);
#else
assert_false(internal_curve25519_called);
#endif
}
#endif /* OPENSSH_MLKEM768X25519_SHA256 */
#ifdef OPENSSH_MLKEM768NISTP256_SHA256
static void torture_override_mlkem768nistp256_sha256(void **state)
{
struct torture_state *s = *state;
bool internal_mlkem768_called;
if (ssh_fips_mode()) {
skip();
}
test_algorithm(s->ssh.session,
"mlkem768nistp256-sha256",
NULL, /* cipher */
NULL /* hostkey */);
internal_mlkem768_called = internal_mlkem768_function_called();
#if SHOULD_CALL_INTERNAL_MLKEM
assert_true(internal_mlkem768_called);
#else
assert_false(internal_mlkem768_called);
#endif
}
#endif /* OPENSSH_MLKEM768NISTP256_SHA256 */
#ifdef OPENSSH_SSH_ED25519
static void torture_override_ed25519(void **state)
{
@@ -378,6 +439,16 @@ int torture_run_tests(void)
session_setup,
session_teardown),
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#ifdef OPENSSH_MLKEM768X25519_SHA256
cmocka_unit_test_setup_teardown(torture_override_mlkem768x25519_sha256,
session_setup,
session_teardown),
#endif /* OPENSSH_MLKEM768X25519_SHA256 */
#ifdef OPENSSH_MLKEM768NISTP256_SHA256
cmocka_unit_test_setup_teardown(torture_override_mlkem768nistp256_sha256,
session_setup,
session_teardown),
#endif /* OPENSSH_MLKEM768NISTP256_SHA256 */
#ifdef OPENSSH_SSH_ED25519
cmocka_unit_test_setup_teardown(torture_override_ed25519,
session_setup,

View File

@@ -306,7 +306,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
#define PKDTESTS_KEX_SNTRUP761(f, client, kexcmd)
#endif
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
#if defined(OPENSSH_MLKEM768X25519_SHA256)
#define PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd) \
f(client, rsa_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
@@ -317,7 +317,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
#define PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd)
#endif
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
#define PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd) \
f(client, rsa_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
@@ -328,7 +328,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
#define PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd)
#endif
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#define PKDTESTS_KEX_MLKEM1024NISTP384(f, client, kexcmd) \
f(client, rsa_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \

View File

@@ -346,7 +346,6 @@ static void torture_algorithm_aes128gcm_with_no_hmac_overlap(void **state)
test_algorithm_no_hmac_overlap(state, "aes128-gcm@openssh.com");
}
#ifdef HAVE_MLKEM
/*
* Check the self-compatibility of a given key exchange method.
*/
@@ -418,6 +417,7 @@ static void torture_algorithm_mlkem768nistp256_self_compat(void **state)
test_kex_self_compat(state, "mlkem768nistp256-sha256");
}
#ifdef HAVE_MLKEM1024
static void torture_algorithm_mlkem1024nistp384_self_compat(void **state)
{
if (ssh_fips_mode()) {
@@ -425,7 +425,7 @@ static void torture_algorithm_mlkem1024nistp384_self_compat(void **state)
}
test_kex_self_compat(state, "mlkem1024nistp384-sha384");
}
#endif /* HAVE_MLKEM */
#endif /* HAVE_MLKEM1024 */
int torture_run_tests(void)
{
@@ -437,14 +437,14 @@ int torture_run_tests(void)
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_algorithm_aes128gcm_with_no_hmac_overlap,
setup_temp_dir, teardown_temp_dir),
#ifdef HAVE_MLKEM
cmocka_unit_test_setup_teardown(torture_algorithm_mlkem768x25119_self_compat,
setup_temp_dir, teardown_temp_dir),
cmocka_unit_test_setup_teardown(torture_algorithm_mlkem768nistp256_self_compat,
setup_temp_dir, teardown_temp_dir),
#ifdef HAVE_MLKEM1024
cmocka_unit_test_setup_teardown(torture_algorithm_mlkem1024nistp384_self_compat,
setup_temp_dir, teardown_temp_dir),
#endif /* HAVE_MLKEM */
#endif /* HAVE_MLKEM1024 */
};
ssh_init();

View File

@@ -282,7 +282,7 @@ static void torture_options_get_key_exchange(void **state)
"diffie-hellman-group16-sha512,"
"diffie-hellman-group18-sha512");
} else {
#ifdef HAVE_MLKEM
#ifdef HAVE_MLKEM1024
assert_string_equal(value,
"mlkem768x25519-sha256,"
"mlkem768nistp256-sha256,"
@@ -296,6 +296,8 @@ static void torture_options_get_key_exchange(void **state)
"diffie-hellman-group14-sha256");
#else
assert_string_equal(value,
"mlkem768x25519-sha256,"
"mlkem768nistp256-sha256,"
"sntrup761x25519-sha512,"
"sntrup761x25519-sha512@openssh.com,"
"curve25519-sha256,curve25519-sha256@libssh.org,"