[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 2/3] pki_gcrypt: Handle ECDSA keys and signatures
[Thread Prev] | [Thread Next]
- Subject: [PATCH 2/3] pki_gcrypt: Handle ECDSA keys and signatures
- From: Justus Winter <justus@xxxxxxxxxxx>
- Reply-to: libssh@xxxxxxxxxx
- Date: Wed, 30 Mar 2016 11:53:25 +0200
- To: libssh@xxxxxxxxxx
- Cc: Justus Winter <justus@xxxxxxxxxxx>
* ConfigureChecks.cmake: Set 'HAVE_ECC' and 'HAVE_GCRYPT_ECC' if applicable. * include/libssh/libgcrypt.h (EVPCTX): Fix type. (NID_gcrypt_nistp{256,384,521}): New constants. * include/libssh/pki.h (struct ssh_key_struct): Fix type of field 'ecdsa'. (struct ssh_signature_struct): Likewise for 'ecdsa_sig'. * src/curve25519.c (ssh_client_curve25519_init): Make use of the gcrypt-variant of 'bignum_bin2bn'. * src/libgcrypt.c (nid_to_md_algo): New function mapping curves to digest algorithms. (evp{,_init,_update,_final}): New functions mimicking the OpenSSL API. * src/pki.c (ssh_pki_key_ecdsa_name): Relax guard now that the used function is also provided by the gcrypt backend. (ssh_signature_free): Free ecdsa signature. * src/pki_gcrypt.c (ECDSA_HEADER_{BEGIN,END}): New macros. (ASN1_OCTET_{STRING,OBJECT_IDENTIFIER}): Likewise. (asn1_check_tag): New function. (privatekey_string_to_buffer): Handle ECDSA keys. (pki_key_ecdsa_to_nid): New function. (pki_key_ecdsa_nid_to_gcrypt_name): Likewise. (pki_key_ecdsa_nid_to_name): Likewise. (pki_key_ecdsa_nid_to_char): Likewise. (pki_key_ecdsa_nid_from_name): Implement. (make_ecpoint_string): New function. (asn1_oi_to_nid): Likewise. (b64decode_ecdsa_privatekey): Likewise. (pki_private_key_from_base64): Handle ECDSA keys. (pki_pubkey_build_ecdsa): Implement. (pki_key_dup): Handle ECDSA keys. (pki_key_generate): Likewise. (pki_key_generate_ecdsa): Implement. (pki_key_compare): Handle ECDSA keys. (pki_publickey_to_blob): Likewise. (pki_signature_from_blob): Likewise. (pki_signature_verify): Likewise. (pki_do_sign): Likewise. (pki_do_sign_sessionid): Likewise. Signed-off-by: Justus Winter <justus@xxxxxxxxxxx> --- ConfigureChecks.cmake | 4 +- include/libssh/libgcrypt.h | 7 +- include/libssh/pki.h | 4 +- src/curve25519.c | 7 + src/libgcrypt.c | 52 +++++ src/pki.c | 6 +- src/pki_gcrypt.c | 516 ++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 584 insertions(+), 12 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 1f4c837..1917ce2 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -199,8 +199,8 @@ endif (OPENSSL_FOUND) if (GCRYPT_FOUND) set(HAVE_LIBGCRYPT 1) if (GCRYPT_VERSION VERSION_GREATER "1.4.6") - #set(HAVE_GCRYPT_ECC 1) - #set(HAVE_ECC 1) + set(HAVE_GCRYPT_ECC 1) + set(HAVE_ECC 1) endif (GCRYPT_VERSION VERSION_GREATER "1.4.6") endif (GCRYPT_FOUND) diff --git a/include/libssh/libgcrypt.h b/include/libssh/libgcrypt.h index 736c667..607a251 100644 --- a/include/libssh/libgcrypt.h +++ b/include/libssh/libgcrypt.h @@ -32,7 +32,7 @@ typedef gcry_md_hd_t SHA384CTX; typedef gcry_md_hd_t SHA512CTX; typedef gcry_md_hd_t MD5CTX; typedef gcry_md_hd_t HMACCTX; -typedef void *EVPCTX; +typedef gcry_md_hd_t EVPCTX; #define SHA_DIGEST_LENGTH 20 #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH #define MD5_DIGEST_LEN 16 @@ -51,6 +51,11 @@ typedef void *EVPCTX; typedef gcry_mpi_t bignum; +/* Constants for curves. */ +#define NID_gcrypt_nistp256 0 +#define NID_gcrypt_nistp384 1 +#define NID_gcrypt_nistp521 2 + /* missing gcrypt functions */ int ssh_gcry_dec2bn(bignum *bn, const char *data); char *ssh_gcry_bn2dec(bignum bn); diff --git a/include/libssh/pki.h b/include/libssh/pki.h index 905956b..e0e30f1 100644 --- a/include/libssh/pki.h +++ b/include/libssh/pki.h @@ -47,7 +47,7 @@ struct ssh_key_struct { #ifdef HAVE_LIBGCRYPT gcry_sexp_t dsa; gcry_sexp_t rsa; - void *ecdsa; + gcry_sexp_t ecdsa; #elif HAVE_LIBCRYPTO DSA *dsa; RSA *rsa; @@ -69,7 +69,7 @@ struct ssh_signature_struct { #ifdef HAVE_LIBGCRYPT gcry_sexp_t dsa_sig; gcry_sexp_t rsa_sig; - void *ecdsa_sig; + gcry_sexp_t ecdsa_sig; #elif defined HAVE_LIBCRYPTO DSA_SIG *dsa_sig; ssh_string rsa_sig; diff --git a/src/curve25519.c b/src/curve25519.c index 43783e8..77fab2d 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -71,11 +71,14 @@ int ssh_client_curve25519_init(ssh_session session){ static int ssh_curve25519_build_k(ssh_session session) { ssh_curve25519_pubkey k; + +#ifdef HAVE_LIBCRYPTO session->next_crypto->k = bignum_new(); if (session->next_crypto->k == NULL) { return SSH_ERROR; } +#endif if (session->server) crypto_scalarmult(k, session->next_crypto->curve25519_privkey, @@ -84,7 +87,11 @@ static int ssh_curve25519_build_k(ssh_session session) { crypto_scalarmult(k, session->next_crypto->curve25519_privkey, session->next_crypto->curve25519_server_pubkey); +#ifdef HAVE_LIBGCRYPT + bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->k); +#elif defined HAVE_LIBCRYPTO bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k); +#endif #ifdef DEBUG_CRYPTO ssh_print_hexa("Session server cookie", diff --git a/src/libgcrypt.c b/src/libgcrypt.c index 60960a3..204fe82 100644 --- a/src/libgcrypt.c +++ b/src/libgcrypt.c @@ -71,6 +71,58 @@ void sha1(unsigned char *digest, int len, unsigned char *hash) { gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len); } +#ifdef HAVE_GCRYPT_ECC +static int nid_to_md_algo(int nid) +{ + switch (nid) { + case NID_gcrypt_nistp256: + return GCRY_MD_SHA256; + case NID_gcrypt_nistp384: + return GCRY_MD_SHA384; + case NID_gcrypt_nistp521: + return GCRY_MD_SHA512; + } + return GCRY_MD_NONE; +} + +void evp(int nid, unsigned char *digest, int len, + unsigned char *hash, unsigned int *hlen) +{ + int algo = nid_to_md_algo(nid); + + /* Note: What gcrypt calls 'hash' is called 'digest' here and + vice-versa. */ + gcry_md_hash_buffer(algo, hash, digest, len); + *hlen = gcry_md_get_algo_dlen(algo); +} + +EVPCTX evp_init(int nid) +{ + gcry_error_t err; + int algo = nid_to_md_algo(nid); + EVPCTX ctx; + + err = gcry_md_open(&ctx, algo, 0); + if (err) { + return NULL; + } + + return ctx; +} + +void evp_update(EVPCTX ctx, const void *data, unsigned long len) +{ + gcry_md_write(ctx, data, len); +} + +void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen) +{ + int algo = gcry_md_get_algo(ctx); + *mdlen = gcry_md_get_algo_dlen(algo); + memcpy(md, gcry_md_read(ctx, algo), *mdlen); +} +#endif + SHA256CTX sha256_init(void) { SHA256CTX ctx = NULL; gcry_md_open(&ctx, GCRY_MD_SHA256, 0); diff --git a/src/pki.c b/src/pki.c index 4835468..eb7c841 100644 --- a/src/pki.c +++ b/src/pki.c @@ -91,7 +91,7 @@ enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) { */ const char *ssh_pki_key_ecdsa_name(const ssh_key key) { -#ifdef HAVE_OPENSSL_ECC /* FIXME Better ECC check needed */ +#ifdef HAVE_ECC /* FIXME Better ECC check needed */ return pki_key_ecdsa_nid_to_name(key->ecdsa_nid); #else (void) key; /* unused */ @@ -357,7 +357,9 @@ void ssh_signature_free(ssh_signature sig) #endif break; case SSH_KEYTYPE_ECDSA: -#if defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_ECC) +#ifdef HAVE_LIBGCRYPT_ECC + gcry_sexp_release(sig->ecdsa_sig); +#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_ECC) ECDSA_SIG_free(sig->ecdsa_sig); #endif break; diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c index 0d847e9..8d9ba2a 100644 --- a/src/pki_gcrypt.c +++ b/src/pki_gcrypt.c @@ -27,6 +27,7 @@ #ifdef HAVE_LIBGCRYPT +#include <assert.h> #include <string.h> #include <stdlib.h> #include <gcrypt.h> @@ -45,11 +46,15 @@ #define RSA_HEADER_END "-----END RSA PRIVATE KEY-----" #define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----" #define DSA_HEADER_END "-----END DSA PRIVATE KEY-----" +#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----" +#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----" #define MAX_KEY_SIZE 32 #define MAX_PASSPHRASE_SIZE 1024 #define ASN1_INTEGER 2 #define ASN1_BIT_STRING 3 +#define ASN1_OCTET_STRING 4 +#define ASN1_OBJECT_IDENTIFIER 6 #define ASN1_SEQUENCE 48 #define PKCS5_SALT_LEN 8 @@ -222,6 +227,17 @@ static int asn1_check_sequence(ssh_buffer buffer) { return 1; } +static int asn1_check_tag(ssh_buffer buffer, unsigned char tag) { + unsigned char tmp; + + if (ssh_buffer_get_data(buffer, &tmp, 1) == 0 || tmp != tag) { + return 0; + } + + (void) asn1_get_len(buffer); + return 1; +} + static int passphrase_to_key(char *data, unsigned int datalen, unsigned char *salt, unsigned char *key, unsigned int keylen) { MD5CTX md; @@ -401,6 +417,10 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, header_begin = RSA_HEADER_BEGIN; header_end = RSA_HEADER_END; break; + case SSH_KEYTYPE_ECDSA: + header_begin = ECDSA_HEADER_BEGIN; + header_end = ECDSA_HEADER_END; + break; default: ssh_buffer_free(buffer); return NULL; @@ -655,10 +675,175 @@ error: } #ifdef HAVE_GCRYPT_ECC +static int pki_key_ecdsa_to_nid(gcry_sexp_t k) +{ + gcry_sexp_t sexp; + const char *tmp; + size_t size; + + sexp = gcry_sexp_find_token(k, "curve", 0); + if (sexp == NULL) { + return -1; + } + + tmp = gcry_sexp_nth_data(sexp, 1, &size); + + if (size == 10 && memcmp("NIST P-256", tmp, size) == 0) { + return NID_gcrypt_nistp256; + } else if (size == 10 && memcmp("NIST P-384", tmp, size) == 0) { + return NID_gcrypt_nistp384; + } else if (size == 10 && memcmp("NIST P-521", tmp, size) == 0) { + return NID_gcrypt_nistp521; + } + + return -1; +} + +static const char *pki_key_ecdsa_nid_to_gcrypt_name(int nid) +{ + switch (nid) { + case NID_gcrypt_nistp256: + return "NIST P-256"; + case NID_gcrypt_nistp384: + return "NIST P-384"; + case NID_gcrypt_nistp521: + return "NIST P-521"; + } + + return "unknown"; +} + + +const char *pki_key_ecdsa_nid_to_name(int nid) +{ + switch (nid) { + case NID_gcrypt_nistp256: + return "ecdsa-sha2-nistp256"; + case NID_gcrypt_nistp384: + return "ecdsa-sha2-nistp384"; + case NID_gcrypt_nistp521: + return "ecdsa-sha2-nistp521"; + } + + return "unknown"; +} + +static const char *pki_key_ecdsa_nid_to_char(int nid) +{ + switch (nid) { + case NID_gcrypt_nistp256: + return "nistp256"; + case NID_gcrypt_nistp384: + return "nistp384"; + case NID_gcrypt_nistp521: + return "nistp521"; + default: + break; + } + + return "unknown"; +} + int pki_key_ecdsa_nid_from_name(const char *name) { + if (strcmp(name, "nistp256") == 0) { + return NID_gcrypt_nistp256; + } else if (strcmp(name, "nistp384") == 0) { + return NID_gcrypt_nistp384; + } else if (strcmp(name, "nistp521") == 0) { + return NID_gcrypt_nistp521; + } + + return -1; +} + +static int asn1_oi_to_nid(const ssh_string oi) +{ + static const struct { + int nid; + size_t length; + const char *identifier; + } *e, mapping[] = { + {NID_gcrypt_nistp256, 8, "\x2a\x86\x48\xce\x3d\x03\x01\x07"}, + {NID_gcrypt_nistp384, 5, "\x2b\x81\x04\x00\x22"}, + {NID_gcrypt_nistp521, 5, "\x2b\x81\x04\x00\x23"}, + {0}, + }; + size_t len = ssh_string_len(oi); + for (e = mapping; e->length; e++) { + if (len == e->length + && memcmp(ssh_string_data(oi), e->identifier, len) == 0) { + return e->nid; + } + } return -1; } + +static int b64decode_ecdsa_privatekey(const char *pkey, gcry_sexp_t *r, + ssh_auth_callback cb, void *userdata, + const char *desc) { + const unsigned char *data; + ssh_buffer buffer = NULL; + ssh_string v = NULL; + ssh_string d = NULL; + ssh_string oi = NULL; + int nid; + ssh_string q = NULL; + int valid = 0; + + buffer = privatekey_string_to_buffer(pkey, SSH_KEYTYPE_ECDSA, + cb, userdata, desc); + if (buffer == NULL) { + goto error; + } + + if (!asn1_check_sequence(buffer)) { + goto error; + } + + /* RFC5915 specifies version 1. */ + v = asn1_get_int(buffer); + if (v == NULL) { + goto error; + } + + data = ssh_string_data(v); + if (ssh_string_len(v) != 1 || data[0] != 1) { + goto error; + } + + d = asn1_get(buffer, ASN1_OCTET_STRING); + if (!asn1_check_tag(buffer, 0xa0)) { + goto error; + } + oi = asn1_get(buffer, ASN1_OBJECT_IDENTIFIER); + nid = asn1_oi_to_nid(oi); + if (!asn1_check_tag(buffer, 0xa1)) { + goto error; + } + q = asn1_get_bit_string(buffer); + + if (d == NULL || oi == NULL || nid == -1 || q == NULL) { + goto error; + } + + valid = gcry_sexp_build(r, NULL, + "(private-key(ecdsa(curve %s)(d %b)(q %b)))", + pki_key_ecdsa_nid_to_gcrypt_name(nid), + ssh_string_len(d), ssh_string_data(d), + ssh_string_len(q), ssh_string_data(q)) == 0; + + error: + ssh_buffer_free(buffer); + ssh_string_free(v); + ssh_string_burn(d); + ssh_string_free(d); + ssh_string_free(oi); + ssh_string_burn(q); + ssh_string_free(q); + + return valid; +} #endif ssh_string pki_private_key_to_pem(const ssh_key key, @@ -681,6 +866,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, { gcry_sexp_t dsa = NULL; gcry_sexp_t rsa = NULL; + gcry_sexp_t ecdsa = NULL; ssh_key key = NULL; enum ssh_keytypes_e type; int valid; @@ -736,9 +922,29 @@ ssh_key pki_private_key_from_base64(const char *b64_key, goto fail; } break; + case SSH_KEYTYPE_ECDSA: +#if HAVE_GCRYPT_ECC + if (passphrase == NULL) { + if (auth_fn) { + valid = b64decode_ecdsa_privatekey(b64_key, &ecdsa, + auth_fn, auth_data, "Passphrase for private key:"); + } else { + valid = b64decode_ecdsa_privatekey(b64_key, &ecdsa, NULL, + NULL, NULL); + } + } else { + valid = b64decode_ecdsa_privatekey(b64_key, &ecdsa, NULL, + (void *)passphrase, NULL); + } + + if (!valid) { + SSH_LOG(SSH_LOG_WARN, "Parsing private key"); + goto fail; + } + break; +#endif case SSH_KEYTYPE_ED25519: /* Cannot open ed25519 keys with libgcrypt */ - case SSH_KEYTYPE_ECDSA: case SSH_KEYTYPE_UNKNOWN: default: SSH_LOG(SSH_LOG_WARN, "Unkown or invalid private key type %d", type); @@ -755,12 +961,20 @@ ssh_key pki_private_key_from_base64(const char *b64_key, key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; key->dsa = dsa; key->rsa = rsa; + key->ecdsa = ecdsa; +#ifdef HAVE_GCRYPT_ECC + 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); + } +#endif return key; fail: ssh_key_free(key); gcry_sexp_release(dsa); gcry_sexp_release(rsa); + gcry_sexp_release(ecdsa); return NULL; } @@ -800,7 +1014,20 @@ int pki_pubkey_build_rsa(ssh_key key, #ifdef HAVE_GCRYPT_ECC int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) { - return -1; + gpg_error_t err; + + key->ecdsa_nid = nid; + key->type_c = pki_key_ecdsa_nid_to_name(nid); + + err = gcry_sexp_build(&key->ecdsa, NULL, + "(public-key(ecdsa(curve %s)(q %b)))", + pki_key_ecdsa_nid_to_gcrypt_name(nid), + ssh_string_len(e), ssh_string_data(e)); + if (err) { + return SSH_ERROR; + } + + return SSH_OK; } #endif @@ -821,6 +1048,8 @@ ssh_key pki_key_dup(const ssh_key key, int demote) gcry_mpi_t d = NULL; gcry_mpi_t u = NULL; + gcry_sexp_t curve = NULL; + new = ssh_key_new(); if (new == NULL) { return NULL; @@ -880,6 +1109,31 @@ ssh_key pki_key_dup(const ssh_key key, int demote) break; case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + new->ecdsa_nid = key->ecdsa_nid; + + err = gcry_sexp_extract_param(key->ecdsa, NULL, + "qd?", &q, &d, NULL); + if (err) { + break; + } + + curve = gcry_sexp_find_token(key->ecdsa, "curve", 0); + if (curve == NULL) { + break; + } + + if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { + err = gcry_sexp_build(&new->ecdsa, NULL, + "(private-key(ecdsa %S (d %m)(q %m)))", + curve, d, q); + } else { + err = gcry_sexp_build(&new->ecdsa, NULL, + "(private-key(ecdsa %S (q %m)))", + curve, q); + } + break; +#endif case SSH_KEYTYPE_UNKNOWN: default: ssh_key_free(new); @@ -902,6 +1156,8 @@ ssh_key pki_key_dup(const ssh_key key, int demote) gcry_mpi_release(d); gcry_mpi_release(u); + gcry_sexp_release(curve); + return new; } @@ -915,10 +1171,19 @@ static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int parameter); if (rc != 0) return SSH_ERROR; - if(type == SSH_KEYTYPE_RSA) + switch (type) { + case SSH_KEYTYPE_RSA: rc = gcry_pk_genkey(&key->rsa, parms); - else + break; + case SSH_KEYTYPE_DSS: rc = gcry_pk_genkey(&key->dsa, parms); + break; + case SSH_KEYTYPE_ECDSA: + rc = gcry_pk_genkey(&key->ecdsa, parms); + break; + default: + assert (! "reached"); + } gcry_sexp_release(parms); if (rc != 0) return SSH_ERROR; @@ -934,7 +1199,22 @@ int pki_key_generate_dss(ssh_key key, int parameter){ #ifdef HAVE_GCRYPT_ECC int pki_key_generate_ecdsa(ssh_key key, int parameter) { - return -1; + int nid; + + switch (parameter) { + case 384: + nid = NID_gcrypt_nistp384; + break; + case 512: + nid = NID_gcrypt_nistp521; + break; + case 256: + default: + nid = NID_gcrypt_nistp256; + } + + key->ecdsa_nid = nid; + return pki_key_generate(key, parameter, "ecdsa", SSH_KEYTYPE_ECDSA); } #endif @@ -1035,6 +1315,20 @@ int pki_key_compare(const ssh_key k1, /* ed25519 keys handled globaly */ return 0; case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + if (k1->ecdsa_nid != k2->ecdsa_nid) { + return 1; + } + + if (_bignum_cmp(k1->ecdsa, k2->ecdsa, "q") != 0) { + return 1; + } + + if (_bignum_cmp(k1->ecdsa, k2->ecdsa, "d") != 0) { + return 1; + } + break; +#endif case SSH_KEYTYPE_DSS_CERT01: case SSH_KEYTYPE_RSA_CERT01: case SSH_KEYTYPE_UNKNOWN: @@ -1166,6 +1460,38 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } break; case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + 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 = ssh_sexp_extract_mpi(key->ecdsa, "q", GCRYMPI_FMT_STD, + GCRYMPI_FMT_STD); + 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; +#endif case SSH_KEYTYPE_UNKNOWN: default: goto fail; @@ -1318,6 +1644,58 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) sig_blob = pki_ed25519_sig_to_blob(sig); break; case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + { + ssh_string R; + ssh_string S; + ssh_buffer b; + int rc; + + b = ssh_buffer_new(); + if (b == NULL) { + return NULL; + } + + R = ssh_sexp_extract_mpi(sig->ecdsa_sig, "r", + GCRYMPI_FMT_USG, GCRYMPI_FMT_STD); + 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_sexp_extract_mpi(sig->ecdsa_sig, "s", + GCRYMPI_FMT_USG, GCRYMPI_FMT_STD); + 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; + } +#endif case SSH_KEYTYPE_UNKNOWN: default: SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %d", sig->type); @@ -1419,6 +1797,78 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, } break; case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + { /* build ecdsa siganature */ + ssh_buffer b; + ssh_string r, s; + uint32_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; + } + + s = ssh_buffer_get_ssh_string(b); + rlen = ssh_buffer_get_len(b); + ssh_buffer_free(b); + if (s == NULL) { + ssh_string_burn(r); + ssh_string_free(r); + 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_string_burn(r); + ssh_string_free(r); + ssh_string_burn(s); + ssh_string_free(s); + ssh_signature_free(sig); + return NULL; + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("r", ssh_string_data(r), ssh_string_len(r)); + ssh_print_hexa("s", ssh_string_data(s), ssh_string_len(s)); +#endif + + err = gcry_sexp_build(&sig->ecdsa_sig, + NULL, + "(sig-val(ecdsa(r %b)(s %b)))", + ssh_string_len(r), ssh_string_data(r), + ssh_string_len(s), ssh_string_data(s)); + ssh_string_burn(r); + ssh_string_free(r); + ssh_string_burn(s); + ssh_string_free(s); + if (err) { + ssh_signature_free(sig); + return NULL; + } + } + break; +#endif case SSH_KEYTYPE_UNKNOWN: default: SSH_LOG(SSH_LOG_WARN, "Unknown signature type"); @@ -1502,6 +1952,31 @@ int pki_signature_verify(ssh_session session, } break; case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + err = gcry_sexp_build(&sexp, NULL, "(data(flags raw)(value %b))", + hlen, hash); + if (err) { + ssh_set_error(session, + SSH_FATAL, + "ECDSA hash error: %s", + gcry_strerror(err)); + return SSH_ERROR; + } + err = gcry_pk_verify(sig->ecdsa_sig, sexp, key->ecdsa); + gcry_sexp_release(sexp); + if (err) { + ssh_set_error(session, SSH_FATAL, "Invalid ECDSA signature"); + abort(); + if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { + ssh_set_error(session, + SSH_FATAL, + "ECDSA verify error: %s", + gcry_strerror(err)); + } + return SSH_ERROR; + } + break; +#endif case SSH_KEYTYPE_UNKNOWN: default: ssh_set_error(session, SSH_FATAL, "Unknown public key type"); @@ -1575,6 +2050,22 @@ ssh_signature pki_do_sign(const ssh_key privkey, } break; case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + err = gcry_sexp_build(&sexp, NULL, "(data(flags raw)(value %b))", + hlen, hash); + if (err) { + ssh_signature_free(sig); + return NULL; + } + + err = gcry_pk_sign(&sig->ecdsa_sig, sexp, privkey->ecdsa); + gcry_sexp_release(sexp); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; +#endif case SSH_KEYTYPE_UNKNOWN: default: ssh_signature_free(sig); @@ -1644,6 +2135,21 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key, case SSH_KEYTYPE_ED25519: /* ED25519 handled in caller */ case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_GCRYPT_ECC + err = gcry_sexp_build(&sexp, NULL, "(data(flags raw)(value %b))", + hlen, hash); + if (err) { + ssh_signature_free(sig); + return NULL; + } + err = gcry_pk_sign(&sig->ecdsa_sig, sexp, key->ecdsa); + gcry_sexp_release(sexp); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; +#endif case SSH_KEYTYPE_UNKNOWN: default: return NULL; -- 2.1.4
[PATCH 1/3] pki_gcrypt: Add primitive to read ASN.1 bit strings | Justus Winter <justus@xxxxxxxxxxx> |