1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-11-29 01:03:57 +03:00
Files
libssh/src/pki_mbedcrypto.c
Jakub Jelen 1f08aabe43 pki: RSA signatures with SHA2 hash algorithms (RFC 8332)
* This change introduces a new API to request signature using
   one key and different hash algorithms. This is used only with
   RSA keys, that used to have SHA1 hardcoded, but the new
   algorithsms allow to use the SHA2 hashes, if the extension
   is negotiated.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2018-08-31 14:18:34 +02:00

1352 lines
37 KiB
C

/*
* 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};
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:
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: {
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_get_mbedtls_ctr_drbg_context(),
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: {
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: {
mbedtls_ecp_keypair *ecdsa1 = k1->ecdsa;
mbedtls_ecp_keypair *ecdsa2 = k2->ecdsa;
if (ecdsa1->grp.id != ecdsa2->grp.id) {
return 1;
}
if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.X, &ecdsa2->Q.X)) {
return 1;
}
if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.Y, &ecdsa2->Q.Y)) {
return 1;
}
if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.Z, &ecdsa2->Q.Z)) {
return 1;
}
if (what == SSH_KEY_CMP_PRIVATE) {
if (mbedtls_mpi_cmp_mpi(&ecdsa1->d, &ecdsa2->d)) {
return 1;
}
}
break;
}
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: {
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;
}
ssh_string pki_signature_to_blob(const ssh_signature sig)
{
ssh_string sig_blob = NULL;
switch(sig->type) {
case SSH_KEYTYPE_RSA:
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);
explicit_bzero(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,
enum ssh_digest_e hash_type)
{
ssh_signature sig = NULL;
int rc;
sig = ssh_signature_new();
if (sig == NULL) {
return NULL;
}
sig->type = type;
sig->hash_type = hash_type;
sig->type_c = ssh_key_signature_to_char(type, hash_type);
switch(type) {
case SSH_KEYTYPE_RSA:
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;
mbedtls_md_type_t md = 0;
switch (key->type) {
case SSH_KEYTYPE_RSA:
switch (sig->hash_type) {
case SSH_DIGEST_SHA1:
case SSH_DIGEST_AUTO:
md = MBEDTLS_MD_SHA1;
break;
case SSH_DIGEST_SHA256:
md = MBEDTLS_MD_SHA256;
break;
case SSH_DIGEST_SHA512:
md = MBEDTLS_MD_SHA512;
break;
default:
SSH_LOG(SSH_LOG_TRACE, "Unknown sig type %d", sig->hash_type);
ssh_set_error(session,
SSH_FATAL,
"Unexpected signature hash type %d during RSA verify",
sig->hash_type);
return SSH_ERROR;
}
rc = mbedtls_pk_verify(key->rsa, md, 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_hash(const unsigned char *digest,
int dlen,
mbedtls_pk_context *privkey,
enum ssh_digest_e hash_type)
{
ssh_string sig_blob = NULL;
mbedtls_md_type_t md = 0;
unsigned char *sig = NULL;
size_t slen;
int ok;
switch (hash_type) {
case SSH_DIGEST_SHA1:
case SSH_DIGEST_AUTO:
md = MBEDTLS_MD_SHA1;
break;
case SSH_DIGEST_SHA256:
md = MBEDTLS_MD_SHA256;
break;
case SSH_DIGEST_SHA512:
md = MBEDTLS_MD_SHA512;
break;
default:
SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
return NULL;
}
sig = malloc(mbedtls_pk_get_bitlen(privkey) / 8);
if (sig == NULL) {
return NULL;
}
ok = mbedtls_pk_sign(privkey,
md,
digest,
dlen,
sig,
&slen,
mbedtls_ctr_drbg_random,
ssh_get_mbedtls_ctr_drbg_context());
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_hash(const ssh_key privkey,
const unsigned char *hash,
size_t hlen,
enum ssh_digest_e hash_type)
{
ssh_signature sig = NULL;
int rc;
/* Only RSA supports different signature algorithm types now */
if (privkey->type != SSH_KEYTYPE_RSA && hash_type != SSH_DIGEST_AUTO) {
SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
return NULL;
}
sig = ssh_signature_new();
if (sig == NULL) {
return NULL;
}
sig->type = privkey->type;
sig->hash_type = hash_type;
sig->type_c = privkey->type_c;
switch(privkey->type) {
case SSH_KEYTYPE_RSA:
sig->type_c = ssh_key_signature_to_char(privkey->type, hash_type);
sig->rsa_sig = rsa_do_sign_hash(hash, hlen, privkey->rsa, hash_type);
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_safe_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_get_mbedtls_ctr_drbg_context());
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:
sig->rsa_sig = rsa_do_sign_hash(hash, hlen, key->rsa, SSH_DIGEST_AUTO);
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_safe_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_get_mbedtls_ctr_drbg_context());
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_get_mbedtls_ctr_drbg_context());
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 */