refactor(curve25519): split the single file curve25519.c into multiple files for better readability

Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Praneeth Sarode
2025-04-18 19:50:54 +05:30
committed by Jakub Jelen
parent d758990d39
commit dcb65fe584
8 changed files with 756 additions and 629 deletions

View File

@@ -51,6 +51,7 @@ typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE];
typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE]; typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE];
int ssh_curve25519_init(ssh_session session); int ssh_curve25519_init(ssh_session session);
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k);
int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k); int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k);
int ssh_client_curve25519_init(ssh_session session); int ssh_client_curve25519_init(ssh_session session);

View File

@@ -175,6 +175,13 @@ if (WITH_GCRYPT)
chachapoly.c chachapoly.c
) )
endif (NOT HAVE_GCRYPT_CHACHA_POLY) endif (NOT HAVE_GCRYPT_CHACHA_POLY)
if (HAVE_GCRYPT_CURVE25519)
set(libssh_SRCS
${libssh_SRCS}
curve25519_gcrypt.c
)
endif(HAVE_GCRYPT_CURVE25519)
elseif (WITH_MBEDTLS) elseif (WITH_MBEDTLS)
set(libssh_SRCS set(libssh_SRCS
${libssh_SRCS} ${libssh_SRCS}
@@ -201,13 +208,19 @@ elseif (WITH_MBEDTLS)
chachapoly.c chachapoly.c
) )
endif() endif()
if (HAVE_MBEDTLS_CURVE25519)
set(libssh_SRCS
${libssh_SRCS}
curve25519_mbedcrypto.c
)
endif(HAVE_MBEDTLS_CURVE25519)
else (WITH_GCRYPT) else (WITH_GCRYPT)
set(libssh_SRCS set(libssh_SRCS
${libssh_SRCS} ${libssh_SRCS}
threads/libcrypto.c threads/libcrypto.c
pki_crypto.c pki_crypto.c
ecdh_crypto.c ecdh_crypto.c
curve25519_crypto.c
getrandom_crypto.c getrandom_crypto.c
md_crypto.c md_crypto.c
libcrypto.c libcrypto.c
@@ -264,9 +277,10 @@ if (WITH_GSSAPI AND GSSAPI_FOUND)
endif (WITH_GSSAPI AND GSSAPI_FOUND) endif (WITH_GSSAPI AND GSSAPI_FOUND)
if (NOT WITH_NACL) if (NOT WITH_NACL)
if (NOT HAVE_LIBCRYPTO) if (NOT (HAVE_LIBCRYPTO OR HAVE_MBEDTLS_CURVE25519 OR HAVE_GCRYPT_CURVE25519))
set(libssh_SRCS set(libssh_SRCS
${libssh_SRCS} ${libssh_SRCS}
curve25519_fallback.c
external/curve25519_ref.c external/curve25519_ref.c
) )
endif() endif()

View File

@@ -26,269 +26,43 @@
#include "libssh/curve25519.h" #include "libssh/curve25519.h"
#ifdef HAVE_CURVE25519 #ifdef HAVE_CURVE25519
#ifdef WITH_NACL #include "libssh/bignum.h"
#include "nacl/crypto_scalarmult_curve25519.h"
#endif
#include "libssh/ssh2.h"
#include "libssh/buffer.h" #include "libssh/buffer.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/crypto.h" #include "libssh/crypto.h"
#include "libssh/dh.h" #include "libssh/dh.h"
#include "libssh/pki.h" #include "libssh/pki.h"
#include "libssh/bignum.h" #include "libssh/priv.h"
#include "libssh/session.h"
#ifdef HAVE_LIBCRYPTO #include "libssh/ssh2.h"
#include <openssl/err.h>
#elif defined(HAVE_MBEDTLS_CURVE25519)
#include "mbedcrypto-compat.h"
#include <mbedtls/ecdh.h>
#include <mbedtls/error.h>
#elif defined(HAVE_GCRYPT_CURVE25519)
#include <gcrypt.h>
#endif
static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply); static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply);
static ssh_packet_callback dh_client_callbacks[] = { static ssh_packet_callback dh_client_callbacks[] = {
ssh_packet_client_curve25519_reply ssh_packet_client_curve25519_reply,
}; };
static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = { static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = {
.start = SSH2_MSG_KEX_ECDH_REPLY, .start = SSH2_MSG_KEX_ECDH_REPLY,
.n_callbacks = 1, .n_callbacks = 1,
.callbacks = dh_client_callbacks, .callbacks = dh_client_callbacks,
.user = NULL .user = NULL,
}; };
int ssh_curve25519_init(ssh_session session) int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k)
{ {
ssh_curve25519_pubkey *pubkey_loc = NULL;
#ifdef HAVE_LIBCRYPTO
int rc; int rc;
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL;
size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
#elif defined(HAVE_MBEDTLS_CURVE25519) #ifdef DEBUG_CRYPTO
int rc; ssh_log_hexdump("Session server cookie",
mbedtls_ecdh_context ecdh_ctx; session->next_crypto->server_kex.cookie,
mbedtls_ctr_drbg_context *ctr_drbg = NULL; 16);
char error_buf[128]; ssh_log_hexdump("Session client cookie",
int ret = SSH_ERROR; session->next_crypto->client_kex.cookie,
16);
#elif defined(HAVE_GCRYPT_CURVE25519)
gcry_error_t gcry_err;
gcry_sexp_t param = NULL, keypair_sexp = NULL;
ssh_string privkey = NULL, pubkey = NULL;
char *pubkey_data = NULL;
int ret = SSH_ERROR;
#else
int rc;
#endif #endif
if (session->server) { rc = curve25519_do_create_k(session, k);
pubkey_loc = &session->next_crypto->curve25519_server_pubkey; return rc;
} else {
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
}
#ifdef HAVE_LIBCRYPTO
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
if (pctx == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 context: %s",
ERR_error_string(ERR_get_error(), NULL));
return SSH_ERROR;
}
rc = EVP_PKEY_keygen_init(pctx);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 keygen: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_CTX_free(pctx);
return SSH_ERROR;
}
rc = EVP_PKEY_keygen(pctx, &pkey);
EVP_PKEY_CTX_free(pctx);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to generate X25519 keys: %s",
ERR_error_string(ERR_get_error(), NULL));
return SSH_ERROR;
}
rc = EVP_PKEY_get_raw_public_key(pkey, *pubkey_loc, &pubkey_len);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to get X25519 raw public key: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_free(pkey);
return SSH_ERROR;
}
session->next_crypto->curve25519_privkey = pkey;
pkey = NULL;
#elif defined(HAVE_MBEDTLS_CURVE25519)
ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
mbedtls_ecdh_init(&ecdh_ctx);
rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_ecdh_gen_public(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(grp),
&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(d),
&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(Q),
mbedtls_ctr_drbg_random,
ctr_drbg);
#else
rc = mbedtls_ecdh_gen_public(&ecdh_ctx.grp,
&ecdh_ctx.d,
&ecdh_ctx.Q,
mbedtls_ctr_drbg_random,
ctr_drbg);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to generate X25519 keypair: %s",
error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(d),
session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE);
#else
rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.d,
session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to write X25519 private key: %s",
error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(Q)
.MBEDTLS_PRIVATE(X),
*pubkey_loc,
CURVE25519_PUBKEY_SIZE);
#else
rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.Q.X,
*pubkey_loc,
CURVE25519_PUBKEY_SIZE);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to write X25519 public key: %s",
error_buf);
goto out;
}
ret = SSH_OK;
out:
mbedtls_ecdh_free(&ecdh_ctx);
return ret;
#elif defined(HAVE_GCRYPT_CURVE25519)
gcry_err =
gcry_sexp_build(&param, NULL, "(genkey (ecdh (curve Curve25519)))");
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create keypair sexp: %s",
gcry_strerror(gcry_err));
goto out;
}
gcry_err = gcry_pk_genkey(&keypair_sexp, param);
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to generate keypair: %s",
gcry_strerror(gcry_err));
goto out;
}
/* Extract the public key */
pubkey = ssh_sexp_extract_mpi(keypair_sexp,
"q",
GCRYMPI_FMT_USG,
GCRYMPI_FMT_STD);
if (pubkey == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to extract public key: %s",
gcry_strerror(gcry_err));
goto out;
}
/* Store the public key in the session */
/* The first byte should be 0x40 indicating that the point is compressed, so
* we skip storing it */
pubkey_data = (char *)ssh_string_data(pubkey);
if (ssh_string_len(pubkey) != CURVE25519_PUBKEY_SIZE + 1 ||
pubkey_data[0] != 0x40) {
SSH_LOG(SSH_LOG_TRACE,
"Invalid public key with length: %zu",
ssh_string_len(pubkey));
goto out;
}
memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE);
/* Store the private key */
session->next_crypto->curve25519_privkey = keypair_sexp;
keypair_sexp = NULL;
ret = SSH_OK;
out:
ssh_string_burn(privkey);
SSH_STRING_FREE(privkey);
ssh_string_burn(pubkey);
SSH_STRING_FREE(pubkey);
gcry_sexp_release(param);
gcry_sexp_release(keypair_sexp);
return ret;
#else
rc = ssh_get_random(session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE, 1);
if (rc != 1) {
ssh_set_error(session, SSH_FATAL, "PRNG error");
return SSH_ERROR;
}
crypto_scalarmult_base(*pubkey_loc,
session->next_crypto->curve25519_privkey);
#endif /* HAVE_LIBCRYPTO */
return SSH_OK;
} }
/** @internal /** @internal
@@ -327,311 +101,6 @@ void ssh_client_curve25519_remove_callbacks(ssh_session session)
ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks); ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
} }
int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k)
{
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
#ifdef HAVE_LIBCRYPTO
int rc, ret = SSH_ERROR;
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL, *pubkey = NULL;
size_t shared_key_len = CURVE25519_PUBKEY_SIZE;
#elif defined(HAVE_MBEDTLS_CURVE25519)
int rc, ret = SSH_ERROR;
mbedtls_ecdh_context ecdh_ctx;
mbedtls_ctr_drbg_context *ctr_drbg = NULL;
char error_buf[128];
#elif defined(HAVE_GCRYPT_CURVE25519)
gcry_error_t gcry_err;
gcry_sexp_t pubkey_sexp = NULL, privkey_data_sexp = NULL,
result_sexp = NULL;
ssh_string shared_secret = NULL, privkey = NULL;
char *shared_secret_data = NULL;
int ret = SSH_ERROR;
#endif
if (session->server) {
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
} else {
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
}
#ifdef HAVE_LIBCRYPTO
pkey = session->next_crypto->curve25519_privkey;
if (pkey == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create X25519 EVP_PKEY: %s",
ERR_error_string(ERR_get_error(), NULL));
return SSH_ERROR;
}
pctx = EVP_PKEY_CTX_new(pkey, NULL);
if (pctx == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 context: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
rc = EVP_PKEY_derive_init(pctx);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 key derivation: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519,
NULL,
*peer_pubkey_loc,
CURVE25519_PUBKEY_SIZE);
if (pubkey == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create X25519 public key EVP_PKEY: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
rc = EVP_PKEY_derive_set_peer(pctx, pubkey);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to set peer X25519 public key: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
rc = EVP_PKEY_derive(pctx, k, &shared_key_len);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to derive X25519 shared secret: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
ret = SSH_OK;
out:
EVP_PKEY_free(pubkey);
EVP_PKEY_CTX_free(pctx);
if (ret == SSH_ERROR) {
return ret;
}
#elif defined(HAVE_MBEDTLS_CURVE25519)
ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
mbedtls_ecdh_init(&ecdh_ctx);
rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(d),
session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE);
#else
rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.d,
session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to read private key: %s", error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(Qp)
.MBEDTLS_PRIVATE(X),
*peer_pubkey_loc,
CURVE25519_PUBKEY_SIZE);
#else
rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.Qp.X,
*peer_pubkey_loc,
CURVE25519_PUBKEY_SIZE);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to read peer public key: %s", error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_mpi_lset(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(Qp)
.MBEDTLS_PRIVATE(Z),
1);
#else
rc = mbedtls_mpi_lset(&ecdh_ctx.Qp.Z, 1);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to set Z coordinate: %s", error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_ecdh_compute_shared(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(grp),
&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(z),
&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(Qp),
&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(d),
mbedtls_ctr_drbg_random,
ctr_drbg);
#else
rc = mbedtls_ecdh_compute_shared(&ecdh_ctx.grp,
&ecdh_ctx.z,
&ecdh_ctx.Qp,
&ecdh_ctx.d,
mbedtls_ctr_drbg_random,
ctr_drbg);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to compute shared secret: %s",
error_buf);
goto out;
}
#if MBEDTLS_VERSION_MAJOR > 2
rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx)
.MBEDTLS_PRIVATE(mbed_ecdh)
.MBEDTLS_PRIVATE(z),
k,
CURVE25519_PUBKEY_SIZE);
#else
rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.z, k, CURVE25519_PUBKEY_SIZE);
#endif
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to write shared secret: %s", error_buf);
goto out;
}
ret = SSH_OK;
out:
mbedtls_ecdh_free(&ecdh_ctx);
if (ret == SSH_ERROR) {
return ret;
}
#elif defined(HAVE_GCRYPT_CURVE25519)
gcry_err = gcry_sexp_build(
&pubkey_sexp,
NULL,
"(key-data(public-key (ecdh (curve Curve25519) (q %b))))",
CURVE25519_PUBKEY_SIZE,
*peer_pubkey_loc);
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create peer public key sexp: %s",
gcry_strerror(gcry_err));
goto out;
}
privkey = ssh_sexp_extract_mpi(session->next_crypto->curve25519_privkey,
"d",
GCRYMPI_FMT_USG,
GCRYMPI_FMT_STD);
if (privkey == NULL) {
SSH_LOG(SSH_LOG_TRACE, "Failed to extract private key");
goto out;
}
gcry_err = gcry_sexp_build(&privkey_data_sexp,
NULL,
"(data(flags raw)(value %b))",
ssh_string_len(privkey),
ssh_string_data(privkey));
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create private key sexp: %s",
gcry_strerror(gcry_err));
goto out;
}
gcry_err = gcry_pk_encrypt(&result_sexp, privkey_data_sexp, pubkey_sexp);
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to compute shared secret: %s",
gcry_strerror(gcry_err));
goto out;
}
shared_secret = ssh_sexp_extract_mpi(result_sexp,
"s",
GCRYMPI_FMT_USG,
GCRYMPI_FMT_USG);
if (shared_secret == NULL) {
SSH_LOG(SSH_LOG_TRACE, "Failed to extract shared secret");
goto out;
}
/* Copy the shared secret to the output buffer */
/* The first byte should be 0x40 indicating that it is a compressed point,
* so we skip it */
shared_secret_data = (char *)ssh_string_data(shared_secret);
if (ssh_string_len(shared_secret) != CURVE25519_PUBKEY_SIZE + 1 ||
shared_secret_data[0] != 0x40) {
SSH_LOG(SSH_LOG_TRACE,
"Invalid shared secret with length: %zu",
ssh_string_len(shared_secret));
goto out;
}
memcpy(k, shared_secret_data + 1, CURVE25519_PUBKEY_SIZE);
ret = SSH_OK;
gcry_sexp_release(session->next_crypto->curve25519_privkey);
session->next_crypto->curve25519_privkey = NULL;
out:
ssh_string_burn(shared_secret);
SSH_STRING_FREE(shared_secret);
ssh_string_burn(privkey);
SSH_STRING_FREE(privkey);
gcry_sexp_release(privkey_data_sexp);
gcry_sexp_release(pubkey_sexp);
gcry_sexp_release(result_sexp);
if (ret == SSH_ERROR) {
return ret;
}
#else
crypto_scalarmult(k,
session->next_crypto->curve25519_privkey,
*peer_pubkey_loc);
#endif /* HAVE_LIBCRYPTO */
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("Session server cookie",
session->next_crypto->server_kex.cookie,
16);
ssh_log_hexdump("Session client cookie",
session->next_crypto->client_kex.cookie,
16);
#endif
return 0;
}
static int ssh_curve25519_build_k(ssh_session session) static int ssh_curve25519_build_k(ssh_session session)
{ {
ssh_curve25519_pubkey k; ssh_curve25519_pubkey k;
@@ -642,7 +111,9 @@ static int ssh_curve25519_build_k(ssh_session session)
return rc; return rc;
} }
bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret); bignum_bin2bn(k,
CURVE25519_PUBKEY_SIZE,
&session->next_crypto->shared_secret);
if (session->next_crypto->shared_secret == NULL) { if (session->next_crypto->shared_secret == NULL) {
return SSH_ERROR; return SSH_ERROR;
} }
@@ -651,98 +122,102 @@ static int ssh_curve25519_build_k(ssh_session session)
ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret); ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret);
#endif #endif
return 0; return SSH_OK;
} }
/** @internal /** @internal
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
* a SSH_MSG_NEWKEYS * a SSH_MSG_NEWKEYS
*/ */
static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply)
ssh_string q_s_string = NULL; {
ssh_string pubkey_blob = NULL; ssh_string q_s_string = NULL;
ssh_string signature = NULL; ssh_string pubkey_blob = NULL;
int rc; ssh_string signature = NULL;
(void)type; int rc;
(void)user; (void)type;
(void)user;
ssh_client_curve25519_remove_callbacks(session); ssh_client_curve25519_remove_callbacks(session);
pubkey_blob = ssh_buffer_get_ssh_string(packet); pubkey_blob = ssh_buffer_get_ssh_string(packet);
if (pubkey_blob == NULL) { if (pubkey_blob == NULL) {
ssh_set_error(session,SSH_FATAL, "No public key in packet"); ssh_set_error(session, SSH_FATAL, "No public key in packet");
goto error; goto error;
} }
rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
SSH_STRING_FREE(pubkey_blob); SSH_STRING_FREE(pubkey_blob);
if (rc != 0) { if (rc != 0) {
ssh_set_error(session, ssh_set_error(session, SSH_FATAL, "Failed to import next public key");
SSH_FATAL, goto error;
"Failed to import next public key"); }
goto error;
}
q_s_string = ssh_buffer_get_ssh_string(packet); q_s_string = ssh_buffer_get_ssh_string(packet);
if (q_s_string == NULL) { if (q_s_string == NULL) {
ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet"); ssh_set_error(session, SSH_FATAL, "No Q_S ECC point in packet");
goto error; goto error;
} }
if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){ if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE) {
ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", ssh_set_error(session,
(int)ssh_string_len(q_s_string)); SSH_FATAL,
SSH_STRING_FREE(q_s_string); "Incorrect size for server Curve25519 public key: %zu",
goto error; ssh_string_len(q_s_string));
} SSH_STRING_FREE(q_s_string);
memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE); goto error;
SSH_STRING_FREE(q_s_string); }
memcpy(session->next_crypto->curve25519_server_pubkey,
ssh_string_data(q_s_string),
CURVE25519_PUBKEY_SIZE);
SSH_STRING_FREE(q_s_string);
signature = ssh_buffer_get_ssh_string(packet); signature = ssh_buffer_get_ssh_string(packet);
if (signature == NULL) { if (signature == NULL) {
ssh_set_error(session, SSH_FATAL, "No signature in packet"); ssh_set_error(session, SSH_FATAL, "No signature in packet");
goto error; goto error;
} }
session->next_crypto->dh_server_signature = signature; session->next_crypto->dh_server_signature = signature;
signature=NULL; /* ownership changed */ signature = NULL; /* ownership changed */
/* TODO: verify signature now instead of waiting for NEWKEYS */ /* TODO: verify signature now instead of waiting for NEWKEYS */
if (ssh_curve25519_build_k(session) < 0) { if (ssh_curve25519_build_k(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot build k number"); ssh_set_error(session, SSH_FATAL, "Cannot build k number");
goto error; goto error;
} }
/* Send the MSG_NEWKEYS */ /* Send the MSG_NEWKEYS */
rc = ssh_packet_send_newkeys(session); rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) { if (rc == SSH_ERROR) {
goto error; goto error;
} }
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED; return SSH_PACKET_USED;
error: error:
session->session_state=SSH_SESSION_STATE_ERROR; session->session_state = SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
#ifdef WITH_SERVER #ifdef WITH_SERVER
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init); static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init);
static ssh_packet_callback dh_server_callbacks[]= { static ssh_packet_callback dh_server_callbacks[] = {
ssh_packet_server_curve25519_init ssh_packet_server_curve25519_init,
}; };
static struct ssh_packet_callbacks_struct ssh_curve25519_server_callbacks = { static struct ssh_packet_callbacks_struct ssh_curve25519_server_callbacks = {
.start = SSH2_MSG_KEX_ECDH_INIT, .start = SSH2_MSG_KEX_ECDH_INIT,
.n_callbacks = 1, .n_callbacks = 1,
.callbacks = dh_server_callbacks, .callbacks = dh_server_callbacks,
.user = NULL .user = NULL,
}; };
/** @internal /** @internal
* @brief sets up the curve25519-sha256@libssh.org kex callbacks * @brief sets up the curve25519-sha256@libssh.org kex callbacks
*/ */
void ssh_server_curve25519_init(ssh_session session){ void ssh_server_curve25519_init(ssh_session session)
{
/* register the packet callbacks */ /* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_curve25519_server_callbacks); ssh_packet_set_callbacks(session, &ssh_curve25519_server_callbacks);
} }
@@ -750,7 +225,8 @@ void ssh_server_curve25519_init(ssh_session session){
/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a /** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a
* SSH_MSG_KEXDH_REPLY * SSH_MSG_KEXDH_REPLY
*/ */
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init)
{
/* ECDH keys */ /* ECDH keys */
ssh_string q_c_string = NULL; ssh_string q_c_string = NULL;
ssh_string q_s_string = NULL; ssh_string q_s_string = NULL;
@@ -769,10 +245,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
/* Extract the client pubkey from the init packet */ /* Extract the client pubkey from the init packet */
q_c_string = ssh_buffer_get_ssh_string(packet); q_c_string = ssh_buffer_get_ssh_string(packet);
if (q_c_string == NULL) { if (q_c_string == NULL) {
ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); ssh_set_error(session, SSH_FATAL, "No Q_C ECC point in packet");
goto error; goto error;
} }
if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){ if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE) {
ssh_set_error(session, ssh_set_error(session,
SSH_FATAL, SSH_FATAL,
"Incorrect size for server Curve25519 public key: %zu", "Incorrect size for server Curve25519 public key: %zu",
@@ -781,7 +257,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
} }
memcpy(session->next_crypto->curve25519_client_pubkey, memcpy(session->next_crypto->curve25519_client_pubkey,
ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE); ssh_string_data(q_c_string),
CURVE25519_PUBKEY_SIZE);
SSH_STRING_FREE(q_c_string); SSH_STRING_FREE(q_c_string);
/* Build server's key pair */ /* Build server's key pair */
@@ -823,8 +300,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
} }
/* add host's public key */ /* add host's public key */
rc = ssh_buffer_add_ssh_string(session->out_buffer, rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_blob);
server_pubkey_blob);
SSH_STRING_FREE(server_pubkey_blob); SSH_STRING_FREE(server_pubkey_blob);
if (rc < 0) { if (rc < 0) {
ssh_set_error_oom(session); ssh_set_error_oom(session);
@@ -885,7 +361,7 @@ error:
SSH_STRING_FREE(q_c_string); SSH_STRING_FREE(q_c_string);
SSH_STRING_FREE(q_s_string); SSH_STRING_FREE(q_s_string);
ssh_buffer_reinit(session->out_buffer); ssh_buffer_reinit(session->out_buffer);
session->session_state=SSH_SESSION_STATE_ERROR; session->session_state = SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }

158
src/curve25519_crypto.c Normal file
View File

@@ -0,0 +1,158 @@
/*
* curve25519_crypto.c - Curve25519 ECDH functions for key exchange (OpenSSL)
*
* This file is part of the SSH Library
*
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
*
* 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/curve25519.h"
#include "libssh/crypto.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include <openssl/err.h>
#include <openssl/evp.h>
int ssh_curve25519_init(ssh_session session)
{
ssh_curve25519_pubkey *pubkey_loc = NULL;
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL;
size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
int rc;
if (session->server) {
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
} else {
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
}
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
if (pctx == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 context: %s",
ERR_error_string(ERR_get_error(), NULL));
return SSH_ERROR;
}
rc = EVP_PKEY_keygen_init(pctx);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 keygen: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_CTX_free(pctx);
return SSH_ERROR;
}
rc = EVP_PKEY_keygen(pctx, &pkey);
EVP_PKEY_CTX_free(pctx);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to generate X25519 keys: %s",
ERR_error_string(ERR_get_error(), NULL));
return SSH_ERROR;
}
rc = EVP_PKEY_get_raw_public_key(pkey, *pubkey_loc, &pubkey_len);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to get X25519 raw public key: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_free(pkey);
return SSH_ERROR;
}
session->next_crypto->curve25519_privkey = pkey;
pkey = NULL;
return SSH_OK;
}
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
{
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
int rc, ret = SSH_ERROR;
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL, *pubkey = NULL;
size_t shared_key_len = CURVE25519_PUBKEY_SIZE;
if (session->server) {
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
} else {
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
}
pkey = session->next_crypto->curve25519_privkey;
if (pkey == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create X25519 EVP_PKEY: %s",
ERR_error_string(ERR_get_error(), NULL));
return SSH_ERROR;
}
pctx = EVP_PKEY_CTX_new(pkey, NULL);
if (pctx == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 context: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
rc = EVP_PKEY_derive_init(pctx);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to initialize X25519 key derivation: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519,
NULL,
*peer_pubkey_loc,
CURVE25519_PUBKEY_SIZE);
if (pubkey == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create X25519 public key EVP_PKEY: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
rc = EVP_PKEY_derive_set_peer(pctx, pubkey);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to set peer X25519 public key: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
rc = EVP_PKEY_derive(pctx, k, &shared_key_len);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to derive X25519 shared secret: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
ret = SSH_OK;
out:
EVP_PKEY_free(pubkey);
EVP_PKEY_CTX_free(pctx);
return ret;
}

73
src/curve25519_fallback.c Normal file
View File

@@ -0,0 +1,73 @@
/*
* curve25519_fallback.c - Curve25519 ECDH functions for key exchange
*
* This file is part of the SSH Library
*
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
*
* 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/curve25519.h"
#include "libssh/crypto.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#ifdef WITH_NACL
#include "nacl/crypto_scalarmult_curve25519.h"
#endif
int ssh_curve25519_init(ssh_session session)
{
ssh_curve25519_pubkey *pubkey_loc = NULL;
int rc;
if (session->server) {
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
} else {
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
}
rc = ssh_get_random(session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE,
1);
if (rc != 1) {
ssh_set_error(session, SSH_FATAL, "PRNG error");
return SSH_ERROR;
}
crypto_scalarmult_base(*pubkey_loc,
session->next_crypto->curve25519_privkey);
return SSH_OK;
}
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
{
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
if (session->server) {
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
} else {
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
}
crypto_scalarmult(k,
session->next_crypto->curve25519_privkey,
*peer_pubkey_loc);
return SSH_OK;
}

199
src/curve25519_gcrypt.c Normal file
View File

@@ -0,0 +1,199 @@
/*
* curve25519_gcrypt.c - Curve25519 ECDH functions for key exchange (Gcrypt)
*
* This file is part of the SSH Library
*
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.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/curve25519.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include <gcrypt.h>
int ssh_curve25519_init(ssh_session session)
{
ssh_curve25519_pubkey *pubkey_loc = NULL;
gcry_error_t gcry_err;
gcry_sexp_t param = NULL, keypair_sexp = NULL;
ssh_string pubkey = NULL;
const char *pubkey_data = NULL;
int ret = SSH_ERROR;
if (session->server) {
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
} else {
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
}
gcry_err =
gcry_sexp_build(&param, NULL, "(genkey (ecdh (curve Curve25519)))");
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create keypair sexp: %s",
gcry_strerror(gcry_err));
goto out;
}
gcry_err = gcry_pk_genkey(&keypair_sexp, param);
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to generate keypair: %s",
gcry_strerror(gcry_err));
goto out;
}
/* Extract the public key */
pubkey = ssh_sexp_extract_mpi(keypair_sexp,
"q",
GCRYMPI_FMT_USG,
GCRYMPI_FMT_STD);
if (pubkey == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to extract public key: %s",
gcry_strerror(gcry_err));
goto out;
}
/* Store the public key in the session */
/* The first byte should be 0x40 indicating that the point is compressed, so
* we skip storing it */
pubkey_data = (char *)ssh_string_data(pubkey);
if (ssh_string_len(pubkey) != CURVE25519_PUBKEY_SIZE + 1 ||
pubkey_data[0] != 0x40) {
SSH_LOG(SSH_LOG_TRACE,
"Invalid public key with length: %zu",
ssh_string_len(pubkey));
goto out;
}
memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE);
/* Store the private key */
session->next_crypto->curve25519_privkey = keypair_sexp;
keypair_sexp = NULL;
ret = SSH_OK;
out:
ssh_string_burn(pubkey);
SSH_STRING_FREE(pubkey);
gcry_sexp_release(param);
gcry_sexp_release(keypair_sexp);
return ret;
}
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
{
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
gcry_error_t gcry_err;
gcry_sexp_t pubkey_sexp = NULL, privkey_data_sexp = NULL,
result_sexp = NULL;
ssh_string shared_secret = NULL, privkey = NULL;
char *shared_secret_data = NULL;
int ret = SSH_ERROR;
if (session->server) {
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
} else {
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
}
gcry_err = gcry_sexp_build(
&pubkey_sexp,
NULL,
"(key-data(public-key (ecdh (curve Curve25519) (q %b))))",
CURVE25519_PUBKEY_SIZE,
*peer_pubkey_loc);
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create peer public key sexp: %s",
gcry_strerror(gcry_err));
goto out;
}
privkey = ssh_sexp_extract_mpi(session->next_crypto->curve25519_privkey,
"d",
GCRYMPI_FMT_USG,
GCRYMPI_FMT_STD);
if (privkey == NULL) {
SSH_LOG(SSH_LOG_TRACE, "Failed to extract private key");
goto out;
}
gcry_err = gcry_sexp_build(&privkey_data_sexp,
NULL,
"(data(flags raw)(value %b))",
ssh_string_len(privkey),
ssh_string_data(privkey));
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create private key sexp: %s",
gcry_strerror(gcry_err));
goto out;
}
gcry_err = gcry_pk_encrypt(&result_sexp, privkey_data_sexp, pubkey_sexp);
if (gcry_err != GPG_ERR_NO_ERROR) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to compute shared secret: %s",
gcry_strerror(gcry_err));
goto out;
}
shared_secret = ssh_sexp_extract_mpi(result_sexp,
"s",
GCRYMPI_FMT_USG,
GCRYMPI_FMT_USG);
if (shared_secret == NULL) {
SSH_LOG(SSH_LOG_TRACE, "Failed to extract shared secret");
goto out;
}
/* Copy the shared secret to the output buffer */
/* The first byte should be 0x40 indicating that it is a compressed point,
* so we skip it */
shared_secret_data = (char *)ssh_string_data(shared_secret);
if (ssh_string_len(shared_secret) != CURVE25519_PUBKEY_SIZE + 1 ||
shared_secret_data[0] != 0x40) {
SSH_LOG(SSH_LOG_TRACE,
"Invalid shared secret with length: %zu",
ssh_string_len(shared_secret));
goto out;
}
memcpy(k, shared_secret_data + 1, CURVE25519_PUBKEY_SIZE);
ret = SSH_OK;
gcry_sexp_release(session->next_crypto->curve25519_privkey);
session->next_crypto->curve25519_privkey = NULL;
out:
ssh_string_burn(shared_secret);
SSH_STRING_FREE(shared_secret);
ssh_string_burn(privkey);
SSH_STRING_FREE(privkey);
gcry_sexp_release(privkey_data_sexp);
gcry_sexp_release(pubkey_sexp);
gcry_sexp_release(result_sexp);
return ret;
}

189
src/curve25519_mbedcrypto.c Normal file
View File

@@ -0,0 +1,189 @@
/*
* curve25519_mbedcrypto.c - Curve25519 ECDH functions for key exchange
* (MbedTLS)
*
* This file is part of the SSH Library
*
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.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/curve25519.h"
#include "libssh/crypto.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "mbedcrypto-compat.h"
#include <mbedtls/ecdh.h>
#include <mbedtls/error.h>
int ssh_curve25519_init(ssh_session session)
{
ssh_curve25519_pubkey *pubkey_loc = NULL;
mbedtls_ecdh_context ecdh_ctx;
mbedtls_ecdh_params *ecdh_params = NULL;
mbedtls_ctr_drbg_context *ctr_drbg = NULL;
int rc, ret = SSH_ERROR;
char error_buf[128];
if (session->server) {
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
} else {
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
}
ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
mbedtls_ecdh_init(&ecdh_ctx);
rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
goto out;
}
ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx);
rc = mbedtls_ecdh_gen_public(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp),
&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Q),
mbedtls_ctr_drbg_random,
ctr_drbg);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to generate X25519 keypair: %s",
error_buf);
goto out;
}
rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to write X25519 private key: %s",
error_buf);
goto out;
}
rc = mbedtls_mpi_write_binary_le(
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Q).MBEDTLS_ECDH_PRIVATE(X),
*pubkey_loc,
CURVE25519_PUBKEY_SIZE);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to write X25519 public key: %s",
error_buf);
goto out;
}
ret = SSH_OK;
out:
mbedtls_ecdh_free(&ecdh_ctx);
return ret;
}
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
{
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
int rc, ret = SSH_ERROR;
mbedtls_ecdh_context ecdh_ctx;
mbedtls_ecdh_params *ecdh_params = NULL;
mbedtls_ctr_drbg_context *ctr_drbg = NULL;
char error_buf[128];
if (session->server) {
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
} else {
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
}
ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
mbedtls_ecdh_init(&ecdh_ctx);
rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
goto out;
}
ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx);
rc = mbedtls_mpi_read_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
session->next_crypto->curve25519_privkey,
CURVE25519_PRIVKEY_SIZE);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to read private key: %s", error_buf);
goto out;
}
rc = mbedtls_mpi_read_binary_le(
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(X),
*peer_pubkey_loc,
CURVE25519_PUBKEY_SIZE);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to read peer public key: %s", error_buf);
goto out;
}
rc = mbedtls_mpi_lset(
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(Z),
1);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to set Z coordinate: %s", error_buf);
goto out;
}
rc = mbedtls_ecdh_compute_shared(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp),
&ecdh_params->MBEDTLS_ECDH_PRIVATE(z),
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp),
&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
mbedtls_ctr_drbg_random,
ctr_drbg);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE,
"Failed to compute shared secret: %s",
error_buf);
goto out;
}
rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(z),
k,
CURVE25519_PUBKEY_SIZE);
if (rc != 0) {
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
SSH_LOG(SSH_LOG_TRACE, "Failed to write shared secret: %s", error_buf);
goto out;
}
ret = SSH_OK;
out:
mbedtls_ecdh_free(&ecdh_ctx);
return ret;
}

View File

@@ -5,13 +5,17 @@
* v3 defines the version inside build_info.h so if it isn't defined * v3 defines the version inside build_info.h so if it isn't defined
* in version.h we should have v3 * in version.h we should have v3
*/ */
#include <mbedtls/version.h>
#include <mbedtls/cipher.h> #include <mbedtls/cipher.h>
#ifdef MBEDTLS_VERSION_MAJOR #include <mbedtls/version.h>
#ifndef MBEDTLS_VERSION_MAJOR
#include <mbedtls/build_info.h>
#endif /* MBEDTLS_VERSION_MAJOR */
#if MBEDTLS_VERSION_MAJOR < 3 #if MBEDTLS_VERSION_MAJOR < 3
static inline size_t mbedtls_cipher_info_get_key_bitlen( static inline size_t
const mbedtls_cipher_info_t *info) mbedtls_cipher_info_get_key_bitlen(const mbedtls_cipher_info_t *info)
{ {
if (info == NULL) { if (info == NULL) {
return 0; return 0;
@@ -19,8 +23,8 @@ static inline size_t mbedtls_cipher_info_get_key_bitlen(
return info->key_bitlen; return info->key_bitlen;
} }
static inline size_t mbedtls_cipher_info_get_iv_size( static inline size_t
const mbedtls_cipher_info_t *info) mbedtls_cipher_info_get_iv_size(const mbedtls_cipher_info_t *info)
{ {
if (info == NULL) { if (info == NULL) {
return 0; return 0;
@@ -29,11 +33,24 @@ static inline size_t mbedtls_cipher_info_get_iv_size(
} }
#define MBEDTLS_PRIVATE(X) X #define MBEDTLS_PRIVATE(X) X
#ifdef HAVE_MBEDTLS_CURVE25519
#include <mbedtls/ecdh.h>
#define MBEDTLS_ECDH_PRIVATE(X) X
#define MBEDTLS_ECDH_PARAMS(X) X
typedef mbedtls_ecdh_context mbedtls_ecdh_params;
#endif /* HAVE_MBEDTLS_CURVE25519 */
#else /* MBEDTLS_VERSION_MAJOR < 3 */
#ifdef HAVE_MBEDTLS_CURVE25519
#include <mbedtls/ecdh.h>
#define MBEDTLS_ECDH_PRIVATE(X) MBEDTLS_PRIVATE(X)
#define MBEDTLS_ECDH_PARAMS(X) X.MBEDTLS_PRIVATE(ctx).MBEDTLS_PRIVATE(mbed_ecdh)
typedef mbedtls_ecdh_context_mbed mbedtls_ecdh_params;
#endif /* HAVE_MBEDTLS_CURVE25519 */
#endif /* MBEDTLS_VERSION_MAJOR < 3 */ #endif /* MBEDTLS_VERSION_MAJOR < 3 */
#else /* MBEDTLS_VERSION_MAJOR */
#include <mbedtls/build_info.h>
#if MBEDTLS_VERSION_MAJOR < 3
#define MBEDTLS_PRIVATE(X) X
#endif /* MBEDTLS_VERSION_MAJOR < 3 */
#endif /* MBEDTLS_VERSION_MAJOR */
#endif /* MBEDCRYPTO_COMPAT_H */ #endif /* MBEDCRYPTO_COMPAT_H */