[PATCH] scd: support ECDSA public key
NIIBE Yutaka
gniibe at fsij.org
Thu Mar 7 06:01:51 CET 2013
On 2013-02-28 at 13:38 +0900, NIIBE Yutaka wrote:
> Here is another patch for GnuPG in the smartcard ECDSA support series.
Here is update. This assume the format of public key, as:
> Data Object Abbrev. Tag Type Certificate
> Object Identifier 0x06 Object Identifier m (mandatory)
[...]
> Public point Y 0x86 Elliptic Curve Point m
I'll commit this patch to master, if no objections.
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 23b28c3..8d507c4 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -116,6 +116,16 @@ static struct {
};
+/* Type of keys. */
+typedef enum
+ {
+ KEY_TYPE_ECDH,
+ KEY_TYPE_ECDSA,
+ KEY_TYPE_RSA,
+ }
+key_type_t;
+
+
/* The format of RSA private keys. */
typedef enum
{
@@ -128,6 +138,15 @@ typedef enum
rsa_key_format_t;
+/* Elliptic Curves. */
+enum
+ {
+ CURVE_NIST_P256,
+ CURVE_NIST_P384,
+ CURVE_NIST_P521
+ };
+
+
/* One cache item for DOs. */
struct cache_s {
struct cache_s *next;
@@ -199,15 +218,27 @@ struct app_local_s {
int fixedlen_admin;
} pinpad;
- struct
- {
- unsigned int n_bits; /* Size of the modulus in bits. The rest
- of this strucuire is only valid if
- this is not 0. */
- unsigned int e_bits; /* Size of the public exponent in bits. */
- rsa_key_format_t format;
- } keyattr[3];
-
+ struct
+ {
+ key_type_t key_type;
+ union {
+ struct {
+ unsigned int n_bits; /* Size of the modulus in bits. The rest
+ of this strucuire is only valid if
+ this is not 0. */
+ unsigned int e_bits; /* Size of the public exponent in bits. */
+ rsa_key_format_t format;
+ } rsa;
+ struct {
+ int curve;
+ } ecdsa;
+ struct {
+ int curve;
+ int hashalgo;
+ int cipheralgo;
+ } ecdh;
+ };
+ } keyattr[3];
};
@@ -845,18 +876,59 @@ send_key_data (ctrl_t ctrl, const char *name,
static void
+get_ecc_key_parameters (int curve, int *r_n_bits, const char **r_curve_oid)
+{
+ if (curve == CURVE_NIST_P256)
+ {
+ *r_n_bits = 256;
+ *r_curve_oid = "1.2.840.10045.3.1.7";
+ }
+ else if (curve == CURVE_NIST_P384)
+ {
+ *r_n_bits = 384;
+ *r_curve_oid = "1.3.132.0.34";
+ }
+ else
+ {
+ *r_n_bits = 521;
+ *r_curve_oid = "1.3.132.0.35";
+ }
+}
+
+static void
send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
{
char buffer[200];
+ int n_bits;
+ const char *curve_oid;
assert (number >=0 && number < DIM(app->app_local->keyattr));
- /* We only support RSA thus the algo identifier is fixed to 1. */
- snprintf (buffer, sizeof buffer, "%d 1 %u %u %d",
- number+1,
- app->app_local->keyattr[number].n_bits,
- app->app_local->keyattr[number].e_bits,
- app->app_local->keyattr[number].format);
+ if (app->app_local->keyattr[number].key_type == KEY_TYPE_RSA)
+ snprintf (buffer, sizeof buffer, "%d 1 %u %u %d",
+ number+1,
+ app->app_local->keyattr[number].rsa.n_bits,
+ app->app_local->keyattr[number].rsa.e_bits,
+ app->app_local->keyattr[number].rsa.format);
+ else if (app->app_local->keyattr[number].key_type == KEY_TYPE_ECDSA)
+ {
+ get_ecc_key_parameters (app->app_local->keyattr[number].ecdsa.curve,
+ &n_bits, &curve_oid);
+ snprintf (buffer, sizeof buffer, "%d 19 %u %s",
+ number+1, n_bits, curve_oid);
+ }
+ else if (app->app_local->keyattr[number].key_type == KEY_TYPE_ECDH)
+ {
+ get_ecc_key_parameters (app->app_local->keyattr[number].ecdh.curve,
+ &n_bits, &curve_oid);
+ snprintf (buffer, sizeof buffer, "%d 18 %u %s %d %d",
+ number+1, n_bits, curve_oid,
+ app->app_local->keyattr[number].ecdh.hashalgo,
+ app->app_local->keyattr[number].ecdh.cipheralgo);
+ }
+ else
+ snprintf (buffer, sizeof buffer, "0 0 UNKNOWN");
+
send_status_direct (ctrl, keyword, buffer);
}
@@ -1154,6 +1226,18 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
#endif /*GNUPG_MAJOR_VERSION > 1*/
+static const char *
+get_curve_name (int curve)
+{
+ if (curve == CURVE_NIST_P256)
+ return "NIST P-256";
+ else if (curve == CURVE_NIST_P384)
+ return "NIST P-384";
+ else
+ return "NIST P-521";
+}
+
+
/* Get the public key for KEYNO and store it as an S-expresion with
the APP handle. On error that field gets cleared. If we already
know about the public key we will just return. Note that this does
@@ -1171,11 +1255,14 @@ get_public_key (app_t app, int keyno)
gpg_error_t err = 0;
unsigned char *buffer;
const unsigned char *keydata, *m, *e;
- size_t buflen, keydatalen, mlen, elen;
+ size_t buflen, keydatalen;
+ size_t mlen = 0;
+ size_t elen = 0;
unsigned char *mbuf = NULL;
unsigned char *ebuf = NULL;
char *keybuf = NULL;
- char *keybuf_p;
+ gcry_sexp_t s_pkey;
+ size_t len;
if (keyno < 1 || keyno > 3)
return gpg_error (GPG_ERR_INV_ID);
@@ -1227,51 +1314,34 @@ get_public_key (app_t app, int keyno)
goto leave;
}
- m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
- if (!m)
+ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
{
- err = gpg_error (GPG_ERR_CARD);
- log_error (_("response does not contain the RSA modulus\n"));
- goto leave;
- }
-
-
- e = find_tlv (keydata, keydatalen, 0x0082, &elen);
- if (!e)
- {
- err = gpg_error (GPG_ERR_CARD);
- log_error (_("response does not contain the RSA public exponent\n"));
- goto leave;
- }
+ m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+ if (!m)
+ {
+ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA modulus\n"));
+ goto leave;
+ }
- /* Prepend numbers with a 0 if needed. */
- if (mlen && (*m & 0x80))
- {
- mbuf = xtrymalloc ( mlen + 1);
- if (!mbuf)
+ e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+ if (!e)
{
- err = gpg_error_from_syserror ();
+ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA public exponent\n"));
goto leave;
}
- *mbuf = 0;
- memcpy (mbuf+1, m, mlen);
- mlen++;
- m = mbuf;
}
- if (elen && (*e & 0x80))
+ else
{
- ebuf = xtrymalloc ( elen + 1);
- if (!ebuf)
+ m = find_tlv (keydata, keydatalen, 0x0086, &mlen);
+ if (!m)
{
- err = gpg_error_from_syserror ();
+ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the EC public point\n"));
goto leave;
}
- *ebuf = 0;
- memcpy (ebuf+1, e, elen);
- elen++;
- e = ebuf;
}
-
}
else
{
@@ -1328,29 +1398,88 @@ get_public_key (app_t app, int keyno)
}
}
- /* Allocate a buffer to construct the S-expression. */
- /* FIXME: We should provide a generalized S-expression creation
- mechanism. */
- keybuf = xtrymalloc (50 + 2*35 + mlen + elen + 1);
- if (!keybuf)
+
+ mbuf = xtrymalloc ( mlen + 1);
+ if (!mbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
+ /* Prepend numbers with a 0 if needed. */
+ if (mlen && (*m & 0x80))
+ {
+ *mbuf = 0;
+ memcpy (mbuf+1, m, mlen);
+ mlen++;
+ }
+ else
+ memcpy (mbuf, m, mlen);
- sprintf (keybuf, "(10:public-key(3:rsa(1:n%u:", (unsigned int) mlen);
- keybuf_p = keybuf + strlen (keybuf);
- memcpy (keybuf_p, m, mlen);
- keybuf_p += mlen;
- sprintf (keybuf_p, ")(1:e%u:", (unsigned int)elen);
- keybuf_p += strlen (keybuf_p);
- memcpy (keybuf_p, e, elen);
- keybuf_p += elen;
- strcpy (keybuf_p, ")))");
- keybuf_p += strlen (keybuf_p);
+ ebuf = xtrymalloc ( elen + 1);
+ if (!ebuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ /* Prepend numbers with a 0 if needed. */
+ if (elen && (*e & 0x80))
+ {
+ *ebuf = 0;
+ memcpy (ebuf+1, e, elen);
+ elen++;
+ }
+ else
+ memcpy (ebuf, e, elen);
+
+ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+ {
+ err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
+ mlen, mbuf, elen, ebuf);
+ if (err)
+ goto leave;
+
+ len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+ keybuf = xtrymalloc (len);
+ if (!keybuf)
+ {
+ gcry_sexp_release (s_pkey);
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+ gcry_sexp_release (s_pkey);
+ }
+ else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECDSA)
+ {
+ const char *curve_name
+ = get_curve_name (app->app_local->keyattr[keyno].ecdsa.curve);
+
+ err = gcry_sexp_build (&s_pkey, NULL,
+ "(public-key(ecdsa(curve%s)(q%b)))",
+ curve_name, mlen, mbuf);
+ if (err)
+ goto leave;
+
+ len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+
+ keybuf = xtrymalloc (len);
+ if (!keybuf)
+ {
+ gcry_sexp_release (s_pkey);
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+ gcry_sexp_release (s_pkey);
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ goto leave;
+ }
app->app_local->pk[keyno].key = (unsigned char*)keybuf;
- app->app_local->pk[keyno].keylen = (keybuf_p - keybuf);
+ app->app_local->pk[keyno].keylen = len - 1; /* Decrement for trailing '\0' */
leave:
/* Set a flag to indicate that we tried to read the key. */
@@ -2395,7 +2524,7 @@ build_privkey_template (app_t app, int keyno,
*result = NULL;
*resultlen = 0;
- switch (app->app_local->keyattr[keyno].format)
+ switch (app->app_local->keyattr[keyno].rsa.format)
{
case RSA_STD:
case RSA_STD_N:
@@ -2409,7 +2538,7 @@ build_privkey_template (app_t app, int keyno,
}
/* Get the required length for E. */
- rsa_e_reqlen = app->app_local->keyattr[keyno].e_bits/8;
+ rsa_e_reqlen = app->app_local->keyattr[keyno].rsa.e_bits/8;
assert (rsa_e_len <= rsa_e_reqlen);
/* Build the 7f48 cardholder private key template. */
@@ -2425,8 +2554,8 @@ build_privkey_template (app_t app, int keyno,
tp += add_tlv (tp, 0x93, rsa_q_len);
datalen += rsa_q_len;
- if (app->app_local->keyattr[keyno].format == RSA_STD_N
- || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+ if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
+ || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{
tp += add_tlv (tp, 0x97, rsa_n_len);
datalen += rsa_n_len;
@@ -2478,8 +2607,8 @@ build_privkey_template (app_t app, int keyno,
memcpy (tp, rsa_q, rsa_q_len);
tp += rsa_q_len;
- if (app->app_local->keyattr[keyno].format == RSA_STD_N
- || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+ if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
+ || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{
memcpy (tp, rsa_n, rsa_n_len);
tp += rsa_n_len;
@@ -2764,7 +2893,7 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave;
}
- maxbits = app->app_local->keyattr[keyno].n_bits;
+ maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
if (opt.verbose)
log_info ("RSA modulus size is %u bits (%u bytes)\n",
@@ -2775,7 +2904,7 @@ do_writekey (app_t app, ctrl_t ctrl,
/* Try to switch the key to a new length. */
err = change_keyattr (app, keyno, nbits, pincb, pincb_arg);
if (!err)
- maxbits = app->app_local->keyattr[keyno].n_bits;
+ maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
}
if (nbits != maxbits)
{
@@ -2785,7 +2914,7 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave;
}
- maxbits = app->app_local->keyattr[keyno].e_bits;
+ maxbits = app->app_local->keyattr[keyno].rsa.e_bits;
if (maxbits > 32 && !app->app_local->extcap.is_v2)
maxbits = 32; /* Our code for v1 does only support 32 bits. */
nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
@@ -2797,7 +2926,7 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave;
}
- maxbits = app->app_local->keyattr[keyno].n_bits/2;
+ maxbits = app->app_local->keyattr[keyno].rsa.n_bits/2;
nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
if (nbits != maxbits)
{
@@ -2966,7 +3095,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
to put a limit on the max. allowed keysize. 2048 bit will
already lead to a 527 byte long status line and thus a 4096 bit
key would exceed the Assuan line length limit. */
- keybits = app->app_local->keyattr[keyno].n_bits;
+ keybits = app->app_local->keyattr[keyno].rsa.n_bits;
if (keybits > 4096)
return gpg_error (GPG_ERR_TOO_LARGE);
@@ -3753,6 +3882,22 @@ parse_historical (struct app_local_s *apploc,
}
+static int
+parse_ecc_curve (const unsigned char *buffer, size_t buflen)
+{
+ int curve;
+
+ if (buflen == 6 && buffer[5] == 0x22)
+ curve = CURVE_NIST_P384;
+ else if (buflen == 6 && buffer[5] == 0x23)
+ curve = CURVE_NIST_P521;
+ else
+ curve = CURVE_NIST_P256;
+
+ return curve;
+}
+
+
/* Parse and optionally show the algorithm attributes for KEYNO.
KEYNO must be in the range 0..2. */
static void
@@ -3765,7 +3910,8 @@ parse_algorithm_attribute (app_t app, int keyno)
assert (keyno >=0 && keyno <= 2);
- app->app_local->keyattr[keyno].n_bits = 0;
+ app->app_local->keyattr[keyno].key_type = KEY_TYPE_RSA;
+ app->app_local->keyattr[keyno].rsa.n_bits = 0;
relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
if (!relptr)
@@ -3784,27 +3930,41 @@ parse_algorithm_attribute (app_t app, int keyno)
log_info ("Key-Attr-%s ..: ", desc[keyno]);
if (*buffer == 1 && (buflen == 5 || buflen == 6))
{
- app->app_local->keyattr[keyno].n_bits = (buffer[1]<<8 | buffer[2]);
- app->app_local->keyattr[keyno].e_bits = (buffer[3]<<8 | buffer[4]);
- app->app_local->keyattr[keyno].format = 0;
+ app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]);
+ app->app_local->keyattr[keyno].rsa.e_bits = (buffer[3]<<8 | buffer[4]);
+ app->app_local->keyattr[keyno].rsa.format = 0;
if (buflen < 6)
- app->app_local->keyattr[keyno].format = RSA_STD;
+ app->app_local->keyattr[keyno].rsa.format = RSA_STD;
else
- app->app_local->keyattr[keyno].format = (buffer[5] == 0? RSA_STD :
- buffer[5] == 1? RSA_STD_N :
- buffer[5] == 2? RSA_CRT :
- buffer[5] == 3? RSA_CRT_N :
- RSA_UNKNOWN_FMT);
+ app->app_local->keyattr[keyno].rsa.format = (buffer[5] == 0? RSA_STD :
+ buffer[5] == 1? RSA_STD_N :
+ buffer[5] == 2? RSA_CRT :
+ buffer[5] == 3? RSA_CRT_N :
+ RSA_UNKNOWN_FMT);
if (opt.verbose)
log_printf
("RSA, n=%u, e=%u, fmt=%s\n",
- app->app_local->keyattr[keyno].n_bits,
- app->app_local->keyattr[keyno].e_bits,
- app->app_local->keyattr[keyno].format == RSA_STD? "std" :
- app->app_local->keyattr[keyno].format == RSA_STD_N?"std+n":
- app->app_local->keyattr[keyno].format == RSA_CRT? "crt" :
- app->app_local->keyattr[keyno].format == RSA_CRT_N?"crt+n":"?");
+ app->app_local->keyattr[keyno].rsa.n_bits,
+ app->app_local->keyattr[keyno].rsa.e_bits,
+ app->app_local->keyattr[keyno].rsa.format == RSA_STD? "std" :
+ app->app_local->keyattr[keyno].rsa.format == RSA_STD_N?"std+n":
+ app->app_local->keyattr[keyno].rsa.format == RSA_CRT? "crt" :
+ app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N?"crt+n":"?");
+ }
+ else if (*buffer == 19) /* ECDSA */
+ {
+ app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDSA;
+ app->app_local->keyattr[keyno].ecdsa.curve
+ = parse_ecc_curve (buffer + 1, buflen - 1);
+ }
+ else if (*buffer == 18 && buflen == 11) /* ECDH */
+ {
+ app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDH;
+ app->app_local->keyattr[keyno].ecdh.curve
+ = parse_ecc_curve (buffer + 1, buflen - 1);
+ app->app_local->keyattr[keyno].ecdh.hashalgo = buffer[1];
+ app->app_local->keyattr[keyno].ecdh.cipheralgo = buffer[2];
}
else if (opt.verbose)
log_printhex ("", buffer, buflen);
--
More information about the Gnupg-devel
mailing list