[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/3] pki_gcrypt: Handle ECDSA keys and signatures


* 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


References:
[PATCH 1/3] pki_gcrypt: Add primitive to read ASN.1 bit stringsJustus Winter <justus@xxxxxxxxxxx>
Archive administrator: postmaster@lists.cynapses.org