diff options
author | Jakub Jelen <jjelen@redhat.com> | 2017-08-08 18:34:01 +0200 |
---|---|---|
committer | Daiki Ueno <ueno@gnu.org> | 2017-10-13 08:51:04 +0200 |
commit | 32680d2667410c8d1802e10746084253daee65d0 (patch) | |
tree | 5871b40b69533179204fd9c2f6c72012dadf53e3 /pkcs11/gkm | |
parent | d96bb48a91bdd2075c08b781dc0b1c49db6f89c6 (diff) | |
download | gnome-keyring-32680d2667410c8d1802e10746084253daee65d0.tar.gz |
ASN.1 definitions of ECDSA and helper functions for DER encoding and decoding
https://bugzilla.gnome.org/show_bug.cgi?id=641082
Diffstat (limited to 'pkcs11/gkm')
-rw-r--r-- | pkcs11/gkm/gkm-data-asn1.c | 121 | ||||
-rw-r--r-- | pkcs11/gkm/gkm-data-asn1.h | 26 | ||||
-rw-r--r-- | pkcs11/gkm/gkm-data-der.c | 165 | ||||
-rw-r--r-- | pkcs11/gkm/gkm-data-der.h | 20 |
4 files changed, 326 insertions, 6 deletions
diff --git a/pkcs11/gkm/gkm-data-asn1.c b/pkcs11/gkm/gkm-data-asn1.c index e063dd48..591bbe88 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 12826dab..822fd08b 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 78c16ca1..b21049c6 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 e4968623..f0587db5 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); |