1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

scd: support ECDSA public key.

* scd/app-openpgp.c (key_type_t): New.
(CURVE_NIST_P256, CURVE_NIST_P384, CURVE_NIST_P521): New.
(struct app_local_s): Change keyattr to have key_type and union.
(get_ecc_key_parameters, get_curve_name): New.
(send_key_attr, get_public_key): Support ECDSA.
(build_privkey_template, do_writekey, do_genkey): Follow the change
of the member KEY_ATTR.
(parse_historical): New.
(parse_algorithm_attribute): Support ECDSA.
--

Add ECDSA support to OpenPGP card.
This commit is contained in:
NIIBE Yutaka 2013-03-08 11:40:37 +09:00
parent 5132ea8a0d
commit 010bc7f4f0

View File

@ -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. */ /* The format of RSA private keys. */
typedef enum typedef enum
{ {
@ -128,6 +138,15 @@ typedef enum
rsa_key_format_t; rsa_key_format_t;
/* Elliptic Curves. */
enum
{
CURVE_NIST_P256,
CURVE_NIST_P384,
CURVE_NIST_P521
};
/* One cache item for DOs. */ /* One cache item for DOs. */
struct cache_s { struct cache_s {
struct cache_s *next; struct cache_s *next;
@ -201,13 +220,25 @@ struct app_local_s {
struct struct
{ {
key_type_t key_type;
union {
struct {
unsigned int n_bits; /* Size of the modulus in bits. The rest unsigned int n_bits; /* Size of the modulus in bits. The rest
of this strucuire is only valid if of this strucuire is only valid if
this is not 0. */ this is not 0. */
unsigned int e_bits; /* Size of the public exponent in bits. */ unsigned int e_bits; /* Size of the public exponent in bits. */
rsa_key_format_t format; rsa_key_format_t format;
} rsa;
struct {
int curve;
} ecdsa;
struct {
int curve;
int hashalgo;
int cipheralgo;
} ecdh;
};
} keyattr[3]; } keyattr[3];
}; };
@ -844,19 +875,60 @@ 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 static void
send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number) send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
{ {
char buffer[200]; char buffer[200];
int n_bits;
const char *curve_oid;
assert (number >=0 && number < DIM(app->app_local->keyattr)); assert (number >=0 && number < DIM(app->app_local->keyattr));
/* We only support RSA thus the algo identifier is fixed to 1. */ if (app->app_local->keyattr[number].key_type == KEY_TYPE_RSA)
snprintf (buffer, sizeof buffer, "%d 1 %u %u %d", snprintf (buffer, sizeof buffer, "%d 1 %u %u %d",
number+1, number+1,
app->app_local->keyattr[number].n_bits, app->app_local->keyattr[number].rsa.n_bits,
app->app_local->keyattr[number].e_bits, app->app_local->keyattr[number].rsa.e_bits,
app->app_local->keyattr[number].format); 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); send_status_direct (ctrl, keyword, buffer);
} }
@ -1154,6 +1226,18 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
#endif /*GNUPG_MAJOR_VERSION > 1*/ #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 /* 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 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 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; gpg_error_t err = 0;
unsigned char *buffer; unsigned char *buffer;
const unsigned char *keydata, *m, *e; 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 *mbuf = NULL;
unsigned char *ebuf = NULL; unsigned char *ebuf = NULL;
char *keybuf = NULL; char *keybuf = NULL;
char *keybuf_p; gcry_sexp_t s_pkey;
size_t len;
if (keyno < 1 || keyno > 3) if (keyno < 1 || keyno > 3)
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
@ -1227,6 +1314,8 @@ get_public_key (app_t app, int keyno)
goto leave; goto leave;
} }
if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
{
m = find_tlv (keydata, keydatalen, 0x0081, &mlen); m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
if (!m) if (!m)
{ {
@ -1235,7 +1324,6 @@ get_public_key (app_t app, int keyno)
goto leave; goto leave;
} }
e = find_tlv (keydata, keydatalen, 0x0082, &elen); e = find_tlv (keydata, keydatalen, 0x0082, &elen);
if (!e) if (!e)
{ {
@ -1243,35 +1331,17 @@ get_public_key (app_t app, int keyno)
log_error (_("response does not contain the RSA public exponent\n")); log_error (_("response does not contain the RSA public exponent\n"));
goto leave; goto leave;
} }
}
/* Prepend numbers with a 0 if needed. */ else
if (mlen && (*m & 0x80))
{ {
mbuf = xtrymalloc ( mlen + 1); m = find_tlv (keydata, keydatalen, 0x0086, &mlen);
if (!mbuf) 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; goto leave;
} }
*mbuf = 0;
memcpy (mbuf+1, m, mlen);
mlen++;
m = mbuf;
} }
if (elen && (*e & 0x80))
{
ebuf = xtrymalloc ( elen + 1);
if (!ebuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
*ebuf = 0;
memcpy (ebuf+1, e, elen);
elen++;
e = ebuf;
}
} }
else 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 mbuf = xtrymalloc ( mlen + 1);
mechanism. */ if (!mbuf)
keybuf = xtrymalloc (50 + 2*35 + mlen + elen + 1);
if (!keybuf)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
goto leave; 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); ebuf = xtrymalloc ( elen + 1);
keybuf_p = keybuf + strlen (keybuf); if (!ebuf)
memcpy (keybuf_p, m, mlen); {
keybuf_p += mlen; err = gpg_error_from_syserror ();
sprintf (keybuf_p, ")(1:e%u:", (unsigned int)elen); goto leave;
keybuf_p += strlen (keybuf_p); }
memcpy (keybuf_p, e, elen); /* Prepend numbers with a 0 if needed. */
keybuf_p += elen; if (elen && (*e & 0x80))
strcpy (keybuf_p, ")))"); {
keybuf_p += strlen (keybuf_p); *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].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: leave:
/* Set a flag to indicate that we tried to read the key. */ /* 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; *result = NULL;
*resultlen = 0; *resultlen = 0;
switch (app->app_local->keyattr[keyno].format) switch (app->app_local->keyattr[keyno].rsa.format)
{ {
case RSA_STD: case RSA_STD:
case RSA_STD_N: case RSA_STD_N:
@ -2409,7 +2538,7 @@ build_privkey_template (app_t app, int keyno,
} }
/* Get the required length for E. */ /* 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); assert (rsa_e_len <= rsa_e_reqlen);
/* Build the 7f48 cardholder private key template. */ /* 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); tp += add_tlv (tp, 0x93, rsa_q_len);
datalen += rsa_q_len; datalen += rsa_q_len;
if (app->app_local->keyattr[keyno].format == RSA_STD_N if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
|| app->app_local->keyattr[keyno].format == RSA_CRT_N) || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{ {
tp += add_tlv (tp, 0x97, rsa_n_len); tp += add_tlv (tp, 0x97, rsa_n_len);
datalen += 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); memcpy (tp, rsa_q, rsa_q_len);
tp += rsa_q_len; tp += rsa_q_len;
if (app->app_local->keyattr[keyno].format == RSA_STD_N if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
|| app->app_local->keyattr[keyno].format == RSA_CRT_N) || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
{ {
memcpy (tp, rsa_n, rsa_n_len); memcpy (tp, rsa_n, rsa_n_len);
tp += rsa_n_len; tp += rsa_n_len;
@ -2764,7 +2893,7 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave; 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; nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
if (opt.verbose) if (opt.verbose)
log_info ("RSA modulus size is %u bits (%u bytes)\n", 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. */ /* Try to switch the key to a new length. */
err = change_keyattr (app, keyno, nbits, pincb, pincb_arg); err = change_keyattr (app, keyno, nbits, pincb, pincb_arg);
if (!err) if (!err)
maxbits = app->app_local->keyattr[keyno].n_bits; maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
} }
if (nbits != maxbits) if (nbits != maxbits)
{ {
@ -2785,7 +2914,7 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave; 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) if (maxbits > 32 && !app->app_local->extcap.is_v2)
maxbits = 32; /* Our code for v1 does only support 32 bits. */ maxbits = 32; /* Our code for v1 does only support 32 bits. */
nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0; 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; 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; nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
if (nbits != maxbits) 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 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 already lead to a 527 byte long status line and thus a 4096 bit
key would exceed the Assuan line length limit. */ 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) if (keybits > 4096)
return gpg_error (GPG_ERR_TOO_LARGE); 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. /* Parse and optionally show the algorithm attributes for KEYNO.
KEYNO must be in the range 0..2. */ KEYNO must be in the range 0..2. */
static void static void
@ -3765,7 +3910,8 @@ parse_algorithm_attribute (app_t app, int keyno)
assert (keyno >=0 && keyno <= 2); 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); relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
if (!relptr) if (!relptr)
@ -3784,13 +3930,13 @@ parse_algorithm_attribute (app_t app, int keyno)
log_info ("Key-Attr-%s ..: ", desc[keyno]); log_info ("Key-Attr-%s ..: ", desc[keyno]);
if (*buffer == 1 && (buflen == 5 || buflen == 6)) if (*buffer == 1 && (buflen == 5 || buflen == 6))
{ {
app->app_local->keyattr[keyno].n_bits = (buffer[1]<<8 | buffer[2]); app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]);
app->app_local->keyattr[keyno].e_bits = (buffer[3]<<8 | buffer[4]); app->app_local->keyattr[keyno].rsa.e_bits = (buffer[3]<<8 | buffer[4]);
app->app_local->keyattr[keyno].format = 0; app->app_local->keyattr[keyno].rsa.format = 0;
if (buflen < 6) if (buflen < 6)
app->app_local->keyattr[keyno].format = RSA_STD; app->app_local->keyattr[keyno].rsa.format = RSA_STD;
else else
app->app_local->keyattr[keyno].format = (buffer[5] == 0? RSA_STD : app->app_local->keyattr[keyno].rsa.format = (buffer[5] == 0? RSA_STD :
buffer[5] == 1? RSA_STD_N : buffer[5] == 1? RSA_STD_N :
buffer[5] == 2? RSA_CRT : buffer[5] == 2? RSA_CRT :
buffer[5] == 3? RSA_CRT_N : buffer[5] == 3? RSA_CRT_N :
@ -3799,12 +3945,26 @@ parse_algorithm_attribute (app_t app, int keyno)
if (opt.verbose) if (opt.verbose)
log_printf log_printf
("RSA, n=%u, e=%u, fmt=%s\n", ("RSA, n=%u, e=%u, fmt=%s\n",
app->app_local->keyattr[keyno].n_bits, app->app_local->keyattr[keyno].rsa.n_bits,
app->app_local->keyattr[keyno].e_bits, app->app_local->keyattr[keyno].rsa.e_bits,
app->app_local->keyattr[keyno].format == RSA_STD? "std" : app->app_local->keyattr[keyno].rsa.format == RSA_STD? "std" :
app->app_local->keyattr[keyno].format == RSA_STD_N?"std+n": app->app_local->keyattr[keyno].rsa.format == RSA_STD_N?"std+n":
app->app_local->keyattr[keyno].format == RSA_CRT? "crt" : app->app_local->keyattr[keyno].rsa.format == RSA_CRT? "crt" :
app->app_local->keyattr[keyno].format == RSA_CRT_N?"crt+n":"?"); 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) else if (opt.verbose)
log_printhex ("", buffer, buflen); log_printhex ("", buffer, buflen);