[gcr/ecc: 3/3] WIP ECDSA



commit a00705408a7f58630584d72b1bc93f9ae52c7354
Author: Stef Walter <stefw redhat com>
Date:   Thu Apr 17 06:51:09 2014 +0200

    WIP ECDSA

 egg/egg-asn1x.c              |   34 ++++--
 egg/egg-asn1x.h              |    3 +
 egg/egg-oid.c                |    7 +
 gck/gck-attributes.c         |    4 +-
 gcr/fixtures/cert-ecc521.pem |   19 +++
 gcr/gcr-oids.list            |   23 ++++
 gcr/gcr-openssh.c            |   64 +++++++++++
 gcr/gcr-parser.c             |  100 +++++++++++++++++
 gcr/gcr-subject-public-key.c |  248 ++++++++++++++++++++++++++++++++++++++++++
 gcr/gcr-subject-public-key.h |    2 +
 gcr/gcr-types.h              |    2 +
 ui/gcr-crypto-types.xml      |    1 +
 ui/gcr-key-renderer.c        |   68 ++---------
 ui/gcr-key-widget.c          |    4 +-
 14 files changed, 508 insertions(+), 71 deletions(-)
---
diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c
index 0daf2ef..55135be 100644
--- a/egg/egg-asn1x.c
+++ b/egg/egg-asn1x.c
@@ -2910,23 +2910,18 @@ egg_asn1x_get_integer_as_raw (GNode *node)
 }
 
 GBytes *
-egg_asn1x_get_integer_as_usg (GNode *node)
+egg_asn1x_data_to_usg (GBytes *data,
+                       gboolean already_unsigned)
 {
-       const guchar *p;
-       Anode *an;
+       const guint8 *p;
        gboolean sign;
        gsize len;
 
-       g_return_val_if_fail (node != NULL, FALSE);
-       g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER, FALSE);
-
-       an = node->data;
-       if (an->value == NULL)
-               return NULL;
+       g_return_val_if_fail (data != NULL, NULL);
 
-       p = g_bytes_get_data (an->value, &len);
+       p = g_bytes_get_data (data, &len);
 
-       if (!an->guarantee_unsigned) {
+       if (!already_unsigned) {
                sign = !!(p[0] & 0x80);
                if (sign) {
                        g_warning ("invalid two's complement integer"); /* UNREACHABLE: */
@@ -2945,7 +2940,22 @@ egg_asn1x_get_integer_as_usg (GNode *node)
 
        return g_bytes_new_with_free_func (p, len,
                                           (GDestroyNotify)g_bytes_unref,
-                                          g_bytes_ref (an->value));
+                                          g_bytes_ref (data));
+}
+
+GBytes *
+egg_asn1x_get_integer_as_usg (GNode *node)
+{
+       Anode *an;
+
+       g_return_val_if_fail (node != NULL, NULL);
+       g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER, NULL);
+
+       an = node->data;
+       if (an->value == NULL)
+               return NULL;
+
+       return egg_asn1x_data_to_usg (an->value, an->guarantee_unsigned);
 }
 
 void
diff --git a/egg/egg-asn1x.h b/egg/egg-asn1x.h
index 0cda783..04e5fea 100644
--- a/egg/egg-asn1x.h
+++ b/egg/egg-asn1x.h
@@ -265,6 +265,9 @@ gconstpointer       egg_asn1x_element_content        (const guchar *data,
                                                       gsize n_data,
                                                       gsize *n_content);
 
+GBytes *            egg_asn1x_data_to_usg            (GBytes *data,
+                                                             gboolean already_unsigned);
+
 #define             egg_asn1x_assert(expr, node) \
        do { if G_LIKELY(expr) ; else \
                g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
diff --git a/egg/egg-oid.c b/egg/egg-oid.c
index 75d909e..e604846 100644
--- a/egg/egg-oid.c
+++ b/egg/egg-oid.c
@@ -102,6 +102,13 @@ static OidInfo oid_info[] = {
        { 0, "1.2.840.10040.4.1", "dsa", N_("DSA"), 0 },
        { 0, "1.2.840.10040.4.3", "sha1WithDSA", N_("SHA1 with DSA"), 0 },
 
+       { 0, "1.2.840.10045.2.1", "ec", N_("Elliptic Curve"), 0, },
+       { 0, "1.2.840.10045.4.1", "sha1WithECDSA", N_("SHA1 with ECDSA"), 0 },
+       { 0, "1.2.840.10045.4.3.1", "sha224WithECDSA", N_("SHA224 with ECDSA"), 0 },
+       { 0, "1.2.840.10045.4.3.2", "sha256WithECDSA", N_("SHA256 with ECDSA"), 0 },
+       { 0, "1.2.840.10045.4.3.3", "sha384WithECDSA", N_("SHA384 with ECDSA"), 0 },
+       { 0, "1.2.840.10045.4.3.4", "sha512WithECDSA", N_("SHA512 with ECDSA"), 0 },
+
        /* Extended Key Usages */
        { 0, "1.3.6.1.5.5.7.3.1", NULL, N_("Server Authentication"), 0 },
        { 0, "1.3.6.1.5.5.7.3.2", NULL, N_("Client Authentication"), 0 },
diff --git a/gck/gck-attributes.c b/gck/gck-attributes.c
index 81716a2..91a194a 100644
--- a/gck/gck-attributes.c
+++ b/gck/gck-attributes.c
@@ -2605,8 +2605,8 @@ _gck_format_key_type (GString *output,
        X (CKK_RSA)
        X (CKK_DSA)
        X (CKK_DH)
-       /* X (CKK_ECDSA) */
-       X (CKK_EC)
+       X (CKK_ECDSA)
+       /* X (CKK_EC) */
        X (CKK_X9_42_DH)
        X (CKK_KEA)
        X (CKK_GENERIC_SECRET)
diff --git a/gcr/fixtures/cert-ecc521.pem b/gcr/fixtures/cert-ecc521.pem
new file mode 100644
index 0000000..3fc1778
--- /dev/null
+++ b/gcr/fixtures/cert-ecc521.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDJDCCAsmgAwIBAgIBBzAKBggqhkjOPQQDAjB9MQswCQYDVQQGEwJCRTEPMA0G
+A1UEChMGR251VExTMSUwIwYDVQQLExxHbnVUTFMgY2VydGlmaWNhdGUgYXV0aG9y
+aXR5MQ8wDQYDVQQIEwZMZXV2ZW4xJTAjBgNVBAMTHEdudVRMUyBjZXJ0aWZpY2F0
+ZSBhdXRob3JpdHkwIhgPMjAxMjA5MDEwOTIyMjRaGA8yMDE5MTAwNTA5MjIyNFow
+gbgxCzAJBgNVBAYTAkdSMRIwEAYDVQQKEwlLb2tvIGluYy4xFzAVBgNVBAsTDnNs
+ZWVwaW5nIGRlcHQuMQ8wDQYDVQQIEwZBdHRpa2kxFTATBgNVBAMTDENpbmR5IExh
+dXBlcjEXMBUGCgmSJomT8ixkAQETB2NsYXVwZXIxDDAKBgNVBAwTA0RyLjEPMA0G
+A1UEQRMGamFja2FsMRwwGgYJKoZIhvcNAQkBFg1ub25lQG5vbmUub3JnMIGbMBAG
+ByqGSM49AgEGBSuBBAAjA4GGAAQAoapA9bLQHQiI8V2mIzs9sq80VR4FBB0TBOSx
+GqBOE3FSzHAejQkIKc/1pW0v0wKvapYMq/RrfhPJxPkjTPtztUsAkU//9E0/aoEW
+VC6Rqf+VX3wIhe7+RS8JXdBh9SM0+Z9MCRUiM8K9qPMtpNgB2ks7T5BGFHSMlNKm
+uLW1agWPy5CjgbYwgbMwDAYDVR0TAQH/BAIwADA9BgNVHREENjA0ggx3d3cubm9u
+ZS5vcmeCE3d3dy5tb3JldGhhbm9uZS5vcmeCCWxvY2FsaG9zdIcEwKgBATATBgNV
+HSUEDDAKBggrBgEFBQcDATAPBgNVHQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBTagKMW
+kYyqTJk/RRjg++gqz6xX6zAfBgNVHSMEGDAWgBTwtIH+mBK/tSi5ZEADy8wfZk4o
+AzAKBggqhkjOPQQDAgNJADBGAiEAoj/ZB98cG/FaA7VVU+R6+TT3icF+De61rfim
+R43VMlUCIQCXjG9gRp0x+/8vCRL0/nr0a32SRPruKVDqbHnNiWchsg==
+-----END CERTIFICATE-----
diff --git a/gcr/gcr-oids.list b/gcr/gcr-oids.list
index b49f4bd..bee64b8 100644
--- a/gcr/gcr-oids.list
+++ b/gcr/gcr-oids.list
@@ -10,6 +10,12 @@ PKIX1_RSA                    1.2.840.113549.1.1.1
 PKIX1_SHA1_WITH_RSA            1.2.840.113549.1.1.5
 PKIX1_DSA                      1.2.840.10040.4.1
 PKIX1_SHA1_WITH_DSA            1.2.840.10040.4.3
+PKIX1_EC                        1.2.840.10045.2.1
+PKIX1_SHA1_WITH_ECDSA           1.2.840.10045.4.1
+PKIX1_SHA224_WITH_ECDSA         1.2.840.10045.4.3.1
+PKIX1_SHA256_WITH_ECDSA         1.2.840.10045.4.3.2
+PKIX1_SHA384_WITH_ECDSA         1.2.840.10045.4.3.3
+PKIX1_SHA512_WITH_ECDSA         1.2.840.10045.4.3.4
 
 PKCS7_DATA                     1.2.840.113549.1.7.1
 PKCS7_SIGNED_DATA              1.2.840.113549.1.7.2
@@ -24,3 +30,20 @@ PKCS12_BAG_CRL                       1.2.840.113549.1.12.10.1.4
 
 ALT_NAME_XMPP_ADDR             1.3.6.1.5.5.7.8.5
 ALT_NAME_DNS_SRV               1.3.6.1.5.5.7.8.7
+
+EC_SECP192R1                    1.2.840.10045.3.1.1
+EC_SECT163K1                    1.3.132.0.1
+EC_SECT163R2                    1.3.132.0.15
+EC_SECP224R1                    1.3.132.0.33
+EC_SECT233K1                    1.3.132.0.26
+EC_SECT233R1                    1.3.132.0.27
+EC_SECP256R1                    1.2.840.10045.3.1.7
+EC_SECT283K1                    1.3.132.0.16
+EC_SECT283R1                    1.3.132.0.17
+EC_SECP384R1                    1.3.132.0.34
+EC_SECT409K1                    1.3.132.0.36
+EC_SECT409R1                    1.3.132.0.37
+EC_SECP521R1                    1.3.132.0.35
+EC_SECP571K1                    1.3.132.0.38
+EC_SECT571R1                    1.3.132.0.39
+
diff --git a/gcr/gcr-openssh.c b/gcr/gcr-openssh.c
index b8a0b39..f4ca9dc 100644
--- a/gcr/gcr-openssh.c
+++ b/gcr/gcr-openssh.c
@@ -25,6 +25,10 @@
 #include "gcr-internal.h"
 #include "gcr-types.h"
 
+#include "gcr/gcr-oids.h"
+
+#include "egg/egg-asn1x.h"
+#include "egg/egg-asn1-defs.h"
 #include "egg/egg-buffer.h"
 #include "egg/egg-decimal.h"
 
@@ -116,6 +120,8 @@ keytype_to_algo (const gchar *algo,
                return CKK_RSA;
        else if (match_word (algo, length, "ssh-dss"))
                return CKK_DSA;
+       else if (match_word (algo, length, "ssh-ecdsa"))
+               return CKK_ECDSA;
        return G_MAXULONG;
 }
 
@@ -291,6 +297,61 @@ read_v2_public_rsa (EggBuffer *buffer,
 }
 
 static gboolean
+read_v2_public_ecdsa (EggBuffer *buffer,
+                      gsize *offset,
+                      GckBuilder *builder)
+{
+       gconstpointer data;
+       GBytes *bytes;
+       gboolean ret;
+       GNode *asn;
+       gchar *curve;
+       GQuark oid;
+       gsize len;
+
+       /* The named curve */
+       if (!egg_buffer_get_string (buffer, *offset, offset,
+                                   &curve, (EggBufferAllocator)g_realloc))
+               return FALSE;
+
+       if (g_strcmp0 (curve, "nistp256") == 0) {
+               oid = GCR_OID_EC_SECP256R1;
+       } else if (g_strcmp0 (curve, "nistp384") == 0) {
+               oid = GCR_OID_EC_SECP384R1;
+       } else if (g_strcmp0 (curve, "nistp521") == 0) {
+               oid = GCR_OID_EC_SECP521R1;
+       } else {
+               g_free (curve);
+               g_message ("unknown or unsupported curve in ssh public key");
+               return FALSE;
+       }
+
+       g_free (curve);
+
+       asn = egg_asn1x_create (pk_asn1_tab, "ECParameters");
+       g_return_val_if_fail (asn != NULL, FALSE);
+
+       ret = egg_asn1x_set_oid_as_quark (egg_asn1x_node (asn, "namedCurve", NULL), oid);
+       g_return_val_if_fail (ret == TRUE, FALSE);
+
+       bytes = egg_asn1x_encode (asn, g_realloc);
+       g_return_val_if_fail (bytes != NULL, FALSE);
+       egg_asn1x_destroy (asn);
+
+       data = g_bytes_get_data (bytes, &len);
+       gck_builder_add_data (builder, CKA_EC_PARAMS, data, len);
+       g_bytes_unref (bytes);
+
+       if (!read_buffer_mpi (buffer, offset, builder, CKA_EC_POINT))
+               return FALSE;
+
+       gck_builder_add_ulong (builder, CKA_KEY_TYPE, CKK_ECDSA);
+       gck_builder_add_ulong (builder, CKA_CLASS, CKO_PUBLIC_KEY);
+
+       return TRUE;
+}
+
+static gboolean
 read_v2_public_key (gulong algo,
                     gconstpointer data,
                     gsize n_data,
@@ -326,6 +387,9 @@ read_v2_public_key (gulong algo,
        case CKK_DSA:
                ret = read_v2_public_dsa (&buffer, &offset, builder);
                break;
+       case CKK_ECDSA:
+               ret = read_v2_public_ecdsa (&buffer, &offset, builder);
+               break;
        default:
                g_assert_not_reached ();
                break;
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index 4c39a1a..50b39ad 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -171,6 +171,7 @@ EGG_SECURE_DECLARE (parser);
 static GQuark PEM_CERTIFICATE;
 static GQuark PEM_RSA_PRIVATE_KEY;
 static GQuark PEM_DSA_PRIVATE_KEY;
+static GQuark PEM_ECDSA_PRIVATE_KEY;
 static GQuark PEM_ANY_PRIVATE_KEY;
 static GQuark PEM_ENCRYPTED_PRIVATE_KEY;
 static GQuark PEM_PRIVATE_KEY;
@@ -195,6 +196,7 @@ init_quarks (void)
                QUARK (PEM_PRIVATE_KEY, "PRIVATE KEY");
                QUARK (PEM_RSA_PRIVATE_KEY, "RSA PRIVATE KEY");
                QUARK (PEM_DSA_PRIVATE_KEY, "DSA PRIVATE KEY");
+               QUARK (PEM_ECDSA_PRIVATE_KEY, "ECDSA PRIVATE KEY");
                QUARK (PEM_ANY_PRIVATE_KEY, "ANY PRIVATE KEY");
                QUARK (PEM_ENCRYPTED_PRIVATE_KEY, "ENCRYPTED PRIVATE KEY");
                QUARK (PEM_PKCS7, "PKCS7");
@@ -609,6 +611,59 @@ done:
        pop_parsed (self, parsed);
        return ret;
 }
+/* -----------------------------------------------------------------------------
+ * EC PRIVATE KEY
+ */
+
+static gint
+parse_der_private_key_ec (GcrParser *self,
+                          GBytes *data)
+{
+       gint ret = GCR_ERROR_UNRECOGNIZED;
+       GNode *asn = NULL;
+       GBytes *bytes = NULL;
+       GBytes *value = NULL;
+       GcrParsed *parsed;
+
+       parsed = push_parsed (self, TRUE);
+
+       asn = egg_asn1x_create_and_decode (pk_asn1_tab, "ECPrivateKey", data);
+       if (!asn)
+               goto done;
+
+       parsing_block (parsed, GCR_FORMAT_DER_PRIVATE_KEY_EC, data);
+       parsing_object (parsed, CKO_PRIVATE_KEY);
+       parsed_ulong_attribute (parsed, CKA_KEY_TYPE, CKK_EC);
+       parsed_boolean_attribute (parsed, CKA_PRIVATE, CK_TRUE);
+       ret = GCR_ERROR_FAILURE;
+
+       if (!parsed_asn1_element (parsed, asn, "parameters", CKA_EC_PARAMS))
+               goto done;
+
+       bytes = egg_asn1x_get_string_as_bytes (egg_asn1x_node (asn, "privateKey", NULL));
+       if (!bytes)
+               goto done;
+
+       value = egg_asn1x_data_to_usg (bytes, FALSE);
+       if (!value)
+               goto done;
+
+       parsed_attribute_bytes (parsed, CKA_VALUE, value);
+       parsed_fire (self, parsed);
+       ret = SUCCESS;
+
+done:
+       if (value)
+               g_bytes_unref (value);
+       if (bytes)
+               g_bytes_unref (bytes);
+       egg_asn1x_destroy (asn);
+       if (ret == GCR_ERROR_FAILURE)
+               g_message ("invalid EC key");
+
+       pop_parsed (self, parsed);
+       return ret;
+}
 
 /* -----------------------------------------------------------------------------
  * PRIVATE KEY
@@ -623,6 +678,8 @@ parse_der_private_key (GcrParser *self,
        res = parse_der_private_key_rsa (self, data);
        if (res == GCR_ERROR_UNRECOGNIZED)
                res = parse_der_private_key_dsa (self, data);
+       if (res == GCR_ERROR_UNRECOGNIZED)
+               res = parse_der_private_key_ec (self, data);
 
        return res;
 }
@@ -692,6 +749,25 @@ done:
 }
 
 static gint
+handle_subject_public_key_ec (GcrParser *self,
+                              GcrParsed *parsed,
+                              GBytes *key,
+                              GNode *params)
+{
+       GBytes *bytes;
+
+       parsing_object (parsed, CKO_PUBLIC_KEY);
+       parsed_ulong_attribute (parsed, CKA_KEY_TYPE, CKK_EC);
+
+       bytes = egg_asn1x_encode (params, g_realloc);
+       parsed_attribute_bytes (parsed, CKA_EC_PARAMS, bytes);
+       parsed_attribute_bytes (parsed, CKA_EC_POINT, key);
+       g_bytes_unref (bytes);
+
+       return SUCCESS;
+}
+
+static gint
 parse_der_subject_public_key (GcrParser *self,
                               GBytes *data)
 {
@@ -725,6 +801,9 @@ parse_der_subject_public_key (GcrParser *self,
        else if (oid == GCR_OID_PKIX1_DSA)
                ret = handle_subject_public_key_dsa (self, parsed, key, params);
 
+       else if (oid == GCR_OID_PKIX1_EC)
+               ret = handle_subject_public_key_ec (self, parsed, key, params);
+
        else
                ret = GCR_ERROR_UNRECOGNIZED;
 
@@ -773,6 +852,8 @@ parse_der_pkcs8_plain (GcrParser *self,
                key_type = CKK_RSA;
        else if (key_algo == GCR_OID_PKIX1_DSA)
                key_type = CKK_DSA;
+       else if (key_algo == GCR_OID_PKIX1_EC)
+               key_type = CKK_EC;
 
        if (key_type == GCK_INVALID) {
                ret = GCR_ERROR_UNRECOGNIZED;
@@ -801,6 +882,10 @@ done:
                        if (ret == GCR_ERROR_UNRECOGNIZED && params)
                                ret = parse_der_private_key_dsa_parts (self, keydata, params);
                        break;
+               case CKK_EC:
+                       ret = parse_der_private_key_ec (self, keydata);
+                       break;
+
                default:
                        g_message ("invalid or unsupported key type in PKCS#8 key");
                        ret = GCR_ERROR_UNRECOGNIZED;
@@ -1723,6 +1808,9 @@ formats_for_armor_type (GQuark armor_type,
        } else if (armor_type == PEM_DSA_PRIVATE_KEY) {
                *inner_format = GCR_FORMAT_DER_PRIVATE_KEY_DSA;
                *outer_format = GCR_FORMAT_PEM_PRIVATE_KEY_DSA;
+       } else if (armor_type == PEM_ECDSA_PRIVATE_KEY) {
+               *inner_format = GCR_FORMAT_DER_PRIVATE_KEY_EC;
+               *outer_format = GCR_FORMAT_PEM_PRIVATE_KEY_ECDSA;
        } else if (armor_type == PEM_ANY_PRIVATE_KEY) {
                *inner_format = GCR_FORMAT_DER_PRIVATE_KEY;
                *outer_format = GCR_FORMAT_PEM_PRIVATE_KEY;
@@ -1934,6 +2022,13 @@ parse_pem_private_key_dsa (GcrParser *self,
 }
 
 static gint
+parse_pem_private_key_ecdsa (GcrParser *self,
+                             GBytes *data)
+{
+       return handle_pem_format (self, GCR_FORMAT_DER_PRIVATE_KEY_EC, data);
+}
+
+static gint
 parse_pem_certificate (GcrParser *self,
                        GBytes *data)
 {
@@ -2028,6 +2123,7 @@ parse_openssh_public (GcrParser *self,
  * @GCR_FORMAT_DER_PRIVATE_KEY: DER encoded private key
  * @GCR_FORMAT_DER_PRIVATE_KEY_RSA: DER encoded RSA private key
  * @GCR_FORMAT_DER_PRIVATE_KEY_DSA: DER encoded DSA private key
+ * @GCR_FORMAT_DER_PRIVATE_KEY_EC: DER encoded EC private key
  * @GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY: DER encoded SubjectPublicKeyInfo
  * @GCR_FORMAT_DER_CERTIFICATE_X509: DER encoded X.509 certificate
  * @GCR_FORMAT_DER_PKCS7: DER encoded PKCS\#7 container file which can contain certificates
@@ -2043,6 +2139,7 @@ parse_openssh_public (GcrParser *self,
  * @GCR_FORMAT_PEM_PRIVATE_KEY: An OpenSSL style PEM file with a private key
  * @GCR_FORMAT_PEM_PRIVATE_KEY_RSA: An OpenSSL style PEM file with a private RSA key
  * @GCR_FORMAT_PEM_PRIVATE_KEY_DSA: An OpenSSL style PEM file with a private DSA key
+ * @GCR_FORMAT_PEM_PRIVATE_KEY_ECDSA: An OpenSSL style PEM file with a private ECDSA key
  * @GCR_FORMAT_PEM_CERTIFICATE_X509: An OpenSSL style PEM file with an X.509 certificate
  * @GCR_FORMAT_PEM_PKCS7: An OpenSSL style PEM file containing PKCS\#7
  * @GCR_FORMAT_PEM_PKCS8_PLAIN: Unencrypted OpenSSL style PEM file containing PKCS\#8
@@ -2065,6 +2162,7 @@ static const ParserFormat parser_normal[] = {
        { GCR_FORMAT_BASE64_SPKAC, parse_base64_spkac },
        { GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
        { GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
+       { GCR_FORMAT_DER_PRIVATE_KEY_EC, parse_der_private_key_ec },
        { GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY, parse_der_subject_public_key },
        { GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
        { GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
@@ -2083,6 +2181,7 @@ static const ParserFormat parser_formats[] = {
        { GCR_FORMAT_DER_PRIVATE_KEY, parse_der_private_key },
        { GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
        { GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
+       { GCR_FORMAT_DER_PRIVATE_KEY_EC, parse_der_private_key_ec },
        { GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY, parse_der_subject_public_key },
        { GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
        { GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
@@ -2105,6 +2204,7 @@ static const ParserFormat parser_formats[] = {
        { GCR_FORMAT_PEM_PKCS8_ENCRYPTED, parse_pem_pkcs8_encrypted },
        { GCR_FORMAT_PEM_PKCS12, parse_pem_pkcs12 },
        { GCR_FORMAT_PEM_PKCS10, parse_pem_pkcs10 },
+       { GCR_FORMAT_PEM_PRIVATE_KEY_ECDSA, parse_pem_private_key_ecdsa },
 };
 
 static int
diff --git a/gcr/gcr-subject-public-key.c b/gcr/gcr-subject-public-key.c
index 5b7dc28..e0c9e9d 100644
--- a/gcr/gcr-subject-public-key.c
+++ b/gcr/gcr-subject-public-key.c
@@ -293,6 +293,45 @@ load_dsa_attributes (GckObject *object,
 }
 
 static gboolean
+check_ec_attributes (GckBuilder *builder)
+{
+       const GckAttribute *ec_params;
+
+       ec_params = gck_builder_find (builder, CKA_EC_PARAMS);
+       return (ec_params && !gck_attribute_is_invalid (ec_params));
+}
+
+
+static gboolean
+load_ec_attributes (GckObject *object,
+                    GckBuilder *builder,
+                    GCancellable *cancellable,
+                    GError **lerror)
+{
+       gulong attr_types[] = { CKA_MODULUS, CKA_PUBLIC_EXPONENT };
+       GckAttributes *attrs;
+       GError *error = NULL;
+
+       if (check_rsa_attributes (builder)) {
+               _gcr_debug ("rsa attributes already loaded");
+               return TRUE;
+       }
+
+       attrs = gck_object_cache_lookup (object, attr_types, G_N_ELEMENTS (attr_types),
+                                        cancellable, &error);
+       if (error != NULL) {
+               _gcr_debug ("couldn't load rsa attributes: %s", error->message);
+               g_propagate_error (lerror, error);
+               return FALSE;
+       }
+
+       gck_builder_set_all (builder, attrs);
+       gck_attributes_unref (attrs);
+
+       return check_rsa_attributes (builder);
+}
+
+static gboolean
 load_attributes (GckObject *object,
                  GckBuilder *builder,
                  GCancellable *cancellable,
@@ -328,6 +367,14 @@ load_attributes (GckObject *object,
                case CKK_DSA:
                        ret = load_dsa_attributes (object, builder, cancellable, lerror);
                        break;
+               case CKK_EC:
+                       if (klass == CKO_PRIVATE_KEY) {
+                               _gcr_debug ("cannot convert elliptic curve private key to public");
+                               ret = FALSE;
+                       } else {
+                               ret = load_ec_attributes (object, builder, cancellable, lerror);
+                       }
+                       break;
                default:
                        _gcr_debug ("unsupported key type: %lu", type);
                        break;
@@ -373,6 +420,8 @@ check_attributes (GckBuilder *builder)
                        return check_rsa_attributes (builder);
                case CKK_DSA:
                        return check_dsa_attributes (builder);
+               case CKK_EC:
+                       return check_ec_attributes (builder);
                default:
                        return FALSE;
                }
@@ -681,6 +730,55 @@ dsa_subject_public_key_from_attributes (GckAttributes *attrs,
        return TRUE;
 }
 
+static gboolean
+ec_subject_public_key_from_attributes (GckAttributes *attrs,
+                                       gulong klass,
+                                       GNode *info_asn)
+{
+       const GckAttribute *ec_params, *ec_point;
+       GNode *key_asn, *params_asn;
+       GBytes *bytes;
+
+       if (klass == CKO_PRIVATE_KEY)
+               return FALSE;
+
+       ec_params = gck_attributes_find (attrs, CKA_EC_PARAMS);
+       ec_point = gck_attributes_find (attrs, CKA_EC_POINT);
+
+       if (ec_params == NULL || gck_attribute_is_invalid (ec_params) ||
+           ec_point == NULL || gck_attribute_is_invalid (ec_point))
+               return FALSE;
+
+       bytes = g_bytes_new_with_free_func (ec_params->value, ec_params->length,
+                                           gck_attributes_unref, gck_attributes_ref (attrs));
+       params_asn = egg_asn1x_create_and_decode (pk_asn1_tab, "ECParameters", bytes);
+       g_bytes_unref (bytes);
+
+       if (params_asn == NULL)
+               return FALSE;
+
+       key_asn = egg_asn1x_create (pk_asn1_tab, "ECPoint");
+       g_return_val_if_fail (key_asn, FALSE);
+
+       bytes = g_bytes_new_with_free_func (ec_point->value, ec_point->length,
+                                           gck_attributes_unref, gck_attributes_ref (attrs));
+       egg_asn1x_set_string_as_bytes (key_asn, bytes);
+       g_bytes_unref (bytes);
+
+       bytes = egg_asn1x_encode (key_asn, NULL);
+       egg_asn1x_destroy (key_asn);
+
+       egg_asn1x_set_bits_as_raw (egg_asn1x_node (info_asn, "subjectPublicKey", NULL),
+                                  bytes, g_bytes_get_size (bytes) * 8);
+       egg_asn1x_set_any_from (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params_asn);
+
+       egg_asn1x_set_oid_as_quark (egg_asn1x_node (info_asn, "algorithm", "algorithm", NULL), 
GCR_OID_PKIX1_EC);
+
+       g_bytes_unref (bytes);
+       egg_asn1x_destroy (params_asn);
+       return TRUE;
+}
+
 static GNode *
 cert_subject_public_key_from_attributes (GckAttributes *attributes)
 {
@@ -747,6 +845,9 @@ _gcr_subject_public_key_for_attributes (GckAttributes *attributes)
                } else if (key_type == CKK_DSA) {
                        ret = dsa_subject_public_key_from_attributes (attributes, klass, asn);
 
+               } else if (key_type == CKK_ECDSA) {
+                       ret = ec_subject_public_key_from_attributes (attributes, klass, asn);
+
                } else {
                        _gcr_debug ("unsupported key type: %lu", key_type);
                        ret = FALSE;
@@ -786,6 +887,24 @@ calculate_rsa_key_size (GBytes *data)
 }
 
 static guint
+attributes_rsa_key_size (GckAttributes *attrs)
+{
+       const GckAttribute *attr;
+       gulong bits;
+
+       attr = gck_attributes_find (attrs, CKA_MODULUS);
+
+       /* Calculate the bit length, and remove the complement */
+       if (attr != NULL)
+               return (attr->length / 2) * 2 * 8;
+
+       if (gck_attributes_find_ulong (attrs, CKA_MODULUS_BITS, &bits))
+               return (gint)bits;
+
+       return 0;
+}
+
+static guint
 calculate_dsa_params_size (GNode *params)
 {
        GNode *asn;
@@ -808,6 +927,110 @@ calculate_dsa_params_size (GNode *params)
        return key_size;
 }
 
+static guint
+attributes_dsa_key_size (GckAttributes *attrs)
+{
+       const GckAttribute *attr;
+       gulong bits;
+
+       attr = gck_attributes_find (attrs, CKA_PRIME);
+
+       /* Calculate the bit length, and remove the complement */
+       if (attr != NULL)
+               return (attr->length / 2) * 2 * 8;
+
+       if (gck_attributes_find_ulong (attrs, CKA_PRIME_BITS, &bits))
+               return (gint)bits;
+
+       return 0;
+}
+
+static guint
+named_curve_size (GNode *params)
+{
+       GQuark oid;
+       guint size;
+
+       oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (params, "namedCurve", NULL));
+
+       if (oid == GCR_OID_EC_SECP192R1)
+               size = 192;
+       else if (oid == GCR_OID_EC_SECT163K1)
+               size = 163;
+       else if (oid == GCR_OID_EC_SECT163R2)
+               size = 163;
+       else if (GCR_OID_EC_SECP224R1)
+               size = 224;
+       else if (GCR_OID_EC_SECT233K1)
+               size = 233;
+       else if (GCR_OID_EC_SECT233R1)
+               size = 233;
+       else if (GCR_OID_EC_SECP256R1)
+               size = 256;
+       else if (GCR_OID_EC_SECT283K1)
+               size = 283;
+       else if (GCR_OID_EC_SECT283R1)
+               size = 283;
+       else if (GCR_OID_EC_SECP384R1)
+               size = 384;
+       else if (GCR_OID_EC_SECT409K1)
+               size = 409;
+       else if (GCR_OID_EC_SECT409R1)
+               size = 409;
+       else if (GCR_OID_EC_SECP521R1)
+               size = 521;
+       else if (GCR_OID_EC_SECP571K1)
+               size = 571;
+       else if (GCR_OID_EC_SECT571R1)
+               size = 571;
+       else
+               size = 0;
+
+       return size;
+
+}
+
+static guint
+calculate_ec_params_size (GNode *params)
+{
+       GNode *asn;
+       guint size;
+
+       asn = egg_asn1x_get_any_as (params, pk_asn1_tab, "ECParameters");
+       g_return_val_if_fail (asn, 0);
+
+       size = named_curve_size (asn);
+       egg_asn1x_destroy (asn);
+
+       return size;
+}
+
+static guint
+attributes_ec_params_size (GckAttributes *attrs)
+{
+       GNode *asn;
+       const GckAttribute *attr;
+       GBytes *bytes;
+       guint size = 0;
+
+       attr = gck_attributes_find (attrs, CKA_EC_PARAMS);
+
+       /* Calculate the bit length, and remove the complement */
+       if (attr && !gck_attribute_is_invalid (attr)) {
+               bytes = g_bytes_new_with_free_func (attr->value, attr->length,
+                                                   gck_attributes_unref,
+                                                   gck_attributes_ref (attrs));
+               asn = egg_asn1x_create_and_decode (pk_asn1_tab, "ECParameters", bytes);
+               g_bytes_unref (bytes);
+
+               if (asn)
+                       size = named_curve_size (asn);
+               egg_asn1x_destroy (asn);
+       }
+
+       return size;
+}
+
 guint
 _gcr_subject_public_key_calculate_size (GNode *subject_public_key)
 {
@@ -834,9 +1057,34 @@ _gcr_subject_public_key_calculate_size (GNode *subject_public_key)
                params = egg_asn1x_node (subject_public_key, "algorithm", "parameters", NULL);
                key_size = calculate_dsa_params_size (params);
 
+       } else if (oid == GCR_OID_PKIX1_EC) {
+               params = egg_asn1x_node (subject_public_key, "algorithm", "parameters", NULL);
+               key_size = calculate_ec_params_size (params);
+
        } else {
                g_message ("unsupported key algorithm: %s", g_quark_to_string (oid));
        }
 
        return key_size;
 }
+
+guint
+_gcr_subject_public_key_attributes_size (GckAttributes *attrs)
+{
+       gulong key_type;
+
+       if (!gck_attributes_find_ulong (attrs, CKA_KEY_TYPE, &key_type))
+               return 0;
+
+       switch (key_type) {
+       case CKK_RSA:
+               return attributes_rsa_key_size (attrs);
+       case CKK_DSA:
+               return attributes_dsa_key_size (attrs);
+       case CKK_EC:
+               return attributes_ec_params_size (attrs);
+       default:
+               g_message ("unsupported key algorithm: %lu", key_type);
+               return 0;
+       }
+}
diff --git a/gcr/gcr-subject-public-key.h b/gcr/gcr-subject-public-key.h
index 4a45bd0..9e7c1d9 100644
--- a/gcr/gcr-subject-public-key.h
+++ b/gcr/gcr-subject-public-key.h
@@ -48,6 +48,8 @@ GNode *        _gcr_subject_public_key_load_finish      (GAsyncResult *result,
 
 guint          _gcr_subject_public_key_calculate_size   (GNode *subject_public_key);
 
+guint          _gcr_subject_public_key_attributes_size  (GckAttributes *attributes);
+
 G_END_DECLS
 
 #endif /* GCR_CERTIFICATE_H */
diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h
index f600454..e7177f1 100644
--- a/gcr/gcr-types.h
+++ b/gcr/gcr-types.h
@@ -63,6 +63,7 @@ typedef enum {
        GCR_FORMAT_DER_PRIVATE_KEY = 100,
        GCR_FORMAT_DER_PRIVATE_KEY_RSA,
        GCR_FORMAT_DER_PRIVATE_KEY_DSA,
+       GCR_FORMAT_DER_PRIVATE_KEY_EC,
 
        GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY = 150,
 
@@ -95,6 +96,7 @@ typedef enum {
        GCR_FORMAT_PEM_PKCS12,
        GCR_FORMAT_PEM_PRIVATE_KEY,
        GCR_FORMAT_PEM_PKCS10,
+       GCR_FORMAT_PEM_PRIVATE_KEY_ECDSA,
 } GcrDataFormat;
 
 /*
diff --git a/ui/gcr-crypto-types.xml b/ui/gcr-crypto-types.xml
index c92577c..e1e3d03 100644
--- a/ui/gcr-crypto-types.xml
+++ b/ui/gcr-crypto-types.xml
@@ -162,6 +162,7 @@
                <magic priority="75">
                        <match type="string" value="-----BEGIN RSA PRIVATE KEY-----" offset="0"/>
                        <match type="string" value="-----BEGIN DSA PRIVATE KEY-----" offset="0"/>
+                       <match type="string" value="-----BEGIN ECDSA PRIVATE KEY-----" offset="0"/>
                </magic>
        </mime-type>
 </mime-info>
diff --git a/ui/gcr-key-renderer.c b/ui/gcr-key-renderer.c
index a4dbdb9..32709db 100644
--- a/ui/gcr-key-renderer.c
+++ b/ui/gcr-key-renderer.c
@@ -114,53 +114,6 @@ calculate_fingerprint (GcrKeyRenderer *self,
        return gcr_fingerprint_from_attributes (attrs, algorithm, n_fingerprint);
 }
 
-static gint
-calculate_rsa_key_size (GckAttributes *attrs)
-{
-       const GckAttribute *attr;
-       gulong bits;
-
-       attr = gck_attributes_find (attrs, CKA_MODULUS);
-
-       /* Calculate the bit length, and remove the complement */
-       if (attr != NULL)
-               return (attr->length / 2) * 2 * 8;
-
-       if (gck_attributes_find_ulong (attrs, CKA_MODULUS_BITS, &bits))
-               return (gint)bits;
-
-       return -1;
-}
-
-static guint
-calculate_dsa_key_size (GckAttributes *attrs)
-{
-       const GckAttribute *attr;
-       gulong bits;
-
-       attr = gck_attributes_find (attrs, CKA_PRIME);
-
-       /* Calculate the bit length, and remove the complement */
-       if (attr != NULL)
-               return (attr->length / 2) * 2 * 8;
-
-       if (gck_attributes_find_ulong (attrs, CKA_PRIME_BITS, &bits))
-               return (gint)bits;
-
-       return -1;
-}
-
-static gint
-calculate_key_size (GckAttributes *attrs, gulong key_type)
-{
-       if (key_type == CKK_RSA)
-               return calculate_rsa_key_size (attrs);
-       else if (key_type == CKK_DSA)
-               return calculate_dsa_key_size (attrs);
-       else
-               return -1;
-}
-
 static void
 on_subject_public_key (GObject *source,
                        GAsyncResult *result,
@@ -362,7 +315,7 @@ gcr_key_renderer_real_render (GcrRenderer *renderer, GcrViewer *viewer)
        gchar *display;
        gulong klass;
        gulong key_type;
-       gint size;
+       guint size;
 
        self = GCR_KEY_RENDERER (renderer);
 
@@ -402,6 +355,8 @@ gcr_key_renderer_real_render (GcrRenderer *renderer, GcrViewer *viewer)
                        text = _("Private RSA Key");
                else if (key_type == CKK_DSA)
                        text = _("Private DSA Key");
+               else if (key_type == CKK_ECDSA)
+                       text = _("Private ECDSA Key");
                else
                        text = _("Private Key");
        } else if (klass == CKO_PUBLIC_KEY) {
@@ -409,15 +364,17 @@ gcr_key_renderer_real_render (GcrRenderer *renderer, GcrViewer *viewer)
                        text = _("Public DSA Key");
                else if (key_type == CKK_DSA)
                        text = _("Public DSA Key");
+               else if (key_type == CKK_ECDSA)
+                       text = _("Public ECDSA Key");
                else
                        text = _("Public Key");
        }
 
        _gcr_display_view_append_content (view, renderer, text, NULL);
 
-       size = calculate_key_size (attrs, key_type);
-       if (size >= 0) {
-               display = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d bit", "%d bits", size), size);
+       size = _gcr_subject_public_key_attributes_size (attrs);
+       if (size > 0) {
+               display = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%u bit", "%u bits", size), size);
                _gcr_display_view_append_content (view, renderer, _("Strength"), display);
                g_free (display);
        }
@@ -428,15 +385,16 @@ gcr_key_renderer_real_render (GcrRenderer *renderer, GcrViewer *viewer)
                text = _("RSA");
        else if (key_type == CKK_DSA)
                text = _("DSA");
+       else if (key_type == CKK_ECDSA)
+               text = _("ECDSA");
        else
                text = _("Unknown");
        _gcr_display_view_append_value (view, renderer, _("Algorithm"), text, FALSE);
 
-       size = calculate_key_size (attrs, key_type);
-       if (size < 0)
+       if (size == 0)
                display = g_strdup (_("Unknown"));
        else
-               display = g_strdup_printf ("%d", size);
+               display = g_strdup_printf ("%u", size);
        _gcr_display_view_append_value (view, renderer, _("Size"), display, FALSE);
        g_free (display);
 
@@ -490,7 +448,7 @@ gcr_key_renderer_new (const gchar *label, GckAttributes *attrs)
  * @attrs: (allow-none): the attributes to display
  *
  * Get the attributes displayed in the renderer. The attributes should represent
- * either an RSA or DSA key in PKCS\#11 style.
+ * either an RSA, DSA, or ECDSA key in PKCS\#11 style.
  */
 void
 gcr_key_renderer_set_attributes (GcrKeyRenderer *self, GckAttributes *attrs)
diff --git a/ui/gcr-key-widget.c b/ui/gcr-key-widget.c
index 5f25501..bbaeb0d 100644
--- a/ui/gcr-key-widget.c
+++ b/ui/gcr-key-widget.c
@@ -32,7 +32,7 @@
  * @title: GcrKeyWidget
  * @short_description: Key widget and renderer
  *
- * A #GcrKeyWidget can be used to display a RSA or DSA key. The widget
+ * A #GcrKeyWidget can be used to display a RSA, DSA or ECDSA key. The widget
  * is normally in a collapsed state showing only details, but can be expanded
  * by the user.
  *
@@ -197,7 +197,7 @@ gcr_key_widget_new (GckAttributes *attrs)
  * @attrs: (allow-none): the attributes to display
  *
  * Get the attributes displayed in the widget. The attributes should represent
- * either an RSA or DSA key in PKCS\#11 style.
+ * either an RSA, DSA or ECDSA key in PKCS\#11 style.
  */
 void
 gcr_key_widget_set_attributes (GcrKeyWidget *self, GckAttributes *attrs)


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