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

[PATCH 4/5] pki_gcrypt: Handle ECDSA keys and signatures


* ConfigureChecks.cmake: Set 'HAVE_ECC' and 'HAVE_GCRYPT_ECC' if
applicable.
* include/libssh/pki.h (struct ssh_key_struct): Fix type of field
'ecdsa'.
(struct ssh_signature_struct): Likewise for 'ecdsa_sig'.
* 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.
(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.
(asn1_oi_to_nid): New function.
(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/pki.h  |   4 +-
 src/pki.c             |   6 +-
 src/pki_gcrypt.c      | 503 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 506 insertions(+), 11 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/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/pki.c b/src/pki.c
index 2d48d25..1469a68 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 acfed2e..ca5b7cd 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,6 +46,8 @@
 #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
@@ -420,6 +423,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;
@@ -674,10 +681,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,
@@ -700,6 +872,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;
@@ -755,9 +928,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);
@@ -774,12 +967,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;
 }
@@ -819,7 +1020,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
 
@@ -840,6 +1054,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;
@@ -916,6 +1132,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);
@@ -938,6 +1179,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;
 }
 
@@ -951,10 +1194,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;
@@ -970,7 +1222,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
 
@@ -1071,6 +1338,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:
@@ -1220,6 +1501,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;
@@ -1372,6 +1685,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);
@@ -1473,6 +1838,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");
@@ -1556,6 +1993,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");
@@ -1629,6 +2091,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);
@@ -1698,6 +2176,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.8.1


References:
Re: [PATCH 2/3] pki_gcrypt: Handle ECDSA keys and signaturesAndreas Schneider <asn@xxxxxxxxxxxxxx>
[PATCH 1/5] curve25519: Small libgcrypt bignum fixJustus Winter <justus@xxxxxxxxxxx>
Archive administrator: postmaster@lists.cynapses.org