aboutsummaryrefslogtreecommitdiff
path: root/src/pki_mbedcrypto.c
diff options
context:
space:
mode:
authorJuraj Vijtiuk <juraj.vijtiuk@sartura.hr>2017-12-28 11:10:43 +0100
committerAndreas Schneider <asn@cryptomilk.org>2017-12-28 11:17:39 +0100
commit778652460f7cceb3e760964a890ffd99ec8230e7 (patch)
treeb30fca5918cc4f88abcba9e12146c6fe5db11ad1 /src/pki_mbedcrypto.c
parent5c3b1ee0a427e3a6a0b2ba0862fd9d2c4c25d736 (diff)
downloadlibssh-778652460f7cceb3e760964a890ffd99ec8230e7.tar.gz
libssh-778652460f7cceb3e760964a890ffd99ec8230e7.tar.xz
libssh-778652460f7cceb3e760964a890ffd99ec8230e7.zip
add mbedtls crypto support
Summary: This patch adds support for mbedTLS as a crypto backend for libssh. mbedTLS is an SSL/TLS library that has been designed to mainly be used in embedded systems. It is loosely coupled and has a low memory footprint. mbedTLS also provides a cryptography library (libmbedcrypto) that can be used without the TLS modules. The patch is unfortunately quite big, since several new files had to be added. DSA is disabled at compile time, since mbedTLS doesn't support DSA Patch review and feedback would be appreciated, and if any issues or suggestions appear, I'm willing to work on them. Signed-off-by: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr> Test Plan: * The patch has been tested with a Debug and MinSizeRel build, with libssh unit tests, client tests and the pkd tests. * All the tests have been run with valgrind's memcheck, drd and helgrind tools. * The examples/samplessh client works when built with the patch. Reviewers: asn, aris Subscribers: simonsj Differential Revision: https://bugs.libssh.org/D1
Diffstat (limited to 'src/pki_mbedcrypto.c')
-rw-r--r--src/pki_mbedcrypto.c1304
1 files changed, 1304 insertions, 0 deletions
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 */