[gnome-keyring/wip/dueno/ecdsa-support: 1/10] ASN.1 definitions of ECDSA and helper functions for DER encoding and decoding



commit 32680d2667410c8d1802e10746084253daee65d0
Author: Jakub Jelen <jjelen redhat com>
Date:   Tue Aug 8 18:34:01 2017 +0200

    ASN.1 definitions of ECDSA and helper functions for DER encoding and decoding
    
    https://bugzilla.gnome.org/show_bug.cgi?id=641082

 egg/pk.asn                 |   22 ++++++
 egg/pk.asn.h               |   20 +++++-
 pkcs11/gkm/gkm-data-asn1.c |  121 ++++++++++++++++++++++++++++++--
 pkcs11/gkm/gkm-data-asn1.h |   26 +++++++
 pkcs11/gkm/gkm-data-der.c  |  165 ++++++++++++++++++++++++++++++++++++++++++++
 pkcs11/gkm/gkm-data-der.h  |   20 +++++
 6 files changed, 367 insertions(+), 7 deletions(-)
---
diff --git a/egg/pk.asn b/egg/pk.asn
index 347323c..17e3a5b 100644
--- a/egg/pk.asn
+++ b/egg/pk.asn
@@ -101,5 +101,27 @@ DHParameter ::= SEQUENCE {
        privateValueLength INTEGER OPTIONAL 
 }
 
+-- FROM New PKIX ASN.1 [RFC5912], RFC5915
+
+Parameters ::= CHOICE {
+       namedCurve    OBJECT IDENTIFIER,
+       implicitlyCA  NULL
+}
+
+-- bogus attribute used to encode Q into CKA_EC_POINT
+ECKeyQ ::= OCTET STRING
+
+ECPrivateKey ::= SEQUENCE {
+       version        INTEGER { ecPrivkeyVer1(1) }, -- should be 1
+       d     OCTET STRING, -- private key
+       parameters [0] Parameters OPTIONAL, -- OID
+       q  [1] BIT STRING OPTIONAL -- public key
+}
+
+-- can't find definition anywhere, but works
+ECPublicKey ::= SEQUENCE {
+       parameters [0] Parameters OPTIONAL, -- OID
+       q  [1] BIT STRING -- public key
+}
 
 END
diff --git a/egg/pk.asn.h b/egg/pk.asn.h
index 618ccca..53d547a 100644
--- a/egg/pk.asn.h
+++ b/egg/pk.asn.h
@@ -2,6 +2,7 @@
 # include "config.h"
 #endif
 
+/* Generated using  asn1Parser pk.asn -o pk.asn.h */
 /* #include <libtasn1.h> */
 
 const ASN1_ARRAY_TYPE pk_asn1_tab[] = {
@@ -63,9 +64,26 @@ const ASN1_ARRAY_TYPE pk_asn1_tab[] = {
   { "g", 1073741827, NULL },
   { "Y", 1073741827, NULL },
   { "priv", 3, NULL },
-  { "DHParameter", 536870917, NULL },
+  { "DHParameter", 1610612741, NULL },
   { "prime", 1073741827, NULL },
   { "base", 1073741827, NULL },
   { "privateValueLength", 16387, NULL },
+  { "Parameters", 1610612754, NULL },
+  { "namedCurve", 1073741836, NULL },
+  { "implicitlyCA", 20, NULL },
+  { "ECKeyQ", 1073741831, NULL },
+  { "ECPrivateKey", 1610612741, NULL },
+  { "version", 1610874883, NULL },
+  { "ecPrivkeyVer1", 1, "1"},
+  { "d", 1073741831, NULL },
+  { "parameters", 1610637314, "Parameters"},
+  { NULL, 2056, "0"},
+  { "q", 536895494, NULL },
+  { NULL, 2056, "1"},
+  { "ECPublicKey", 536870917, NULL },
+  { "parameters", 1610637314, "Parameters"},
+  { NULL, 2056, "0"},
+  { "q", 536879110, NULL },
+  { NULL, 2056, "1"},
   { NULL, 0, NULL }
 };
diff --git a/pkcs11/gkm/gkm-data-asn1.c b/pkcs11/gkm/gkm-data-asn1.c
index e063dd4..591bbe8 100644
--- a/pkcs11/gkm/gkm-data-asn1.c
+++ b/pkcs11/gkm/gkm-data-asn1.c
@@ -26,8 +26,8 @@
 
 #include "egg/egg-asn1x.h"
 
-gboolean
-gkm_data_asn1_read_mpi (GNode *asn, gcry_mpi_t *mpi)
+static gboolean
+gkm_data_asn1_read_mpi_internal (GNode *asn, gcry_mpi_t *mpi, GBytes *(*asn1_get)(GNode *))
 {
        gcry_error_t gcry;
        GBytes *buf;
@@ -36,7 +36,7 @@ gkm_data_asn1_read_mpi (GNode *asn, gcry_mpi_t *mpi)
        g_return_val_if_fail (asn, FALSE);
        g_return_val_if_fail (mpi, FALSE);
 
-       buf = egg_asn1x_get_integer_as_raw (asn);
+       buf = asn1_get (asn);
        if (!buf)
                return FALSE;
 
@@ -50,8 +50,8 @@ gkm_data_asn1_read_mpi (GNode *asn, gcry_mpi_t *mpi)
        return TRUE;
 }
 
-gboolean
-gkm_data_asn1_write_mpi (GNode *asn, gcry_mpi_t mpi)
+static gboolean
+gkm_data_asn1_write_mpi_internal (GNode *asn, gcry_mpi_t mpi, void (*asn1_set)(GNode *, GBytes *))
 {
        gcry_error_t gcry;
        GBytes *bytes;
@@ -72,8 +72,117 @@ gkm_data_asn1_write_mpi (GNode *asn, gcry_mpi_t mpi)
        g_return_val_if_fail (gcry == 0, FALSE);
 
        bytes = g_bytes_new_with_free_func (buf, len, gcry_free, buf);
-       egg_asn1x_set_integer_as_raw (asn, bytes);
+       asn1_set (asn, bytes);
        g_bytes_unref (bytes);
 
        return TRUE;
 }
+
+/* ECDSA private key (d) is OCTET STRING encoded MPI */
+gboolean
+gkm_data_asn1_read_string_mpi (GNode *asn, gcry_mpi_t *mpi)
+{
+       return gkm_data_asn1_read_mpi_internal (asn, mpi, egg_asn1x_get_string_as_bytes);
+}
+
+gboolean
+gkm_data_asn1_write_string_mpi (GNode *asn, gcry_mpi_t mpi)
+{
+       return gkm_data_asn1_write_mpi_internal (asn, mpi, egg_asn1x_set_string_as_bytes);
+}
+
+gboolean
+gkm_data_asn1_read_mpi (GNode *asn, gcry_mpi_t *mpi)
+{
+       return gkm_data_asn1_read_mpi_internal (asn, mpi, egg_asn1x_get_integer_as_raw);
+}
+
+gboolean
+gkm_data_asn1_write_mpi (GNode *asn, gcry_mpi_t mpi)
+{
+       return gkm_data_asn1_write_mpi_internal (asn, mpi, egg_asn1x_set_integer_as_raw);
+}
+
+/* ECDSA CKA_EC_POINT encodes q value as a OCTET STRING in PKCS#11 */
+gboolean
+gkm_data_asn1_read_string (GNode *asn, GBytes **data)
+{
+       GBytes *buf;
+
+       g_return_val_if_fail (asn, FALSE);
+       g_return_val_if_fail (data, FALSE);
+
+       buf = egg_asn1x_get_string_as_bytes (asn);
+       if (!buf)
+               return FALSE;
+
+       *data = buf;
+       return TRUE;
+}
+
+gboolean
+gkm_data_asn1_write_string (GNode *asn, GBytes *data)
+{
+       g_return_val_if_fail (asn, FALSE);
+       g_return_val_if_fail (data, FALSE);
+
+       egg_asn1x_set_string_as_bytes (asn, data);
+
+       return TRUE;
+}
+
+/* ECDSA public key (q) is encoded as a bit string in PEM files */
+gboolean
+gkm_data_asn1_read_bit_string (GNode *asn, GBytes **data, gsize *data_bits)
+{
+       GBytes *buf;
+       guint n_bits;
+
+       g_return_val_if_fail (asn, FALSE);
+       g_return_val_if_fail (data, FALSE);
+
+       buf = egg_asn1x_get_bits_as_raw (asn, &n_bits);
+       if (!buf)
+               return FALSE;
+
+       *data = buf;
+       *data_bits = n_bits;
+       return TRUE;
+}
+
+gboolean
+gkm_data_asn1_write_bit_string (GNode *asn, GBytes *data, gsize data_bits)
+{
+       g_return_val_if_fail (asn, FALSE);
+       g_return_val_if_fail (data, FALSE);
+
+       egg_asn1x_set_bits_as_raw (asn, data, data_bits);
+
+       return TRUE;
+}
+
+/* ECDSA differentiates curves based on the OID */
+gboolean
+gkm_data_asn1_read_oid (GNode *asn, GQuark *oid)
+{
+       GQuark q;
+
+       g_return_val_if_fail (asn, FALSE);
+       g_return_val_if_fail (oid, FALSE);
+
+       q = egg_asn1x_get_oid_as_quark (asn);
+       if (!q)
+               return FALSE;
+
+       *oid = q;
+       return TRUE;
+}
+
+gboolean
+gkm_data_asn1_write_oid (GNode *asn, GQuark oid)
+{
+       g_return_val_if_fail (asn, FALSE);
+       g_return_val_if_fail (oid, FALSE);
+
+       return egg_asn1x_set_oid_as_quark (asn, oid);
+}
diff --git a/pkcs11/gkm/gkm-data-asn1.h b/pkcs11/gkm/gkm-data-asn1.h
index 12826da..822fd08 100644
--- a/pkcs11/gkm/gkm-data-asn1.h
+++ b/pkcs11/gkm/gkm-data-asn1.h
@@ -32,4 +32,30 @@ gboolean           gkm_data_asn1_read_mpi                      (GNode *asn,
 gboolean           gkm_data_asn1_write_mpi                     (GNode *asn,
                                                                 gcry_mpi_t mpi);
 
+gboolean           gkm_data_asn1_read_string_mpi               (GNode *asn,
+                                                                gcry_mpi_t *mpi);
+
+gboolean           gkm_data_asn1_write_string_mpi              (GNode *asn,
+                                                                gcry_mpi_t mpi);
+
+gboolean           gkm_data_asn1_read_string                   (GNode *asn,
+                                                                GBytes **data);
+
+gboolean           gkm_data_asn1_write_string                  (GNode *asn,
+                                                                GBytes *data);
+
+gboolean           gkm_data_asn1_read_bit_string               (GNode *asn,
+                                                                GBytes **data,
+                                                                gsize *data_bits);
+
+gboolean           gkm_data_asn1_write_bit_string              (GNode *asn,
+                                                                GBytes *data,
+                                                                gsize data_bits);
+
+gboolean           gkm_data_asn1_read_oid                      (GNode *asn,
+                                                                GQuark *oid);
+
+gboolean           gkm_data_asn1_write_oid                     (GNode *asn,
+                                                                GQuark oid);
+
 #endif /*GKM_DATA_ASN_H_*/
diff --git a/pkcs11/gkm/gkm-data-der.c b/pkcs11/gkm/gkm-data-der.c
index 78c16ca..b21049c 100644
--- a/pkcs11/gkm/gkm-data-der.c
+++ b/pkcs11/gkm/gkm-data-der.c
@@ -43,7 +43,11 @@ EGG_SECURE_DECLARE (data_der);
 
 static GQuark OID_PKIX1_RSA;
 static GQuark OID_PKIX1_DSA;
+static GQuark OID_PKIX1_ECDSA;
 static GQuark OID_PKCS12_PBE_3DES_SHA1;
+static GQuark OID_ANSI_SECP256R1;
+static GQuark OID_ANSI_SECP384R1;
+static GQuark OID_ANSI_SECP521R1;
 
 static void
 init_quarks (void)
@@ -57,7 +61,11 @@ init_quarks (void)
 
                QUARK (OID_PKIX1_RSA, "1.2.840.113549.1.1.1");
                QUARK (OID_PKIX1_DSA, "1.2.840.10040.4.1");
+               QUARK (OID_PKIX1_ECDSA, "1.2.840.10045.2.1");
                QUARK (OID_PKCS12_PBE_3DES_SHA1, "1.2.840.113549.1.12.1.3");
+               QUARK (OID_ANSI_SECP256R1, "1.2.840.10045.3.1.7");
+               QUARK (OID_ANSI_SECP384R1, "1.3.132.0.34");
+               QUARK (OID_ANSI_SECP521R1, "1.3.132.0.35");
 
                #undef QUARK
 
@@ -65,6 +73,161 @@ init_quarks (void)
        }
 }
 
+const gchar *
+gkm_data_der_oid_to_curve (GQuark oid)
+{
+       if (oid == OID_ANSI_SECP256R1)
+               return "NIST P-256";
+       else if (oid == OID_ANSI_SECP384R1)
+               return "NIST P-384";
+       else if (oid == OID_ANSI_SECP521R1)
+               return "NIST P-521";
+       return NULL;
+}
+
+/*
+ * Convert ecc->curve values from libgcrypt representation to internal one.
+ * Ignore duplicates and alternative names, since S-expressions are created
+ * only by us throught this code.
+ */
+static GQuark
+gkm_data_der_curve_to_oid (const gchar *curve)
+{
+       if (g_str_equal (curve, "NIST P-256"))
+               return OID_ANSI_SECP256R1;
+       else if (g_str_equal (curve, "NIST P-384"))
+               return OID_ANSI_SECP384R1;
+       else if (g_str_equal (curve, "NIST P-521"))
+               return OID_ANSI_SECP521R1;
+       return 0;
+}
+
+
+GQuark
+gkm_data_der_oid_from_ec_params (GBytes *params)
+{
+       GNode *asn;
+       GQuark oid;
+
+       init_quarks ();
+
+       asn = egg_asn1x_create_and_decode (pk_asn1_tab, "Parameters", params);
+       if (!asn)
+               return 0;
+
+       oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "namedCurve", NULL));
+
+       egg_asn1x_destroy (asn);
+       return oid;
+}
+
+GBytes *
+gkm_data_der_get_ec_params (GQuark oid)
+{
+       GNode *asn;
+       GBytes *params = NULL;
+       GNode *named_curve;
+
+       asn = egg_asn1x_create (pk_asn1_tab, "Parameters");
+       if (!asn)
+               goto done;
+
+       named_curve = egg_asn1x_node (asn, "namedCurve", NULL);
+
+       if (!egg_asn1x_set_oid_as_quark (named_curve, oid))
+               goto done;
+
+       if (!egg_asn1x_set_choice (asn, named_curve))
+               goto done;
+
+       params = egg_asn1x_encode (asn, NULL);
+
+done:
+       egg_asn1x_destroy (asn);
+       return params;
+}
+
+/* wrapper so we do not have to export the GQuark magic */
+GBytes *
+gkm_data_der_curve_to_ec_params (const gchar *curve_name)
+{
+       GQuark oid;
+
+       init_quarks ();
+
+       oid = gkm_data_der_curve_to_oid (curve_name);
+       if (oid == 0)
+               return NULL;
+
+       return gkm_data_der_get_ec_params (oid);
+}
+
+GBytes *
+gkm_data_der_encode_ecdsa_q_str (const guchar *data, gsize data_len)
+{
+       GNode *asn = NULL;
+       GBytes *bytes, *result = NULL;
+
+       asn = egg_asn1x_create (pk_asn1_tab, "ECKeyQ");
+       g_return_val_if_fail (asn, FALSE);
+
+       bytes = g_bytes_new_static (data, data_len);
+
+       /* "consumes" bytes */
+       if (!gkm_data_asn1_write_string (asn, bytes))
+               goto done;
+
+       result = egg_asn1x_encode (asn, g_realloc);
+       if (result == NULL)
+               g_warning ("couldn't encode Q into the PKCS#11 structure: %s", egg_asn1x_message (asn));
+done:
+       egg_asn1x_destroy (asn);
+       return result;
+}
+
+gboolean
+gkm_data_der_encode_ecdsa_q (gcry_mpi_t q, GBytes **result)
+{
+       gcry_error_t gcry;
+       guchar data[1024];
+       gsize data_len = 1024;
+       gboolean rv = TRUE;
+
+       g_assert (q);
+       g_assert (result);
+
+       gcry = gcry_mpi_print (GCRYMPI_FMT_USG, data, data_len, &data_len, q);
+       g_return_val_if_fail (gcry == 0, FALSE);
+
+       *result = gkm_data_der_encode_ecdsa_q_str (data, data_len);
+       if (*result == NULL)
+               rv = FALSE;
+
+       return rv;
+}
+
+gboolean
+gkm_data_der_decode_ecdsa_q (GBytes *data, GBytes **result)
+{
+       GNode *asn = NULL;
+       gboolean rv = TRUE;
+
+       g_assert (data);
+       g_assert (result);
+
+       asn = egg_asn1x_create_and_decode (pk_asn1_tab, "ECKeyQ", data);
+       /* workaround a bug in gcr (not DER encoding the MPI) */
+       if (!asn) {
+               *result = data;
+               return rv;
+       }
+
+       rv = gkm_data_asn1_read_string (asn, result);
+
+       egg_asn1x_destroy (asn);
+       return rv;
+}
+
 /* -----------------------------------------------------------------------------
  * KEY PARSING
  */
@@ -518,6 +681,8 @@ gkm_data_der_read_private_pkcs8_plain (GBytes *data,
                algorithm = GCRY_PK_RSA;
        else if (key_algo == OID_PKIX1_DSA)
                algorithm = GCRY_PK_DSA;
+       else if (key_algo == OID_PKIX1_ECDSA)
+               algorithm = GCRY_PK_ECC;
 
        if (!algorithm) {
                ret = GKM_DATA_UNRECOGNIZED;
diff --git a/pkcs11/gkm/gkm-data-der.h b/pkcs11/gkm/gkm-data-der.h
index e496862..f0587db 100644
--- a/pkcs11/gkm/gkm-data-der.h
+++ b/pkcs11/gkm/gkm-data-der.h
@@ -44,6 +44,26 @@ GkmDataResult      gkm_data_der_read_private_key_dsa_parts   (GBytes *keydata,
                                                               GBytes *params,
                                                               gcry_sexp_t *s_key);
 
+const gchar *      gkm_data_der_oid_to_curve                 (GQuark oid);
+
+GQuark             gkm_data_der_oid_from_ec_params           (GBytes *params);
+
+GBytes *           gkm_data_der_get_ec_params                (GQuark oid);
+
+GBytes *           gkm_data_der_encode_ecdsa_q_str           (const guchar *data,
+                                                              gsize data_len);
+
+gboolean           gkm_data_der_encode_ecdsa_q               (gcry_mpi_t q,
+                                                              GBytes **result);
+
+gboolean           gkm_data_der_decode_ecdsa_q               (GBytes *data,
+                                                              GBytes **result);
+
+GBytes *           gkm_data_der_curve_to_ec_params           (const gchar *curve_name);
+
+GkmDataResult      gkm_data_der_read_private_key_ecdsa       (GBytes *data,
+                                                              gcry_sexp_t *s_key);
+
 GkmDataResult      gkm_data_der_read_private_key             (GBytes *data,
                                                               gcry_sexp_t *s_key);
 


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