aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt19
-rw-r--r--src/bignum.c12
-rw-r--r--src/bind.c4
-rw-r--r--src/curve25519.c8
-rw-r--r--src/dh.c29
-rw-r--r--src/ecdh_mbedcrypto.c295
-rw-r--r--src/kex.c12
-rw-r--r--src/known_hosts.c6
-rw-r--r--src/legacy.c6
-rw-r--r--src/libmbedcrypto.c1122
-rw-r--r--src/mbedcrypto_missing.c128
-rw-r--r--src/misc.c8
-rw-r--r--src/options.c14
-rw-r--r--src/pki.c15
-rw-r--r--src/pki_mbedcrypto.c1304
-rw-r--r--src/server.c2
-rw-r--r--src/session.c2
-rw-r--r--src/threads.c32
18 files changed, 3017 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e5a93538..da87313e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -39,6 +39,17 @@ if (OPENSSL_CRYPTO_LIBRARY)
)
endif (OPENSSL_CRYPTO_LIBRARY)
+if (MBEDTLS_CRYPTO_LIBRARY)
+ set(LIBSSH_PRIVATE_INCLUDE_DIRS
+ ${LIBSSH_PRIVATE_INCLUDE_DIRS}
+ ${MBEDTLS_INCLUDE_DIR}
+ )
+ set(LIBSSH_LINK_LIBRARIES
+ ${LIBSSH_LINK_LIBRARIES}
+ ${MBEDTLS_CRYPTO_LIBRARY}
+ )
+endif (MBEDTLS_CRYPTO_LIBRARY)
+
if (GCRYPT_LIBRARY)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
${LIBSSH_PRIVATE_INCLUDE_DIRS}
@@ -160,6 +171,14 @@ if (WITH_GCRYPT)
pki_gcrypt.c
ecdh_gcrypt.c
)
+elseif (WITH_MBEDTLS)
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ libmbedcrypto.c
+ mbedcrypto_missing.c
+ pki_mbedcrypto.c
+ ecdh_mbedcrypto.c
+ )
else (WITH_GCRYPT)
set(libssh_SRCS
${libssh_SRCS}
diff --git a/src/bignum.c b/src/bignum.c
index 346a08a8..064006d4 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -60,6 +60,8 @@ ssh_string ssh_make_bignum_string(bignum num) {
bignum_bn2bin(num, len, ptr->data + pad);
#elif HAVE_LIBCRYPTO
bignum_bn2bin(num, ptr->data + pad);
+#elif HAVE_LIBMBEDCRYPTO
+ bignum_bn2bin(num, ptr->data + pad);
#endif
return ptr;
@@ -78,6 +80,9 @@ bignum ssh_make_string_bn(ssh_string string){
bignum_bin2bn(string->data, len, &bn);
#elif defined HAVE_LIBCRYPTO
bn = bignum_bin2bn(string->data, len, NULL);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bn = bignum_new();
+ bignum_bin2bn(string->data, len, bn);
#endif
return bn;
@@ -91,6 +96,8 @@ void ssh_make_string_bn_inplace(ssh_string string, bignum bnout) {
(void) bnout;
#elif defined HAVE_LIBCRYPTO
bignum_bin2bn(string->data, len, bnout);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_bin2bn(string->data, len, bnout);
#endif
}
@@ -102,6 +109,9 @@ void ssh_print_bignum(const char *which, const bignum num) {
#elif defined HAVE_LIBCRYPTO
char *hex = NULL;
hex = bignum_bn2hex(num);
+#elif defined HAVE_LIBMBEDCRYPTO
+ char *hex = NULL;
+ hex = bignum_bn2hex(num);
#endif
fprintf(stderr, "%s value: ", which);
fprintf(stderr, "%s\n", (hex == NULL) ? "(null)" : (char *) hex);
@@ -109,5 +119,7 @@ void ssh_print_bignum(const char *which, const bignum num) {
SAFE_FREE(hex);
#elif defined HAVE_LIBCRYPTO
OPENSSL_free(hex);
+#elif defined HAVE_LIBMBEDCRYPTO
+ SAFE_FREE(hex);
#endif
}
diff --git a/src/bind.c b/src/bind.c
index fa5f8d57..7a3e8c93 100644
--- a/src/bind.c
+++ b/src/bind.c
@@ -178,6 +178,7 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
}
#endif
+#ifdef HAVE_DSA
if (sshbind->dsa == NULL && sshbind->dsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->dsakey,
NULL,
@@ -199,6 +200,7 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
return SSH_ERROR;
}
}
+#endif
if (sshbind->rsa == NULL && sshbind->rsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->rsakey,
@@ -450,6 +452,7 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
}
}
#endif
+#ifdef HAVE_DSA
if (sshbind->dsa) {
session->srv.dsa_key = ssh_key_dup(sshbind->dsa);
if (session->srv.dsa_key == NULL) {
@@ -457,6 +460,7 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
return SSH_ERROR;
}
}
+#endif
if (sshbind->rsa) {
session->srv.rsa_key = ssh_key_dup(sshbind->rsa);
if (session->srv.rsa_key == NULL) {
diff --git a/src/curve25519.c b/src/curve25519.c
index 77fab2d2..6d9a409c 100644
--- a/src/curve25519.c
+++ b/src/curve25519.c
@@ -78,6 +78,12 @@ static int ssh_curve25519_build_k(ssh_session session) {
if (session->next_crypto->k == NULL) {
return SSH_ERROR;
}
+#elif defined HAVE_LIBMBEDCRYPTO
+ session->next_crypto->k = bignum_new();
+
+ if (session->next_crypto->k == NULL) {
+ return SSH_ERROR;
+ }
#endif
if (session->server)
@@ -91,6 +97,8 @@ static int ssh_curve25519_build_k(ssh_session session) {
bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->k);
#elif defined HAVE_LIBCRYPTO
bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k);
#endif
#ifdef DEBUG_CRYPTO
diff --git a/src/dh.c b/src/dh.c
index 968af8ce..31b8baec 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -143,6 +143,8 @@ int ssh_get_random(void *where, int len, int strong){
return RAND_pseudo_bytes(where,len);
}
# endif /* OPENSSL_VERSION_NUMBER */
+#elif defined HAVE_LIBMBEDCRYPTO
+ return ssh_mbedtls_random(where, len, strong);
#endif
/* never reached */
@@ -162,6 +164,8 @@ int ssh_crypto_init(void) {
gcry_control(GCRYCTL_INIT_SECMEM, 4096);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED,0);
}
+#elif HAVE_LIBMBEDCRYPTO
+ ssh_mbedtls_init();
#endif
g = bignum_new();
@@ -206,7 +210,12 @@ int ssh_crypto_init(void) {
bignum_bin2bn(p_group14_value, P_GROUP14_LEN, p_group14);
OpenSSL_add_all_algorithms();
+#elif defined HAVE_LIBMBEDCRYPTO
+ p_group1 = bignum_new();
+ bignum_bin2bn(p_group1_value, P_GROUP1_LEN, p_group1);
+ p_group14 = bignum_new();
+ bignum_bin2bn(p_group14_value, P_GROUP14_LEN, p_group14);
#endif
ssh_crypto_initialized = 1;
@@ -228,6 +237,8 @@ void ssh_crypto_finalize(void) {
#elif defined HAVE_LIBCRYPTO
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
+#elif defined HAVE_LIBMBEDTLS
+ ssh_mbedtls_cleanup();
#endif
ssh_crypto_initialized=0;
}
@@ -249,6 +260,8 @@ int ssh_dh_generate_x(ssh_session session) {
bignum_rand(session->next_crypto->x, keysize);
#elif defined HAVE_LIBCRYPTO
bignum_rand(session->next_crypto->x, keysize, -1, 0);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_rand(session->next_crypto->x, keysize, -1, 0);
#endif
/* not harder than this */
@@ -276,6 +289,8 @@ int ssh_dh_generate_y(ssh_session session) {
bignum_rand(session->next_crypto->y, keysize);
#elif defined HAVE_LIBCRYPTO
bignum_rand(session->next_crypto->y, keysize, -1, 0);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_rand(session->next_crypto->y, keysize, -1, 0);
#endif
/* not harder than this */
@@ -309,6 +324,9 @@ int ssh_dh_generate_e(ssh_session session) {
#elif defined HAVE_LIBCRYPTO
bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x,
select_p(session->next_crypto->kex_type), ctx);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x,
+ select_p(session->next_crypto->kex_type), NULL);
#endif
#ifdef DEBUG_CRYPTO
@@ -344,6 +362,9 @@ int ssh_dh_generate_f(ssh_session session) {
#elif defined HAVE_LIBCRYPTO
bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y,
select_p(session->next_crypto->kex_type), ctx);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y,
+ select_p(session->next_crypto->kex_type), NULL);
#endif
#ifdef DEBUG_CRYPTO
@@ -430,6 +451,14 @@ int ssh_dh_build_k(ssh_session session) {
bignum_mod_exp(session->next_crypto->k, session->next_crypto->e,
session->next_crypto->y, select_p(session->next_crypto->kex_type), ctx);
}
+#elif defined HAVE_LIBMBEDCRYPTO
+ if (session->client) {
+ bignum_mod_exp(session->next_crypto->k, session->next_crypto->f,
+ session->next_crypto->x, select_p(session->next_crypto->kex_type), NULL);
+ } else {
+ bignum_mod_exp(session->next_crypto->k, session->next_crypto->e,
+ session->next_crypto->y, select_p(session->next_crypto->kex_type), NULL);
+ }
#endif
#ifdef DEBUG_CRYPTO
diff --git a/src/ecdh_mbedcrypto.c b/src/ecdh_mbedcrypto.c
new file mode 100644
index 00000000..d4e7a774
--- /dev/null
+++ b/src/ecdh_mbedcrypto.c
@@ -0,0 +1,295 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * 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, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/session.h"
+#include "libssh/ecdh.h"
+#include "libssh/buffer.h"
+#include "libssh/ssh2.h"
+#include "libssh/dh.h"
+#include "libssh/pki.h"
+#include "libssh/bignum.h"
+#include "libssh/libmbedcrypto.h"
+
+#include <mbedtls/ecdh.h>
+#include <mbedtls/ecp.h>
+
+#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) {
+ return MBEDTLS_ECP_DP_SECP256R1;
+ } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
+ return MBEDTLS_ECP_DP_SECP384R1;
+ } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
+ return MBEDTLS_ECP_DP_SECP521R1;
+ }
+
+ return MBEDTLS_ECP_DP_NONE;
+}
+int ssh_client_ecdh_init(ssh_session session)
+{
+ ssh_string client_pubkey = NULL;
+ mbedtls_ecp_group grp;
+ int rc;
+ mbedtls_ecp_group_id curve;
+
+ curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
+ if (curve == MBEDTLS_ECP_DP_NONE) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
+
+ session->next_crypto->ecdh_privkey = malloc(sizeof(mbedtls_ecp_keypair));
+ if (session->next_crypto->ecdh_privkey == NULL) {
+ return SSH_ERROR;
+ }
+
+ mbedtls_ecp_keypair_init(session->next_crypto->ecdh_privkey);
+ mbedtls_ecp_group_init(&grp);
+
+ rc = mbedtls_ecp_group_load(&grp, curve);
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = mbedtls_ecp_gen_keypair(&grp, &session->next_crypto->ecdh_privkey->d,
+ &session->next_crypto->ecdh_privkey->Q, mbedtls_ctr_drbg_random,
+ &ssh_mbedtls_ctr_drbg);
+
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ client_pubkey = make_ecpoint_string(&grp,
+ &session->next_crypto->ecdh_privkey->Q);
+ if (client_pubkey == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
+ if (rc < 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ session->next_crypto->ecdh_client_pubkey = client_pubkey;
+ client_pubkey = NULL;
+
+ rc = ssh_packet_send(session);
+
+out:
+ mbedtls_ecp_group_free(&grp);
+ ssh_string_free(client_pubkey);
+
+ return rc;
+}
+
+int ecdh_build_k(ssh_session session)
+{
+ mbedtls_ecp_group grp;
+ mbedtls_ecp_point pubkey;
+ int rc;
+ mbedtls_ecp_group_id curve;
+
+ curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
+ if (curve == MBEDTLS_ECP_DP_NONE) {
+ return SSH_ERROR;
+ }
+
+ mbedtls_ecp_group_init(&grp);
+ mbedtls_ecp_point_init(&pubkey);
+
+ rc = mbedtls_ecp_group_load(&grp, curve);
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ if (session->server) {
+ rc = mbedtls_ecp_point_read_binary(&grp, &pubkey,
+ ssh_string_data(session->next_crypto->ecdh_client_pubkey),
+ ssh_string_len(session->next_crypto->ecdh_client_pubkey));
+ } else {
+ rc = mbedtls_ecp_point_read_binary(&grp, &pubkey,
+ ssh_string_data(session->next_crypto->ecdh_server_pubkey),
+ ssh_string_len(session->next_crypto->ecdh_server_pubkey));
+ }
+
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ session->next_crypto->k = malloc(sizeof(mbedtls_mpi));
+ if (session->next_crypto->k == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ mbedtls_mpi_init(session->next_crypto->k);
+
+ rc = mbedtls_ecdh_compute_shared(&grp, session->next_crypto->k, &pubkey,
+ &session->next_crypto->ecdh_privkey->d, mbedtls_ctr_drbg_random,
+ &ssh_mbedtls_ctr_drbg);
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+out:
+ mbedtls_ecp_keypair_free(session->next_crypto->ecdh_privkey);
+ SAFE_FREE(session->next_crypto->ecdh_privkey);
+ mbedtls_ecp_group_free(&grp);
+ mbedtls_ecp_point_free(&pubkey);
+ return rc;
+}
+
+#ifdef WITH_SERVER
+int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet)
+{
+ ssh_string q_c_string = NULL;
+ ssh_string q_s_string = NULL;
+ mbedtls_ecp_group grp;
+ ssh_key privkey = NULL;
+ ssh_string sig_blob = NULL;
+ int rc;
+ mbedtls_ecp_group_id curve;
+
+ curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
+ if (curve == MBEDTLS_ECP_DP_NONE) {
+ return SSH_ERROR;
+ }
+
+ q_c_string = ssh_buffer_get_ssh_string(packet);
+ if (q_c_string == NULL) {
+ ssh_set_error(session, SSH_FATAL, "No Q_C ECC point in packet");
+ return SSH_ERROR;
+ }
+
+ session->next_crypto->ecdh_privkey = malloc(sizeof(mbedtls_ecp_keypair));
+ if (session->next_crypto->ecdh_privkey == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+
+ session->next_crypto->ecdh_client_pubkey = q_c_string;
+
+ mbedtls_ecp_group_init(&grp);
+ mbedtls_ecp_keypair_init(session->next_crypto->ecdh_privkey);
+
+ rc = mbedtls_ecp_group_load(&grp, curve);
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = mbedtls_ecp_gen_keypair(&grp, &session->next_crypto->ecdh_privkey->d,
+ &session->next_crypto->ecdh_privkey->Q, mbedtls_ctr_drbg_random,
+ &ssh_mbedtls_ctr_drbg);
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ q_s_string = make_ecpoint_string(&grp, &session->next_crypto->ecdh_privkey->Q);
+ if (q_s_string == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ session->next_crypto->ecdh_server_pubkey = q_s_string;
+
+ /* build k and session_id */
+ rc = ecdh_build_k(session);
+ if (rc != SSH_OK) {
+ ssh_set_error(session, SSH_FATAL, "Cannot build k number");
+ goto out;
+ }
+
+ /* privkey is not allocated */
+ rc = ssh_get_key_params(session, &privkey);
+ if (rc == SSH_ERROR) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = ssh_make_sessionid(session);
+ if (rc != SSH_OK) {
+ ssh_set_error(session, SSH_FATAL, "Could not create a session id");
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey);
+ if (sig_blob == NULL) {
+ ssh_set_error(session, SSH_FATAL, "Could not sign the session id");
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = ssh_buffer_pack(session->out_buffer, "bSSS",
+ SSH2_MSG_KEXDH_REPLY, session->next_crypto->server_pubkey,
+ q_s_string,
+ sig_blob);
+
+ ssh_string_free(sig_blob);
+
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
+ rc = ssh_packet_send(session);
+ if (rc != SSH_OK) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
+ if (rc < 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+ rc = ssh_packet_send(session);
+ SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
+
+out:
+ mbedtls_ecp_group_free(&grp);
+ return rc;
+}
+
+#endif /* WITH_SERVER */
+#endif
diff --git a/src/kex.c b/src/kex.c
index 21523fa9..911f644e 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -43,6 +43,12 @@
# define DES "3des-cbc"
# define DES_SUPPORTED "3des-cbc,des-cbc-ssh1"
+#elif defined HAVE_LIBMBEDCRYPTO
+# define BLOWFISH "blowfish-cbc,"
+# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
+# define DES "3des-cbc"
+# define DES_SUPPORTED "3des-cbc,des-cbc-ssh1"
+
#elif defined(HAVE_LIBCRYPTO)
# ifdef HAVE_OPENSSL_BLOWFISH_H
@@ -81,7 +87,11 @@
#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
#define HOSTKEYS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss"
#else
+#ifdef HAVE_DSA
#define HOSTKEYS "ssh-ed25519,ssh-rsa,ssh-dss"
+#else
+#define HOSTKEYS "ssh-ed25519,ssh-rsa"
+#endif
#define ECDH ""
#endif
@@ -560,7 +570,9 @@ static char *ssh_client_select_hostkeys(ssh_session session){
"ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp256",
"ssh-rsa",
+#ifdef HAVE_DSA
"ssh-dss",
+#endif
"ssh-rsa1",
NULL
};
diff --git a/src/known_hosts.c b/src/known_hosts.c
index a6bb32f1..2f0584d8 100644
--- a/src/known_hosts.c
+++ b/src/known_hosts.c
@@ -220,7 +220,11 @@ static int check_public_key(ssh_session session, char **tokens) {
for (i = 2; i < 4; i++) { /* e, then n */
tmpbn = NULL;
+#ifdef HAVE_LIBMBEDCRYPTO
+ bignum_dec2bn(tokens[i], tmpbn);
+#else
bignum_dec2bn(tokens[i], &tmpbn);
+#endif
if (tmpbn == NULL) {
ssh_buffer_free(pubkey_buffer);
return -1;
@@ -242,6 +246,8 @@ static int check_public_key(ssh_session session, char **tokens) {
bignum_bn2bin(tmpbn, len, ssh_string_data(tmpstring));
#elif defined HAVE_LIBCRYPTO
bignum_bn2bin(tmpbn, ssh_string_data(tmpstring));
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_bn2bin(tmpbn, ssh_string_data(tmpstring));
#endif
bignum_free(tmpbn);
if (ssh_buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) {
diff --git a/src/legacy.c b/src/legacy.c
index 3f09992c..139f81b5 100644
--- a/src/legacy.c
+++ b/src/legacy.c
@@ -362,6 +362,9 @@ void publickey_free(ssh_public_key key) {
gcry_sexp_release(key->rsa_pub);
#elif defined HAVE_LIBCRYPTO
RSA_free(key->rsa_pub);
+#elif defined HAVE_LIBMBEDCRYPTO
+ mbedtls_pk_free(key->rsa_pub);
+ SAFE_FREE(key->rsa_pub);
#endif
break;
default:
@@ -463,6 +466,9 @@ void privatekey_free(ssh_private_key prv) {
#elif defined HAVE_LIBCRYPTO
DSA_free(prv->dsa_priv);
RSA_free(prv->rsa_priv);
+#elif defined HAVE_LIBMBEDCRYPTO
+ mbedtls_pk_free(prv->rsa_priv);
+ SAFE_FREE(prv->rsa_priv);
#endif
memset(prv, 0, sizeof(struct ssh_private_key_struct));
SAFE_FREE(prv);
diff --git a/src/libmbedcrypto.c b/src/libmbedcrypto.c
new file mode 100644
index 00000000..6854bd98
--- /dev/null
+++ b/src/libmbedcrypto.c
@@ -0,0 +1,1122 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * 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, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/wrapper.h"
+#include "libssh/crypto.h"
+#include "libssh/priv.h"
+
+#ifdef HAVE_LIBMBEDCRYPTO
+#include <mbedtls/md.h>
+
+struct ssh_mac_ctx_struct {
+ enum ssh_mac_e mac_type;
+ mbedtls_md_context_t ctx;
+};
+
+void ssh_reseed(void)
+{
+ mbedtls_ctr_drbg_reseed(&ssh_mbedtls_ctr_drbg, NULL, 0);
+}
+
+SHACTX sha1_init(void)
+{
+ SHACTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void sha1_update(SHACTX c, const void *data, unsigned long len)
+{
+ mbedtls_md_update(c, data, len);
+}
+
+void sha1_final(unsigned char *md, SHACTX c)
+{
+ mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+void sha1(unsigned char *digest, int len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ if (md_info != NULL) {
+ mbedtls_md(md_info, digest, len, hash);
+ }
+}
+
+static mbedtls_md_type_t nid_to_md_algo(int nid)
+{
+ switch (nid) {
+ case NID_mbedtls_nistp256:
+ return MBEDTLS_MD_SHA256;
+ case NID_mbedtls_nistp384:
+ return MBEDTLS_MD_SHA384;
+ case NID_mbedtls_nistp521:
+ return MBEDTLS_MD_SHA512;
+ }
+ return MBEDTLS_MD_NONE;
+}
+
+void evp(int nid, unsigned char *digest, int len,
+ unsigned char *hash, unsigned int *hlen)
+{
+ mbedtls_md_type_t algo = nid_to_md_algo(nid);
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(algo);
+
+
+ if (md_info != NULL) {
+ *hlen = mbedtls_md_get_size(md_info);
+ mbedtls_md(md_info, digest, len, hash);
+ }
+}
+
+EVPCTX evp_init(int nid)
+{
+ EVPCTX ctx = NULL;
+ int rc;
+ mbedtls_md_type_t algo = nid_to_md_algo(nid);
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(algo);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void evp_update(EVPCTX ctx, const void *data, unsigned long len)
+{
+ mbedtls_md_update(ctx, data, len);
+}
+
+void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
+{
+ *mdlen = mbedtls_md_get_size(ctx->md_info);
+ mbedtls_md_hmac_finish(ctx, md);
+ mbedtls_md_free(ctx);
+ SAFE_FREE(ctx);
+}
+
+SHA256CTX sha256_init(void)
+{
+ SHA256CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if(ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void sha256_update(SHA256CTX c, const void *data, unsigned long len)
+{
+ mbedtls_md_update(c, data, len);
+}
+
+void sha256_final(unsigned char *md, SHA256CTX c)
+{
+ mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+void sha256(unsigned char *digest, int len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+ if (md_info != NULL) {
+ mbedtls_md(md_info, digest, len, hash);
+ }
+}
+
+SHA384CTX sha384_init(void)
+{
+ SHA384CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void sha384_update(SHA384CTX c, const void *data, unsigned long len)
+{
+ mbedtls_md_update(c, data, len);
+}
+
+void sha384_final(unsigned char *md, SHA384CTX c)
+{
+ mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+void sha384(unsigned char *digest, int len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+ if (md_info != NULL) {
+ mbedtls_md(md_info, digest, len, hash);
+ }
+}
+
+SHA512CTX sha512_init(void)
+{
+ SHA512CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void sha512_update(SHA512CTX c, const void *data, unsigned long len)
+{
+ mbedtls_md_update(c, data, len);
+}
+
+void sha512_final(unsigned char *md, SHA512CTX c)
+{
+ mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+void sha512(unsigned char *digest, int len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ if (md_info != NULL) {
+ mbedtls_md(md_info, digest, len, hash);
+ }
+}
+
+MD5CTX md5_init(void)
+{
+ MD5CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+void md5_update(MD5CTX c, const void *data, unsigned long len) {
+ mbedtls_md_update(c, data, len);
+}
+
+void md5_final(unsigned char *md, MD5CTX c)
+{
+ mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type)
+{
+ ssh_mac_ctx ctx = malloc(sizeof (struct ssh_mac_ctx_struct));
+ const mbedtls_md_info_t *md_info;
+ int rc;
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->mac_type=type;
+ switch(type) {
+ case SSH_MAC_SHA1:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ break;
+ case SSH_MAC_SHA256:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+ break;
+ case SSH_MAC_SHA384:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+ break;
+ case SSH_MAC_SHA512:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ break;
+ default:
+ goto error;
+ }
+
+ if (md_info == NULL) {
+ goto error;
+ }
+
+ mbedtls_md_init(&ctx->ctx);
+
+ rc = mbedtls_md_setup(&ctx->ctx, md_info, 0);
+ if (rc != 0) {
+ goto error;
+ }
+
+ rc = mbedtls_md_starts(&ctx->ctx);
+ if (rc != 0) {
+ goto error;
+ }
+
+ return ctx;
+
+error:
+ SAFE_FREE(ctx);
+ return NULL;
+}
+
+void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len)
+{
+ mbedtls_md_update(&ctx->ctx, data, len);
+}
+
+void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx)
+{
+ mbedtls_md_finish(&ctx->ctx, md);
+ mbedtls_md_free(&ctx->ctx);
+ SAFE_FREE(ctx);
+}
+
+HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type)
+{
+ HMACCTX ctx = NULL;
+ const mbedtls_md_info_t *md_info = NULL;
+ int rc;
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ switch (type) {
+ case SSH_HMAC_SHA1:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ break;
+ case SSH_HMAC_SHA256:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+ break;
+ case SSH_HMAC_SHA384:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+ break;
+ case SSH_HMAC_SHA512:
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ break;
+ default:
+ goto error;
+ }
+
+ mbedtls_md_init(ctx);
+
+ if (md_info == NULL) {
+ goto error;
+ }
+
+ rc = mbedtls_md_setup(ctx, md_info, 1);
+ if (rc != 0) {
+ goto error;
+ }
+
+ rc = mbedtls_md_hmac_starts(ctx, key, len);
+ if (rc != 0) {
+ goto error;
+ }
+
+ return ctx;
+
+error:
+ mbedtls_md_free(ctx);
+ SAFE_FREE(ctx);
+ return NULL;
+}
+
+void hmac_update(HMACCTX c, const void *data, unsigned long len)
+{
+ mbedtls_md_hmac_update(c, data, len);
+}
+
+void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len)
+{
+ *len = mbedtls_md_get_size(c->md_info);
+ mbedtls_md_hmac_finish(c, hashmacbuf);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+static int cipher_set_encrypt_key(struct ssh_cipher_struct *cipher, void *key,
+ void *IV)
+{
+
+ const mbedtls_cipher_info_t *cipher_info = NULL;
+ int rc;
+
+ mbedtls_cipher_init(&cipher->encrypt_ctx);
+ cipher_info = mbedtls_cipher_info_from_type(cipher->type);
+
+ rc = mbedtls_cipher_setup(&cipher->encrypt_ctx, cipher_info);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setup failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_setkey(&cipher->encrypt_ctx, key,
+ cipher_info->key_bitlen,
+ MBEDTLS_ENCRYPT);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_set_iv(&cipher->encrypt_ctx, IV, cipher_info->iv_size);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_reset(&cipher->encrypt_ctx);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ goto error;
+ }
+
+ return SSH_OK;
+error:
+ mbedtls_cipher_free(&cipher->encrypt_ctx);
+ return SSH_ERROR;
+}
+
+static int cipher_set_encrypt_key_cbc(struct ssh_cipher_struct *cipher, void *key,
+ void *IV)
+{
+
+ const mbedtls_cipher_info_t *cipher_info = NULL;
+ int rc;
+
+ mbedtls_cipher_init(&cipher->encrypt_ctx);
+ cipher_info = mbedtls_cipher_info_from_type(cipher->type);
+
+ rc = mbedtls_cipher_setup(&cipher->encrypt_ctx, cipher_info);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setup failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_setkey(&cipher->encrypt_ctx, key,
+ cipher_info->key_bitlen,
+ MBEDTLS_ENCRYPT);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_set_iv(&cipher->encrypt_ctx, IV, cipher_info->iv_size);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ goto error;
+ }
+
+ /* libssh only encypts and decrypts packets that are multiples of a block
+ * size, and no padding is used */
+ rc = mbedtls_cipher_set_padding_mode(&cipher->encrypt_ctx,
+ MBEDTLS_PADDING_NONE);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_padding_mode failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_reset(&cipher->encrypt_ctx);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ goto error;
+ }
+
+ return SSH_OK;
+error:
+ mbedtls_cipher_free(&cipher->encrypt_ctx);
+ return SSH_ERROR;
+}
+
+static int cipher_set_decrypt_key(struct ssh_cipher_struct *cipher, void *key,
+ void *IV)
+{
+ const mbedtls_cipher_info_t *cipher_info = NULL;
+ int rc;
+
+ mbedtls_cipher_init(&cipher->decrypt_ctx);
+ cipher_info = mbedtls_cipher_info_from_type(cipher->type);
+
+ rc = mbedtls_cipher_setup(&cipher->decrypt_ctx, cipher_info);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_setkey(&cipher->decrypt_ctx, key,
+ cipher_info->key_bitlen,
+ MBEDTLS_DECRYPT);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_set_iv(&cipher->decrypt_ctx, IV, cipher_info->iv_size);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ goto error;
+ }
+
+ mbedtls_cipher_reset(&cipher->decrypt_ctx);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ goto error;
+ }
+
+ return SSH_OK;
+error:
+ mbedtls_cipher_free(&cipher->decrypt_ctx);
+ return SSH_ERROR;
+}
+
+static int cipher_set_decrypt_key_cbc(struct ssh_cipher_struct *cipher, void *key,
+ void *IV)
+{
+ const mbedtls_cipher_info_t *cipher_info;
+ int rc;
+
+ mbedtls_cipher_init(&cipher->decrypt_ctx);
+ cipher_info = mbedtls_cipher_info_from_type(cipher->type);
+
+ rc = mbedtls_cipher_setup(&cipher->decrypt_ctx, cipher_info);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_setkey(&cipher->decrypt_ctx, key,
+ cipher_info->key_bitlen,
+ MBEDTLS_DECRYPT);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_set_iv(&cipher->decrypt_ctx, IV, cipher_info->iv_size);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_set_padding_mode(&cipher->decrypt_ctx,
+ MBEDTLS_PADDING_NONE);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_padding_mode failed");
+ goto error;
+ }
+
+ mbedtls_cipher_reset(&cipher->decrypt_ctx);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ goto error;
+ }
+
+ return SSH_OK;
+error:
+ mbedtls_cipher_free(&cipher->decrypt_ctx);
+ return SSH_ERROR;
+}
+
+static void cipher_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
+ unsigned long len)
+{
+ size_t outlen = 0;
+ size_t total_len = 0;
+ int rc = 0;
+ rc = mbedtls_cipher_update(&cipher->encrypt_ctx, in, len, out, &outlen);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during encryption");
+ return;
+ }
+
+ total_len += outlen;
+
+ if (total_len == len) {
+ return;
+ }
+
+ rc = mbedtls_cipher_finish(&cipher->encrypt_ctx, (unsigned char *) out + outlen,
+ &outlen);
+
+ total_len += outlen;
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_finish failed during encryption");
+ return;
+ }
+
+ if (total_len != len) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ outlen, len);
+ return;
+ }
+
+}
+
+static void cipher_encrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void *out,
+ unsigned long len)
+{
+ size_t outlen = 0;
+ int rc = 0;
+ rc = mbedtls_cipher_update(&cipher->encrypt_ctx, in, len, out, &outlen);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during encryption");
+ return;
+ }
+
+ if (outlen != len) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ outlen, len);
+ return;
+ }
+
+}
+
+static void cipher_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
+ unsigned long len)
+{
+ size_t outlen = 0;
+ int rc = 0;
+ size_t total_len = 0;
+
+ rc = mbedtls_cipher_update(&cipher->decrypt_ctx, in, len, out, &outlen);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during decryption");
+ return;
+ }
+
+ total_len += outlen;
+
+ if (total_len == len) {
+ return;
+ }
+
+ rc = mbedtls_cipher_finish(&cipher->decrypt_ctx, (unsigned char *) out +
+ outlen, &outlen);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed during decryption");
+ return;
+ }
+
+ total_len += outlen;
+
+ if (total_len != len) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ outlen, len);
+ return;
+ }
+
+}
+
+static void cipher_decrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void *out,
+ unsigned long len)
+{
+ size_t outlen = 0;
+ int rc = 0;
+ rc = mbedtls_cipher_update(&cipher->decrypt_ctx, in, len, out, &outlen);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during decryption");
+ return;
+ }
+
+ /* MbedTLS caches the last block when decrypting with cbc.
+ * By calling finish the block is flushed to out, however the unprocessed
+ * data counter is not reset.
+ * Calling mbedtls_cipher_reset resets the unprocessed data counter.
+ */
+ if (outlen == 0) {
+ rc = mbedtls_cipher_finish(&cipher->decrypt_ctx, out, &outlen);
+ } else if (outlen == len) {
+ return;
+ } else {
+ rc = mbedtls_cipher_finish(&cipher->decrypt_ctx, (unsigned char *) out +
+ outlen , &outlen);
+ }
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_finish failed during decryption");
+ return;
+ }
+
+ rc = mbedtls_cipher_reset(&cipher->decrypt_ctx);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed during decryption");
+ return;
+ }
+
+ if (outlen != len) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ outlen, len);
+ return;
+ }
+
+}
+
+static void cipher_cleanup(struct ssh_cipher_struct *cipher)
+{
+ mbedtls_cipher_free(&cipher->encrypt_ctx);
+ mbedtls_cipher_free(&cipher->decrypt_ctx);
+}
+
+static int des3_set_encrypt_key(struct ssh_cipher_struct *cipher, void *key,
+ void *IV)
+{
+ const mbedtls_cipher_info_t *cipher_info = NULL;
+ unsigned char *des3_key = NULL;
+ size_t des_key_size = 0;
+ int rc;
+
+ mbedtls_cipher_init(&cipher->encrypt_ctx);
+ cipher_info = mbedtls_cipher_info_from_type(cipher->type);
+
+ rc = mbedtls_cipher_setup(&cipher->encrypt_ctx, cipher_info);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setup failed");
+ goto error;
+ }
+
+ des3_key = malloc(cipher_info->key_bitlen / 8);
+ if (des3_key == NULL) {
+ SSH_LOG(SSH_LOG_WARNING, "error allocating memory for key");
+ goto error;
+ }
+
+ des_key_size = cipher_info->key_bitlen / (8 * 3);
+ memcpy(des3_key, key, des_key_size);
+ memcpy(des3_key + des_key_size, (unsigned char * )key + des_key_size,
+ des_key_size);
+ memcpy(des3_key + 2 * des_key_size,
+ (unsigned char *) key + 2 * des_key_size, des_key_size);
+
+ rc = mbedtls_cipher_setkey(&cipher->encrypt_ctx, des3_key,
+ cipher_info->key_bitlen,
+ MBEDTLS_ENCRYPT);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_set_iv(&cipher->encrypt_ctx, IV, cipher_info->iv_size);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_reset(&cipher->encrypt_ctx);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ goto error;
+ }
+
+ SAFE_FREE(des3_key);
+ return SSH_OK;
+error:
+ mbedtls_cipher_free(&cipher->encrypt_ctx);
+ SAFE_FREE(des3_key);
+ return SSH_ERROR;
+}
+
+static int des3_set_decrypt_key(struct ssh_cipher_struct *cipher, void *key,
+ void *IV)
+{
+ const mbedtls_cipher_info_t *cipher_info = NULL;
+ unsigned char *des3_key = NULL;
+ size_t des_key_size = 0;
+ int rc;
+
+ mbedtls_cipher_init(&cipher->decrypt_ctx);
+ cipher_info = mbedtls_cipher_info_from_type(cipher->type);
+
+ rc = mbedtls_cipher_setup(&cipher->decrypt_ctx, cipher_info);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setup failed");
+ goto error;
+ }
+
+ des3_key = malloc(cipher_info->key_bitlen / 8);
+ if (des3_key == NULL) {
+ SSH_LOG(SSH_LOG_WARNING, "error allocating memory for key");
+ goto error;
+ }
+
+ des_key_size = cipher_info->key_bitlen / (8 * 3);
+ memcpy(des3_key, key, des_key_size);
+ memcpy(des3_key + des_key_size, (unsigned char *) key + des_key_size,
+ des_key_size);
+ memcpy(des3_key + 2 * des_key_size,
+ (unsigned char *) key + 2 * des_key_size,
+ des_key_size);
+
+ rc = mbedtls_cipher_setkey(&cipher->decrypt_ctx, des3_key,
+ cipher_info->key_bitlen,
+ MBEDTLS_DECRYPT);
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_set_iv(&cipher->decrypt_ctx, IV, cipher_info->iv_size);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ goto error;
+ }
+
+ rc = mbedtls_cipher_reset(&cipher->decrypt_ctx);
+
+ if (rc != 0) {
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ goto error;
+ }
+
+ SAFE_FREE(des3_key);
+ return SSH_OK;
+error:
+ mbedtls_cipher_free(&cipher->decrypt_ctx);
+ if (des3_key != NULL) {
+ SAFE_FREE(des3_key);
+ }
+ return SSH_ERROR;
+}
+
+static struct ssh_cipher_struct ssh_ciphertab[] = {
+ {
+ .name = "blowfish-cbc",
+ .blocksize = 8,
+ .keysize = 128,
+ .type = MBEDTLS_CIPHER_BLOWFISH_CBC,
+ .set_encrypt_key = cipher_set_encrypt_key_cbc,
+ .set_decrypt_key = cipher_set_decrypt_key_cbc,
+ .encrypt = cipher_encrypt_cbc,
+ .decrypt = cipher_decrypt_cbc,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "aes128-ctr",
+ .blocksize = 16,
+ .keysize = 128,
+ .type = MBEDTLS_CIPHER_AES_128_CTR,
+ .set_encrypt_key = cipher_set_encrypt_key,
+ .set_decrypt_key = cipher_set_decrypt_key,
+ .encrypt = cipher_encrypt,
+ .decrypt = cipher_decrypt,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "aes192-ctr",
+ .blocksize = 16,
+ .keysize = 192,
+ .type = MBEDTLS_CIPHER_AES_192_CTR,
+ .set_encrypt_key = cipher_set_encrypt_key,
+ .set_decrypt_key = cipher_set_decrypt_key,
+ .encrypt = cipher_encrypt,
+ .decrypt = cipher_decrypt,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "aes256-ctr",
+ .blocksize = 16,
+ .keysize = 256,
+ .type = MBEDTLS_CIPHER_AES_256_CTR,
+ .set_encrypt_key = cipher_set_encrypt_key,
+ .set_decrypt_key = cipher_set_decrypt_key,
+ .encrypt = cipher_encrypt,
+ .decrypt = cipher_decrypt,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "aes128-cbc",
+ .blocksize = 16,
+ .keysize = 128,
+ .type = MBEDTLS_CIPHER_AES_128_CBC,
+ .set_encrypt_key = cipher_set_encrypt_key_cbc,
+ .set_decrypt_key = cipher_set_decrypt_key_cbc,
+ .encrypt = cipher_encrypt_cbc,
+ .decrypt = cipher_decrypt_cbc,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "aes192-cbc",
+ .blocksize = 16,
+ .keysize = 192,
+ .type = MBEDTLS_CIPHER_AES_192_CBC,
+ .set_encrypt_key = cipher_set_encrypt_key_cbc,
+ .set_decrypt_key = cipher_set_decrypt_key_cbc,
+ .encrypt = cipher_encrypt_cbc,
+ .decrypt = cipher_decrypt_cbc,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "aes256-cbc",
+ .blocksize = 16,
+ .keysize = 256,
+ .type = MBEDTLS_CIPHER_AES_256_CBC,
+ .set_encrypt_key = cipher_set_encrypt_key_cbc,
+ .set_decrypt_key = cipher_set_decrypt_key_cbc,
+ .encrypt = cipher_encrypt_cbc,
+ .decrypt = cipher_decrypt_cbc,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "3des-cbc",
+ .blocksize = 8,
+ .keysize = 192,
+ .type = MBEDTLS_CIPHER_DES_EDE3_CBC,
+ .set_encrypt_key = cipher_set_encrypt_key_cbc,
+ .set_decrypt_key = cipher_set_decrypt_key_cbc,
+ .encrypt = cipher_encrypt_cbc,
+ .decrypt = cipher_decrypt_cbc,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "3des-cbc-ssh1",
+ .blocksize = 8,
+ .keysize = 192,
+ .type = MBEDTLS_CIPHER_DES_CBC,
+ .set_encrypt_key = des3_set_encrypt_key,
+ .set_decrypt_key = des3_set_decrypt_key,
+ .encrypt = cipher_encrypt_cbc,
+ .decrypt = cipher_decrypt_cbc,
+ .cleanup = cipher_cleanup
+ },
+ {
+ .name = "des-cbc-ssh1",
+ .blocksize = 8,
+ .keysize = 64,
+ .type = MBEDTLS_CIPHER_DES_CBC,
+ .set_encrypt_key = cipher_set_encrypt_key_cbc,
+ .set_decrypt_key = cipher_set_decrypt_key_cbc,
+ .encrypt = cipher_encrypt_cbc,
+ .decrypt = cipher_decrypt_cbc,
+ },
+ {
+ .name = NULL,
+ .blocksize = 0,
+ .keysize = 0,
+ .set_encrypt_key = NULL,
+ .set_decrypt_key = NULL,
+ .encrypt = NULL,
+ .decrypt = NULL,
+ .cleanup = NULL
+ }
+};
+
+struct ssh_cipher_struct *ssh_get_ciphertab(void)
+{
+ return ssh_ciphertab;
+}
+
+void ssh_mbedtls_init(void)
+{
+ int rc;
+
+ mbedtls_entropy_init(&ssh_mbedtls_entropy);
+ mbedtls_ctr_drbg_init(&ssh_mbedtls_ctr_drbg);
+
+ rc = mbedtls_ctr_drbg_seed(&ssh_mbedtls_ctr_drbg, mbedtls_entropy_func,
+ &ssh_mbedtls_entropy, NULL, 0);
+ if (rc != 0) {
+ mbedtls_ctr_drbg_free(&ssh_mbedtls_ctr_drbg);
+ }
+}
+
+int ssh_mbedtls_random(void *where, int len, int strong)
+{
+ int rc = 0;
+ if (strong) {
+ mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
+ MBEDTLS_CTR_DRBG_PR_ON);
+ rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
+ mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
+ MBEDTLS_CTR_DRBG_PR_OFF);
+ } else {
+ rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
+ }
+
+ return !rc;
+}
+
+void ssh_mbedtls_cleanup(void)
+{
+ mbedtls_ctr_drbg_free(&ssh_mbedtls_ctr_drbg);
+ mbedtls_entropy_free(&ssh_mbedtls_entropy);
+}
+
+#endif /* HAVE_LIBMBEDCRYPTO */
diff --git a/src/mbedcrypto_missing.c b/src/mbedcrypto_missing.c
new file mode 100644
index 00000000..44ac7ddc
--- /dev/null
+++ b/src/mbedcrypto_missing.c
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * 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, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/priv.h"
+#include "libssh/libmbedcrypto.h"
+
+#ifdef HAVE_LIBMBEDCRYPTO
+bignum ssh_mbedcry_bn_new(void)
+{
+ bignum bn;
+
+ bn = malloc(sizeof(mbedtls_mpi));
+ if (bn) {
+ mbedtls_mpi_init(bn);
+ }
+
+ return bn;
+}
+
+void ssh_mbedcry_bn_free(bignum bn)
+{
+ mbedtls_mpi_free(bn);
+ SAFE_FREE(bn);
+}
+
+char *ssh_mbedcry_bn2num(bignum num, int radix)
+{
+ char *buf = NULL;
+ size_t olen;
+ int rc;
+
+ rc = mbedtls_mpi_write_string(num, radix, buf, 0, &olen);
+ if (rc != 0) {
+ return NULL;
+ }
+
+ buf = malloc(olen);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ rc = mbedtls_mpi_write_string(num, radix, buf, olen, &olen);
+ if (rc != 0) {
+ SAFE_FREE(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+int ssh_mbedcry_rand(bignum rnd, int bits, int top, int bottom)
+{
+ size_t len;
+ int rc;
+ int i;
+
+ if (bits <= 0) {
+ return 0;
+ }
+
+ len = bits / 8 + 1;
+ rc = mbedtls_mpi_fill_random(rnd, len, mbedtls_ctr_drbg_random,
+ &ssh_mbedtls_ctr_drbg);
+ if (rc != 0) {
+ return 0;
+ }
+
+ for (i = len * 8 - 1; i >= bits; i--) {
+ rc = mbedtls_mpi_set_bit(rnd, i, 0);
+ if (rc != 0) {
+ return 0;
+ }
+ }
+
+ if (top == 0) {
+ rc = mbedtls_mpi_set_bit(rnd, bits - 1, 0);
+ }
+
+ if (top == 1) {
+ if (bits < 2) {
+ return 0;
+ }
+
+ rc = mbedtls_mpi_set_bit(rnd, bits - 2, 0);
+ if (rc != 0) {
+ return 0;
+ }
+ }
+
+ if (bottom) {
+ rc = mbedtls_mpi_set_bit(rnd, 0, 1);
+ if (rc != 0) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int ssh_mbedcry_is_bit_set(bignum num, size_t pos)
+{
+ int bit;
+ bit = mbedtls_mpi_get_bit(num, pos);
+ return bit;
+}
+#endif
diff --git a/src/misc.c b/src/misc.c
index fad33294..9ef31184 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -82,6 +82,12 @@
#define CRYPTO_STRING ""
#endif
+#ifdef HAVE_LIBMBEDCRYPTO
+#define MBED_STRING "/mbedtls"
+#else
+#define MBED_STRING ""
+#endif
+
#ifdef WITH_ZLIB
#define ZLIB_STRING "/zlib"
#else
@@ -349,7 +355,7 @@ char *ssh_hostport(const char *host, int port){
*/
const char *ssh_version(int req_version) {
if (req_version <= LIBSSH_VERSION_INT) {
- return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING
+ return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING MBED_STRING
ZLIB_STRING;
}
diff --git a/src/options.c b/src/options.c
index d2128c13..14b9e01e 100644
--- a/src/options.c
+++ b/src/options.c
@@ -1493,8 +1493,15 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
key_type = ssh_key_type(key);
switch (key_type) {
case SSH_KEYTYPE_DSS:
+#ifdef HAVE_DSA
bind_key_loc = &sshbind->dsa;
bind_key_path_loc = &sshbind->dsakey;
+#else
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "DSS key used and libssh compiled "
+ "without DSA support");
+#endif
break;
case SSH_KEYTYPE_ECDSA:
#ifdef HAVE_ECC
@@ -1550,7 +1557,14 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
key_type = ssh_key_type(key);
switch (key_type) {
case SSH_KEYTYPE_DSS:
+#ifdef HAVE_DSA
bind_key_loc = &sshbind->dsa;
+#else
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "DSA key used and libssh compiled "
+ "without DSA support");
+#endif
break;
case SSH_KEYTYPE_ECDSA:
#ifdef HAVE_ECC
diff --git a/src/pki.c b/src/pki.c
index 91f972b1..c362ae24 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -138,6 +138,16 @@ void ssh_key_clean (ssh_key key){
#ifdef HAVE_OPENSSL_ECC
if(key->ecdsa) EC_KEY_free(key->ecdsa);
#endif /* HAVE_OPENSSL_ECC */
+#elif defined HAVE_LIBMBEDCRYPTO
+ if (key->rsa != NULL) {
+ mbedtls_pk_free(key->rsa);
+ SAFE_FREE(key->rsa);
+ }
+
+ if (key->ecdsa != NULL) {
+ mbedtls_ecdsa_free(key->ecdsa);
+ SAFE_FREE(key->ecdsa);
+ }
#endif
if (key->ed25519_privkey != NULL){
BURN_BUFFER(key->ed25519_privkey, sizeof(ed25519_privkey));
@@ -354,6 +364,8 @@ void ssh_signature_free(ssh_signature sig)
gcry_sexp_release(sig->rsa_sig);
#elif defined HAVE_LIBCRYPTO
SAFE_FREE(sig->rsa_sig);
+#elif defined HAVE_LIBMBEDCRYPTO
+ SAFE_FREE(sig->rsa_sig);
#endif
break;
case SSH_KEYTYPE_ECDSA:
@@ -361,6 +373,9 @@ void ssh_signature_free(ssh_signature sig)
gcry_sexp_release(sig->ecdsa_sig);
#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_ECC)
ECDSA_SIG_free(sig->ecdsa_sig);
+#elif defined HAVE_LIBMBEDCRYPTO
+ bignum_free(sig->ecdsa_sig.r);
+ bignum_free(sig->ecdsa_sig.s);
#endif
break;
case SSH_KEYTYPE_ED25519:
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
new file mode 100644
index 00000000..c3508540
--- /dev/null
+++ b/src/pki_mbedcrypto.c
@@ -0,0 +1,1304 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * 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, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_LIBMBEDCRYPTO
+#include <mbedtls/pk.h>
+#include <mbedtls/error.h>
+
+#include "libssh/priv.h"
+#include "libssh/pki.h"
+#include "libssh/pki_priv.h"
+#include "libssh/buffer.h"
+#include "libssh/bignum.h"
+
+#define MAX_PASSPHRASE_SIZE 1024
+#define MAX_KEY_SIZE 32
+
+ssh_string pki_private_key_to_pem(const ssh_key key, const char *passphrase,
+ ssh_auth_callback auth_fn, void *auth_data)
+{
+ (void) key;
+ (void) passphrase;
+ (void) auth_fn;
+ (void) auth_data; return NULL;
+}
+
+static int pki_key_ecdsa_to_nid(mbedtls_ecdsa_context *ecdsa)
+{
+ mbedtls_ecp_group_id id;
+
+ id = ecdsa->grp.id;
+ if (id == MBEDTLS_ECP_DP_SECP256R1) {
+ return NID_mbedtls_nistp256;
+ } else if (id == MBEDTLS_ECP_DP_SECP384R1) {
+ return NID_mbedtls_nistp384;
+ } else if (id == MBEDTLS_ECP_DP_SECP521R1) {
+ return NID_mbedtls_nistp521;
+ }
+
+ return -1;
+}
+
+ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase,
+ ssh_auth_callback auth_fn, void *auth_data)
+{
+ ssh_key key = NULL;
+ mbedtls_pk_context *rsa = NULL;
+ mbedtls_pk_context *ecdsa = NULL;
+ ed25519_privkey *ed25519 = NULL;
+ enum ssh_keytypes_e type;
+ int valid;
+ /* mbedtls pk_parse_key expects strlen to count the 0 byte */
+ size_t b64len = strlen(b64_key) + 1;
+ unsigned char tmp[MAX_PASSPHRASE_SIZE] = {0};
+
+ if (ssh_init() < 0) {
+ return NULL;
+ }
+
+ type = pki_privatekey_type_from_string(b64_key);
+ if (type == SSH_KEYTYPE_UNKNOWN) {
+ SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key.");
+ return NULL;
+ }
+
+ switch (type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ rsa = malloc(sizeof(mbedtls_pk_context));
+ if (rsa == NULL) {
+ return NULL;
+ }
+
+ mbedtls_pk_init(rsa);
+
+ if (passphrase == NULL) {
+ if (auth_fn) {
+ valid = auth_fn("Passphrase for private key:", (char *) tmp,
+ MAX_PASSPHRASE_SIZE, 0, 0, auth_data);
+ if (valid < 0) {
+ return NULL;
+ }
+ /* TODO fix signedness and strlen */
+ valid = mbedtls_pk_parse_key(rsa,
+ (const unsigned char *) b64_key,
+ b64len, tmp,
+ strnlen((const char *) tmp, MAX_PASSPHRASE_SIZE));
+ } else {
+ valid = mbedtls_pk_parse_key(rsa,
+ (const unsigned char *) b64_key,
+ b64len, NULL,
+ 0);
+ }
+ } else {
+ valid = mbedtls_pk_parse_key(rsa,
+ (const unsigned char *) b64_key, b64len,
+ (const unsigned char *) passphrase,
+ strnlen(passphrase, MAX_PASSPHRASE_SIZE));
+ }
+
+ if (valid != 0) {
+ char error_buf[100];
+ mbedtls_strerror(valid, error_buf, 100);
+ SSH_LOG(SSH_LOG_WARN,"Parsing private key %s", error_buf);
+ goto fail;
+ }
+ break;
+ case SSH_KEYTYPE_ECDSA:
+ ecdsa = malloc(sizeof(mbedtls_pk_context));
+ if (ecdsa == NULL) {
+ return NULL;
+ }
+
+ mbedtls_pk_init(ecdsa);
+
+ if (passphrase == NULL) {
+ if (auth_fn) {
+ valid = auth_fn("Passphrase for private key:", (char *) tmp,
+ MAX_PASSPHRASE_SIZE, 0, 0, auth_data);
+ if (valid < 0) {
+ return NULL;
+ }
+ valid = mbedtls_pk_parse_key(ecdsa,
+ (const unsigned char *) b64_key,
+ b64len, tmp,
+ strnlen((const char *) tmp, MAX_PASSPHRASE_SIZE));
+ } else {
+ valid = mbedtls_pk_parse_key(ecdsa,
+ (const unsigned char *) b64_key,
+ b64len, NULL,
+ 0);
+ }
+ } else {
+ valid = mbedtls_pk_parse_key(ecdsa,
+ (const unsigned char *) b64_key, b64len,
+ (const unsigned char *) passphrase,
+ strnlen(passphrase, MAX_PASSPHRASE_SIZE));
+ }
+
+ if (valid != 0) {
+ char error_buf[100];
+ mbedtls_strerror(valid, error_buf, 100);
+ SSH_LOG(SSH_LOG_WARN,"Parsing private key %s", error_buf);
+ goto fail;
+ }
+ break;
+ case SSH_KEYTYPE_ED25519:
+ /* Cannot open ed25519 keys with libmbedcrypto */
+ default:
+ SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d",
+ type);
+ return NULL;
+ }
+
+ key = ssh_key_new();
+ if (key == NULL) {
+ goto fail;
+ }
+
+ key->type = type;
+ key->type_c = ssh_key_type_to_char(type);
+ key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
+ key->rsa = rsa;
+ if (ecdsa != NULL) {
+ mbedtls_ecp_keypair *keypair = mbedtls_pk_ec(*ecdsa);
+
+ key->ecdsa = malloc(sizeof(mbedtls_ecdsa_context));
+ if (key->ecdsa == NULL) {
+ goto fail;
+ }
+
+ mbedtls_ecdsa_init(key->ecdsa);
+ mbedtls_ecdsa_from_keypair(key->ecdsa, keypair);
+ mbedtls_pk_free(ecdsa);
+ SAFE_FREE(ecdsa);
+ } else {
+ key->ecdsa = NULL;
+ }
+ key->ed25519_privkey = ed25519;
+ rsa = NULL;
+ ecdsa = NULL;
+ if (key->type == SSH_KEYTYPE_ECDSA) {
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa);
+ key->type_c = pki_key_ecdsa_nid_to_name(key->ecdsa_nid);
+ }
+
+ return key;
+fail:
+ ssh_key_free(key);
+ if (rsa != NULL) {
+ mbedtls_pk_free(rsa);
+ SAFE_FREE(rsa);
+ }
+ if (ecdsa != NULL) {
+ mbedtls_pk_free(ecdsa);
+ SAFE_FREE(ecdsa);
+ }
+ return NULL;
+}
+
+int pki_pubkey_build_rsa(ssh_key key, ssh_string e, ssh_string n)
+{
+ mbedtls_rsa_context *rsa = NULL;
+ const mbedtls_pk_info_t *pk_info = NULL;
+ int rc;
+
+ key->rsa = malloc(sizeof(mbedtls_pk_context));
+ if (key->rsa == NULL) {
+ return SSH_ERROR;
+ }
+
+ mbedtls_pk_init(key->rsa);
+ pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
+ mbedtls_pk_setup(key->rsa, pk_info);
+
+ if (mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA)) {
+ rsa = mbedtls_pk_rsa(*key->rsa);
+ rc = mbedtls_mpi_read_binary(&rsa->N, ssh_string_data(n),
+ ssh_string_len(n));
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_mpi_read_binary(&rsa->E, ssh_string_data(e),
+ ssh_string_len(e));
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+
+ rsa->len = (mbedtls_mpi_bitlen(&rsa->N) + 7) >> 3;
+ } else {
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+ssh_key pki_key_dup(const ssh_key key, int demote)
+{
+ ssh_key new = NULL;
+ int rc;
+ const mbedtls_pk_info_t *pk_info = NULL;
+
+
+ new = ssh_key_new();
+ if (new == NULL) {
+ return NULL;
+ }
+
+ new->type = key->type;
+ new->type_c = key->type_c;
+ if (demote) {
+ new->flags = SSH_KEY_FLAG_PUBLIC;
+ } else {
+ new->flags = key->flags;
+ }
+
+
+ switch(key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1: {
+ mbedtls_rsa_context *rsa, *new_rsa;
+
+ new->rsa = malloc(sizeof(mbedtls_pk_context));
+ if (new->rsa == NULL) {
+ goto fail;
+ }
+
+ mbedtls_pk_init(new->rsa);
+ pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
+ mbedtls_pk_setup(new->rsa, pk_info);
+
+ if (mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA) &&
+ mbedtls_pk_can_do(new->rsa, MBEDTLS_PK_RSA)) {
+ rsa = mbedtls_pk_rsa(*key->rsa);
+ new_rsa = mbedtls_pk_rsa(*new->rsa);
+
+ rc = mbedtls_mpi_copy(&new_rsa->N, &rsa->N);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->E, &rsa->E);
+ if (rc != 0) {
+ goto fail;
+ }
+ new_rsa->len = (mbedtls_mpi_bitlen(&new_rsa->N) + 7) >> 3;
+
+ if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
+ rc = mbedtls_mpi_copy(&new_rsa->D, &rsa->D);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->P, &rsa->P);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->Q, &rsa->Q);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->DP, &rsa->DP);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->DQ, &rsa->DQ);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->QP, &rsa->QP);
+ if (rc != 0) {
+ goto fail;
+ }
+ }
+ } else {
+ goto fail;
+ }
+
+ break;
+ }
+ case SSH_KEYTYPE_ECDSA:
+ new->ecdsa_nid = key->ecdsa_nid;
+
+ new->ecdsa = malloc(sizeof(mbedtls_ecdsa_context));
+
+ if (new->ecdsa == NULL) {
+ goto fail;
+ }
+
+ mbedtls_ecdsa_init(new->ecdsa);
+
+ if (demote && ssh_key_is_private(key)) {
+ rc = mbedtls_ecp_copy(&new->ecdsa->Q, &key->ecdsa->Q);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_ecp_group_copy(&new->ecdsa->grp, &key->ecdsa->grp);
+ if (rc != 0) {
+ goto fail;
+ }
+ } else {
+ mbedtls_ecdsa_from_keypair(new->ecdsa, key->ecdsa);
+ }
+
+ break;
+ case SSH_KEYTYPE_ED25519:
+ rc = pki_ed25519_key_dup(new, key);
+ if (rc != SSH_OK) {
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+
+ return new;
+fail:
+ ssh_key_free(new);
+ return NULL;
+}
+
+int pki_key_generate_rsa(ssh_key key, int parameter)
+{
+ int rc;
+ const mbedtls_pk_info_t *info = NULL;
+
+ key->rsa = malloc(sizeof(mbedtls_pk_context));
+ if (key->rsa == NULL) {
+ return SSH_ERROR;
+ }
+
+ mbedtls_pk_init(key->rsa);
+
+ info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
+ rc = mbedtls_pk_setup(key->rsa, info);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+
+ if (mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA)) {
+ rc = mbedtls_rsa_gen_key(mbedtls_pk_rsa(*key->rsa), mbedtls_ctr_drbg_random,
+ &ssh_mbedtls_ctr_drbg, parameter, 65537);
+ if (rc != 0) {
+ mbedtls_pk_free(key->rsa);
+ return SSH_ERROR;
+ }
+ }
+
+ return SSH_OK;
+}
+
+int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
+{
+ switch (k1->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1: {
+ mbedtls_rsa_context *rsa1, *rsa2;
+ if (mbedtls_pk_can_do(k1->rsa, MBEDTLS_PK_RSA) &&
+ mbedtls_pk_can_do(k2->rsa, MBEDTLS_PK_RSA)) {
+ if (mbedtls_pk_get_type(k1->rsa) != mbedtls_pk_get_type(k2->rsa) ||
+ mbedtls_pk_get_bitlen(k1->rsa) !=
+ mbedtls_pk_get_bitlen(k2->rsa)) {
+ return 1;
+ }
+
+ rsa1 = mbedtls_pk_rsa(*k1->rsa);
+ rsa2 = mbedtls_pk_rsa(*k2->rsa);
+ if (mbedtls_mpi_cmp_mpi(&rsa1->N, &rsa2->N) != 0) {
+ return 1;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->E, &rsa2->E) != 0) {
+ return 1;
+ }
+
+ if (what == SSH_KEY_CMP_PRIVATE) {
+ if (mbedtls_mpi_cmp_mpi(&rsa1->P, &rsa2->P) != 0) {
+ return 1;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->Q, &rsa2->Q) != 0) {
+ return 1;
+ }
+ }
+ }
+ break;
+ }
+ case SSH_KEYTYPE_ECDSA:
+ /* TODO: mbedTLS can't compare ecdsa keys.
+ mbedtls_ecdsa_context is actually a mbedtls_ecp_keypair,
+ so the private and public points and the group can be accessed
+ through the keypair. However, mbedtls has no method corresponding
+ to OpenSSL's EC_GROUP_cmp and EC_POITN_cmp, so the comparison
+ would have to be done manually.
+ */
+ return 1;
+ case SSH_KEYTYPE_ED25519:
+ /* ed25519 keys handled globally */
+ return 0;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+ssh_string make_ecpoint_string(const mbedtls_ecp_group *g, const
+ mbedtls_ecp_point *p)
+{
+ ssh_string s = NULL;
+ size_t len = 1;
+ int rc;
+
+ s = ssh_string_new(len);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ rc = mbedtls_ecp_point_write_binary(g, p, MBEDTLS_ECP_PF_UNCOMPRESSED,
+ &len, ssh_string_data(s), ssh_string_len(s));
+ if (rc == MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) {
+ ssh_string_free(s);
+
+ s = ssh_string_new(len);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ rc = mbedtls_ecp_point_write_binary(g, p, MBEDTLS_ECP_PF_UNCOMPRESSED,
+ &len, ssh_string_data(s), ssh_string_len(s));
+ }
+
+ if (rc != 0) {
+ ssh_string_free(s);
+ return NULL;
+ }
+
+ if (len != ssh_string_len(s)) {
+ ssh_string_free(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+static const char* pki_key_ecdsa_nid_to_char(int nid)
+{
+ switch (nid) {
+ case NID_mbedtls_nistp256:
+ return "nistp256";
+ case NID_mbedtls_nistp384:
+ return "nistp384";
+ case NID_mbedtls_nistp521:
+ return "nistp521";
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+ssh_string pki_publickey_to_blob(const ssh_key key)
+{
+ ssh_buffer buffer = NULL;
+ ssh_string type_s = NULL;
+ ssh_string e = NULL;
+ ssh_string n = NULL;
+ ssh_string str = NULL;
+ int rc;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ return NULL;
+ }
+
+ if (key->cert != NULL) {
+ rc = ssh_buffer_add_buffer(buffer, key->cert);
+ if (rc < 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ goto makestring;
+ }
+
+ type_s = ssh_string_from_char(key->type_c);
+ if (type_s == NULL) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, type_s);
+ ssh_string_free(type_s);
+ if (rc < 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1: {
+ mbedtls_rsa_context *rsa;
+ if (mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA) == 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rsa = mbedtls_pk_rsa(*key->rsa);
+
+ e = ssh_make_bignum_string(&rsa->E);
+ if (e == NULL) {
+ goto fail;
+ }
+
+ n = ssh_make_bignum_string(&rsa->N);
+ if (n == NULL) {
+ goto fail;
+ }
+
+ if (ssh_buffer_add_ssh_string(buffer, e) < 0) {
+ goto fail;
+ }
+
+ if (ssh_buffer_add_ssh_string(buffer, n) < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(e);
+ ssh_string_free(e);
+ e = NULL;
+ ssh_string_burn(n);
+ ssh_string_free(n);
+ n = NULL;
+
+ break;
+ }
+ case SSH_KEYTYPE_ECDSA:
+ rc = ssh_buffer_reinit(buffer);
+ if (rc < 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ type_s =
+ ssh_string_from_char(pki_key_ecdsa_nid_to_name(key->ecdsa_nid));
+ if (type_s == NULL) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, type_s);
+ ssh_string_free(type_s);
+ if (rc < 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ type_s =
+ ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid));
+ if (type_s == NULL) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, type_s);
+ ssh_string_free(type_s);
+ if (rc < 0) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ e = make_ecpoint_string(&key->ecdsa->grp, &key->ecdsa->Q);
+
+ if (e == NULL) {
+ ssh_buffer_free(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(e);
+ ssh_string_free(e);
+ e = NULL;
+
+ break;
+ case SSH_KEYTYPE_ED25519:
+ rc = pki_ed25519_public_key_to_blob(buffer, key);
+ if (rc != SSH_OK) {
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+makestring:
+ str = ssh_string_new(ssh_buffer_get_len(buffer));
+ if (str == NULL) {
+ goto fail;
+ }
+
+ rc = ssh_string_fill(str, ssh_buffer_get(buffer),
+ ssh_buffer_get_len(buffer));
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_buffer_free(buffer);
+ return str;
+fail:
+ ssh_buffer_free(buffer);
+ ssh_string_burn(str);
+ ssh_string_free(str);
+ ssh_string_burn(e);
+ ssh_string_free(e);
+ ssh_string_burn(n);
+ ssh_string_free(n);
+
+ return NULL;
+}
+
+int pki_export_pubkey_rsa1(const ssh_key key, const char *host, char *rsa1,
+ size_t rsa1_len)
+{
+ char *e = NULL;
+ char *n = NULL;
+ int rsa_size = mbedtls_pk_get_bitlen(key->rsa);
+ mbedtls_rsa_context *rsa = NULL;
+
+ if (!mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA)) {
+ return SSH_ERROR;
+ }
+
+ rsa = mbedtls_pk_rsa(*key->rsa);
+
+ n = bignum_bn2dec(&rsa->N);
+ if (n == NULL) {
+ return SSH_ERROR;
+ }
+
+ e = bignum_bn2dec(&rsa->E);
+ if (e == NULL) {
+ return SSH_ERROR;
+ }
+
+ snprintf(rsa1, rsa1_len, "%s %d %s %s\n",
+ host, rsa_size << 3, e, n);
+
+ SAFE_FREE(e);
+ SAFE_FREE(n);
+ return SSH_OK;
+}
+
+ssh_string pki_signature_to_blob(const ssh_signature sig)
+{
+ ssh_string sig_blob = NULL;
+
+ switch(sig->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ sig_blob = ssh_string_copy(sig->rsa_sig);
+ break;
+ case SSH_KEYTYPE_ECDSA: {
+ ssh_string r;
+ ssh_string s;
+ ssh_buffer b;
+ int rc;
+
+ b = ssh_buffer_new();
+ if (b == NULL) {
+ return NULL;
+ }
+
+ r = ssh_make_bignum_string(sig->ecdsa_sig.r);
+ if (r == NULL) {
+ ssh_buffer_free(b);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(b, r);
+ ssh_string_free(r);
+ if (rc < 0) {
+ ssh_buffer_free(b);
+ return NULL;
+ }
+
+ s = ssh_make_bignum_string(sig->ecdsa_sig.s);
+ if (s == NULL) {
+ ssh_buffer_free(b);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(b, s);
+ ssh_string_free(s);
+ if (rc < 0) {
+ ssh_buffer_free(b);
+ return NULL;
+ }
+
+ sig_blob = ssh_string_new(ssh_buffer_get_len(b));
+ if (sig_blob == NULL) {
+ ssh_buffer_free(b);
+ return NULL;
+ }
+
+ ssh_string_fill(sig_blob, ssh_buffer_get(b), ssh_buffer_get_len(b));
+ ssh_buffer_free(b);
+ break;
+ }
+ case SSH_KEYTYPE_ED25519:
+ sig_blob = pki_ed25519_sig_to_blob(sig);
+ break;
+ default:
+ SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %s",
+ sig->type_c);
+ return NULL;
+ }
+
+ return sig_blob;
+}
+
+static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey, const
+ ssh_string sig_blob, ssh_signature sig)
+{
+ size_t pad_len = 0;
+ char *blob_orig = NULL;
+ char *blob_padded_data = NULL;
+ ssh_string sig_blob_padded = NULL;
+
+ size_t rsalen = 0;
+ size_t len = ssh_string_len(sig_blob);
+
+ if (pubkey->rsa == NULL) {
+ SSH_LOG(SSH_LOG_WARN, "Pubkey RSA field NULL");
+ goto errout;
+ }
+
+ rsalen = mbedtls_pk_get_bitlen(pubkey->rsa) / 8;
+ if (len > rsalen) {
+ SSH_LOG(SSH_LOG_WARN,
+ "Signature is too big: %lu > %lu",
+ (unsigned long) len,
+ (unsigned long) rsalen);
+ goto errout;
+ }
+#ifdef DEBUG_CRYPTO
+ SSH_LOG(SSH_LOG_WARN, "RSA signature len: %lu", (unsigned long)len);
+ ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len);
+#endif
+
+ if (len == rsalen) {
+ sig->rsa_sig = ssh_string_copy(sig_blob);
+ } else {
+ SSH_LOG(SSH_LOG_DEBUG, "RSA signature len %lu < %lu",
+ (unsigned long) len,
+ (unsigned long) rsalen);
+ pad_len = rsalen - len;
+
+ sig_blob_padded = ssh_string_new(rsalen);
+ if (sig_blob_padded == NULL) {
+ goto errout;
+ }
+
+ blob_padded_data = (char *) ssh_string_data(sig_blob_padded);
+ blob_orig = (char *) ssh_string_data(sig_blob);
+
+ BURN_BUFFER(blob_padded_data, pad_len);
+ memcpy(blob_padded_data + pad_len, blob_orig, len);
+
+ sig->rsa_sig = sig_blob_padded;
+ }
+
+ return sig;
+
+errout:
+ ssh_signature_free(sig);
+ return NULL;
+}
+ssh_signature pki_signature_from_blob(const ssh_key pubkey, const ssh_string
+ sig_blob, enum ssh_keytypes_e type)
+{
+ ssh_signature sig = NULL;
+ int rc;
+
+ sig = ssh_signature_new();
+ if (sig == NULL) {
+ return NULL;
+ }
+
+ sig->type = type;
+ sig->type_c = ssh_key_type_to_char(type);
+
+ switch(type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ sig = pki_signature_from_rsa_blob(pubkey, sig_blob, sig);
+ break;
+ case SSH_KEYTYPE_ECDSA: {
+ ssh_buffer b;
+ ssh_string r;
+ ssh_string s;
+ size_t rlen;
+
+ b = ssh_buffer_new();
+ if (b == NULL) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_data(b, ssh_string_data(sig_blob),
+ ssh_string_len(sig_blob));
+
+ if (rc < 0) {
+ ssh_buffer_free(b);
+ ssh_signature_free(sig);
+ return NULL;
+ }
+
+ r = ssh_buffer_get_ssh_string(b);
+ if (r == NULL) {
+ ssh_buffer_free(b);
+ ssh_signature_free(sig);
+ return NULL;
+ }
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("r", ssh_string_data(r), ssh_string_len(r));
+#endif
+ sig->ecdsa_sig.r = ssh_make_string_bn(r);
+ ssh_string_burn(r);
+ ssh_string_free(r);
+ if (sig->ecdsa_sig.r == NULL) {
+ ssh_buffer_free(b);
+ ssh_signature_free(sig);
+ return NULL;
+ }
+
+ s = ssh_buffer_get_ssh_string(b);
+ rlen = ssh_buffer_get_len(b);
+ ssh_buffer_free(b);
+ if (s == NULL) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("s", ssh_string_data(s), ssh_string_len(s));
+#endif
+ sig->ecdsa_sig.s = ssh_make_string_bn(s);
+ ssh_string_burn(s);
+ ssh_string_free(s);
+ if (sig->ecdsa_sig.s == NULL) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+
+ if (rlen != 0) {
+ SSH_LOG(SSH_LOG_WARN, "Signature has remaining bytes in inner "
+ "sigblob: %lu",
+ (unsigned long)rlen);
+ ssh_signature_free(sig);
+ return NULL;
+ }
+
+ break;
+ }
+ case SSH_KEYTYPE_ED25519:
+ rc = pki_ed25519_sig_from_blob(sig, sig_blob);
+ if (rc == SSH_ERROR) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+ break;
+ default:
+ SSH_LOG(SSH_LOG_WARN, "Unknown signature type");
+ return NULL;
+ }
+
+ return sig;
+}
+
+int pki_signature_verify(ssh_session session, const ssh_signature sig, const
+ ssh_key key, const unsigned char *hash, size_t hlen)
+{
+ int rc;
+
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ rc = mbedtls_pk_verify(key->rsa, MBEDTLS_MD_SHA1, hash, hlen,
+ ssh_string_data(sig->rsa_sig),
+ ssh_string_len(sig->rsa_sig));
+ if (rc != 0) {
+ char error_buf[100];
+ mbedtls_strerror(rc, error_buf, 100);
+ ssh_set_error(session, SSH_FATAL, "RSA error: %s", error_buf);
+ return SSH_ERROR;
+ }
+ break;
+ case SSH_KEYTYPE_ECDSA:
+ rc = mbedtls_ecdsa_verify(&key->ecdsa->grp, hash, hlen,
+ &key->ecdsa->Q, sig->ecdsa_sig.r, sig->ecdsa_sig.s);
+ if (rc != 0) {
+ char error_buf[100];
+ mbedtls_strerror(rc, error_buf, 100);
+ ssh_set_error(session, SSH_FATAL, "RSA error: %s", error_buf);
+ return SSH_ERROR;
+
+ }
+ break;
+ case SSH_KEYTYPE_ED25519:
+ rc = pki_ed25519_verify(key, sig, hash, hlen);
+ if (rc != SSH_OK) {
+ ssh_set_error(session, SSH_FATAL,
+ "ed25519 signature verification error");
+ return SSH_ERROR;
+ }
+ break;
+ default:
+ ssh_set_error(session, SSH_FATAL, "Unknown public key type");
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+static ssh_string rsa_do_sign(const unsigned char *digest, int dlen,
+ mbedtls_pk_context *privkey)
+{
+ ssh_string sig_blob = NULL;
+ unsigned char *sig = NULL;
+ size_t slen;
+ int ok;
+
+ sig = malloc(mbedtls_pk_get_bitlen(privkey) / 8);
+ if (sig == NULL) {
+ return NULL;
+ }
+
+ ok = mbedtls_pk_sign(privkey, MBEDTLS_MD_SHA1, digest, dlen, sig, &slen,
+ mbedtls_ctr_drbg_random, &ssh_mbedtls_ctr_drbg);
+
+ if (ok != 0) {
+ SAFE_FREE(sig);
+ return NULL;
+ }
+
+ sig_blob = ssh_string_new(slen);
+ if (sig_blob == NULL) {
+ SAFE_FREE(sig);
+ return NULL;
+ }
+
+ ssh_string_fill(sig_blob, sig, slen);
+ memset(sig, 'd', slen);
+ SAFE_FREE(sig);
+
+ return sig_blob;
+}
+
+
+ssh_signature pki_do_sign(const ssh_key privkey, const unsigned char *hash,
+ size_t hlen)
+{
+ ssh_signature sig = NULL;
+ int rc;
+
+ sig = ssh_signature_new();
+ if (sig == NULL) {
+ return NULL;
+ }
+
+ sig->type = privkey->type;
+ sig->type_c = privkey->type_c;
+
+ switch(privkey->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ sig->rsa_sig = rsa_do_sign(hash, hlen, privkey->rsa);
+ if (sig->rsa_sig == NULL) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+ break;
+ case SSH_KEYTYPE_ECDSA:
+ sig->ecdsa_sig.r = bignum_new();
+ if (sig->ecdsa_sig.r == NULL) {
+ return NULL;
+ }
+
+ sig->ecdsa_sig.s = bignum_new();
+ if (sig->ecdsa_sig.s == NULL) {
+ bignum_free(sig->ecdsa_sig.r);
+ return NULL;
+ }
+
+ rc = mbedtls_ecdsa_sign(&privkey->ecdsa->grp, sig->ecdsa_sig.r,
+ sig->ecdsa_sig.s, &privkey->ecdsa->d, hash, hlen,
+ mbedtls_ctr_drbg_random, &ssh_mbedtls_ctr_drbg);
+ if (rc != 0) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+ break;
+ case SSH_KEYTYPE_ED25519:
+ rc = pki_ed25519_sign(privkey, sig, hash, hlen);
+ if (rc != SSH_OK) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+ break;
+ default:
+ ssh_signature_free(sig);
+ return NULL;
+
+ }
+
+ return sig;
+}
+
+#ifdef WITH_SERVER
+ssh_signature pki_do_sign_sessionid(const ssh_key key, const unsigned char
+ *hash, size_t hlen)
+{
+ ssh_signature sig = NULL;
+ int rc;
+
+ sig = ssh_signature_new();
+ if (sig == NULL) {
+ return NULL;
+ }
+ sig->type = key->type;
+ sig->type_c = key->type_c;
+
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ sig->rsa_sig = rsa_do_sign(hash, hlen, key->rsa);
+ if (sig->rsa_sig == NULL) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+ break;
+ case SSH_KEYTYPE_ECDSA:
+ sig->ecdsa_sig.r = bignum_new();
+ if (sig->ecdsa_sig.r == NULL) {
+ return NULL;
+ }
+
+ sig->ecdsa_sig.s = bignum_new();
+ if (sig->ecdsa_sig.s == NULL) {
+ bignum_free(sig->ecdsa_sig.r);
+ return NULL;
+ }
+
+ rc = mbedtls_ecdsa_sign(&key->ecdsa->grp, sig->ecdsa_sig.r,
+ sig->ecdsa_sig.s, &key->ecdsa->d, hash, hlen,
+ mbedtls_ctr_drbg_random, &ssh_mbedtls_ctr_drbg);
+ if (rc != 0) {
+ ssh_signature_free(sig);
+ return NULL;
+ }
+ break;
+ case SSH_KEYTYPE_ED25519:
+ /* ED25519 handled in caller */
+ default:
+ ssh_signature_free(sig);
+ return NULL;
+ }
+
+ return sig;
+}
+#endif /* WITH_SERVER */
+
+const char *pki_key_ecdsa_nid_to_name(int nid)
+{
+ switch (nid) {
+ case NID_mbedtls_nistp256:
+ return "ecdsa-sha2-nistp256";
+ case NID_mbedtls_nistp384:
+ return "ecdsa-sha2-nistp384";
+ case NID_mbedtls_nistp521:
+ return "ecdsa-sha2-nistp521";
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+int pki_key_ecdsa_nid_from_name(const char *name)
+{
+ if (strcmp(name, "nistp256") == 0) {
+ return NID_mbedtls_nistp256;
+ } else if (strcmp(name, "nistp384") == 0) {
+ return NID_mbedtls_nistp384;
+ } else if (strcmp(name, "nistp521") == 0) {
+ return NID_mbedtls_nistp521;
+ }
+
+ return -1;
+}
+
+static mbedtls_ecp_group_id pki_key_ecdsa_nid_to_mbed_gid(int nid)
+{
+ switch (nid) {
+ case NID_mbedtls_nistp256:
+ return MBEDTLS_ECP_DP_SECP256R1;
+ case NID_mbedtls_nistp384:
+ return MBEDTLS_ECP_DP_SECP384R1;
+ case NID_mbedtls_nistp521:
+ return MBEDTLS_ECP_DP_SECP521R1;
+ }
+
+ return MBEDTLS_ECP_DP_NONE;
+}
+
+int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
+{
+ int rc;
+ mbedtls_ecp_keypair keypair;
+ mbedtls_ecp_group group;
+ mbedtls_ecp_point Q;
+
+ key->ecdsa_nid = nid;
+ key->type_c = pki_key_ecdsa_nid_to_name(nid);
+
+ key->ecdsa = malloc(sizeof(mbedtls_ecdsa_context));
+ if (key->ecdsa == NULL) {
+ return SSH_ERROR;
+ }
+
+ mbedtls_ecdsa_init(key->ecdsa);
+ mbedtls_ecp_keypair_init(&keypair);
+ mbedtls_ecp_group_init(&group);
+ mbedtls_ecp_point_init(&Q);
+
+ rc = mbedtls_ecp_group_load(&group,
+ pki_key_ecdsa_nid_to_mbed_gid(nid));
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_ecp_point_read_binary(&group, &Q, ssh_string_data(e),
+ ssh_string_len(e));
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_ecp_copy(&keypair.Q, &Q);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_ecp_group_copy(&keypair.grp, &group);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ mbedtls_mpi_init(&keypair.d);
+
+ rc = mbedtls_ecdsa_from_keypair(key->ecdsa, &keypair);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ mbedtls_ecp_point_free(&Q);
+ mbedtls_ecp_group_free(&group);
+ mbedtls_ecp_keypair_free(&keypair);
+ return SSH_OK;
+fail:
+ mbedtls_ecdsa_free(key->ecdsa);
+ mbedtls_ecp_point_free(&Q);
+ mbedtls_ecp_group_free(&group);
+ mbedtls_ecp_keypair_free(&keypair);
+ SAFE_FREE(key->ecdsa);
+ return SSH_ERROR;
+}
+
+int pki_key_generate_ecdsa(ssh_key key, int parameter)
+{
+ int nid;
+ int ok;
+
+ switch (parameter) {
+ case 384:
+ nid = NID_mbedtls_nistp384;
+ break;
+ case 512:
+ nid = NID_mbedtls_nistp521;
+ break;
+ case 256:
+ default:
+ nid = NID_mbedtls_nistp256;
+ break;
+ }
+
+ key->ecdsa_nid = nid;
+ key->type = SSH_KEYTYPE_ECDSA;
+ key->type_c = pki_key_ecdsa_nid_to_name(nid);
+
+ key->ecdsa = malloc(sizeof(mbedtls_ecdsa_context));
+ if (key->ecdsa == NULL) {
+ return SSH_ERROR;
+ }
+
+ mbedtls_ecdsa_init(key->ecdsa);
+
+ ok = mbedtls_ecdsa_genkey(key->ecdsa, pki_key_ecdsa_nid_to_mbed_gid(nid),
+ mbedtls_ctr_drbg_random, &ssh_mbedtls_ctr_drbg);
+
+ if (ok != 0) {
+ mbedtls_ecdsa_free(key->ecdsa);
+ SAFE_FREE(key->ecdsa);
+ }
+
+ return SSH_OK;
+}
+
+int pki_pubkey_build_dss(ssh_key key, ssh_string p, ssh_string q, ssh_string g,
+ ssh_string pubkey)
+{
+ (void) key;
+ (void) p;
+ (void) q;
+ (void) g;
+ (void) pubkey;
+ return SSH_ERROR;
+}
+
+int pki_key_generate_dss(ssh_key key, int parameter)
+{
+ (void) key;
+ (void) parameter;
+ return SSH_ERROR;
+}
+#endif /* HAVE_LIBMBEDCRYPTO */
diff --git a/src/server.c b/src/server.c
index b2552eaa..b31a8e42 100644
--- a/src/server.c
+++ b/src/server.c
@@ -107,6 +107,7 @@ static int server_set_kex(ssh_session session) {
",%s", session->srv.ecdsa_key->type_c);
}
#endif
+#ifdef HAVE_DSA
if (session->srv.dsa_key != NULL) {
len = strlen(hostkeys);
keytype = ssh_key_type(session->srv.dsa_key);
@@ -114,6 +115,7 @@ static int server_set_kex(ssh_session session) {
snprintf(hostkeys + len, sizeof(hostkeys) - len,
",%s", ssh_key_type_to_char(keytype));
}
+#endif
if (session->srv.rsa_key != NULL) {
len = strlen(hostkeys);
keytype = ssh_key_type(session->srv.rsa_key);
diff --git a/src/session.c b/src/session.c
index bb01f169..b372dad6 100644
--- a/src/session.c
+++ b/src/session.c
@@ -148,6 +148,7 @@ ssh_session ssh_new(void) {
goto err;
}
+#ifdef HAVE_DSA
id = strdup("%d/id_dsa");
if (id == NULL) {
goto err;
@@ -156,6 +157,7 @@ ssh_session ssh_new(void) {
if (rc == SSH_ERROR) {
goto err;
}
+#endif
id = strdup("%d/identity");
if (id == NULL) {
diff --git a/src/threads.c b/src/threads.c
index 062c3b84..b85ac756 100644
--- a/src/threads.c
+++ b/src/threads.c
@@ -33,6 +33,10 @@
#include "libssh/crypto.h"
#include "libssh/threads.h"
+#ifdef HAVE_LIBMBEDCRYPTO
+#include <mbedtls/threading.h>
+#endif
+
static int threads_noop (void **lock){
(void)lock;
return 0;
@@ -100,6 +104,28 @@ static int libgcrypt_thread_init(void){
return SSH_OK;
}
#endif /* GCRYPT_VERSION_NUMBER */
+#elif defined HAVE_LIBMBEDCRYPTO
+static int libmbedcrypto_thread_init(void)
+{
+ if (user_callbacks == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (user_callbacks == &ssh_threads_noop) {
+ return SSH_OK;
+ }
+#ifdef MBEDTLS_THREADING_ALT
+ else {
+ mbedtls_threading_set_alt(user_callbacks->mutex_init,
+ user_callbacks->mutex_destroy, user_callbacks->mutex_lock,
+ user_callbacks->mutex_unlock);
+ }
+#elif defined MBEDTLS_THREADING_PTHREAD
+ return SSH_OK;
+#else
+ return SSH_ERROR;
+#endif
+}
#else /* HAVE_LIBGCRYPT */
/* Libcrypto specific stuff */
@@ -181,6 +207,8 @@ int ssh_threads_init(void){
/* Then initialize the crypto libraries threading callbacks */
#ifdef HAVE_LIBGCRYPT
ret = libgcrypt_thread_init();
+#elif HAVE_LIBMBEDCRYPTO
+ ret = libmbedcrypto_thread_init();
#else /* Libcrypto */
ret = libcrypto_thread_init();
#endif
@@ -191,6 +219,10 @@ int ssh_threads_init(void){
void ssh_threads_finalize(void){
#ifdef HAVE_LIBGCRYPT
+#elif HAVE_LIBMBEDCRYPTO
+#ifdef MBEDTLS_THREADING_ALT
+ mbedtls_threading_free_alt();
+#endif
#else
libcrypto_thread_finalize();
#endif