scd: ECDH Support (was: scd: Fix for EdDSA)
NIIBE Yutaka
gniibe at fsij.org
Tue Dec 9 08:27:56 CET 2014
Here are changes to support ECDH by scdaemon.
I tested this code with experimental version of Gnuk. It takes about
0.6 second to decrypt for NIST P-256 (measured on host PC by gpg
--decrypt). Around 2011 when I implemented NIST P-256 computation
itself, I had thought less second. This year, I modified the code to
be more constant time, computation requires more time now. Still,
it's good than RSA-2048 which takes more than 1.4 second.
I don't know if this protocol is good (or compatible) with existing
smartcard/token/hsm. In this implementation, scdaemon only computes
[d]P (d: private key, P: point) to get shared point and does not
compute AESWrap, following the protocol of gpg-agent.
OK to commit?
* agent/divert-scd.c (divert_pkdecrypt): Support ECDH.
* scd/app-openpgp.c (get_algo_byte, store_fpr): Support ECDH.
(send_key_attr): Support ECDH. Fix EdDSA algorithm value.
(retrieve_key_material): Initialize fields.
(get_public_key, ecc_writekey, do_writekey): Support ECDH.
(ecdh_writekey): Remove.
(do_decipher): Support ECDH.
(parse_algorithm_attribute): Support ECDH. Fix EdDSA.
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index ceef588..1408d65 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -417,17 +417,45 @@ divert_pkdecrypt (ctrl_t ctrl,
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
- if (!smatch (&s, n, "rsa"))
+ if (smatch (&s, n, "rsa"))
+ {
+ if (*s != '(')
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ s++;
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ if (!smatch (&s, n, "a"))
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ n = snext (&s);
+ }
+ else if (smatch (&s, n, "ecdh"))
+ {
+ if (*s != '(')
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ s++;
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ if (smatch (&s, n, "s"))
+ {
+ n = snext (&s);
+ s += n;
+ if (*s++ != ')')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ if (*s++ != '(')
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ }
+ if (!smatch (&s, n, "e"))
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ n = snext (&s);
+ }
+ else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
- if (*s != '(')
- return gpg_error (GPG_ERR_UNKNOWN_SEXP);
- s++;
- n = snext (&s);
- if (!n)
- return gpg_error (GPG_ERR_INV_SEXP);
- if (!smatch (&s, n, "a"))
- return gpg_error (GPG_ERR_UNKNOWN_SEXP);
- n = snext (&s);
+
if (!n)
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
ciphertext = s;
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 663b7d3..b35a3f2 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -120,8 +120,7 @@ static struct {
/* Type of keys. */
typedef enum
{
- KEY_TYPE_ECDH,
- KEY_TYPE_ECDSA,
+ KEY_TYPE_ECC,
KEY_TYPE_EDDSA,
KEY_TYPE_RSA,
}
@@ -236,15 +235,10 @@ struct app_local_s {
} rsa;
struct {
int curve;
- } ecdsa;
+ } ecc;
struct {
int curve;
} eddsa;
- struct {
- int curve;
- int hashalgo;
- int cipheralgo;
- } ecdh;
};
} keyattr[3];
};
@@ -745,11 +739,11 @@ parse_login_data (app_t app)
static unsigned char
-get_algo_byte (key_type_t key_type)
+get_algo_byte (int keynumber, key_type_t key_type)
{
- if (key_type == KEY_TYPE_ECDSA)
+ if (key_type == KEY_TYPE_ECC && keynumber != 1)
return 19;
- else if (key_type == KEY_TYPE_ECDH)
+ else if (key_type == KEY_TYPE_ECC && keynumber == 1)
return 18;
else if (key_type == KEY_TYPE_EDDSA)
return 22;
@@ -777,13 +771,10 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
int i;
n = 6; /* key packet version, 4-byte timestamps, and algorithm */
- if (key_type == KEY_TYPE_RSA || key_type == KEY_TYPE_ECDSA
- || key_type == KEY_TYPE_EDDSA)
- argc = 2;
- else if (key_type == KEY_TYPE_ECDH)
+ if (keynumber == 1 && key_type == KEY_TYPE_ECC)
argc = 3;
else
- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ argc = 2;
va_start (ap, key_type);
for (i = 0; i < argc; i++)
@@ -812,7 +803,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
*p++ = timestamp >> 16;
*p++ = timestamp >> 8;
*p++ = timestamp;
- *p++ = get_algo_byte (key_type);
+ *p++ = get_algo_byte (keynumber, key_type);
for (i = 0; i < argc; i++)
{
@@ -977,27 +968,18 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
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)
+ else if (app->app_local->keyattr[number].key_type == KEY_TYPE_ECC)
{
- get_ecc_key_parameters (app->app_local->keyattr[number].ecdsa.curve,
+ get_ecc_key_parameters (app->app_local->keyattr[number].ecc.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);
+ snprintf (buffer, sizeof buffer, "%d %d %u %s",
+ number+1, number==1? 18: 19, n_bits, curve_oid);
}
else if (app->app_local->keyattr[number].key_type == KEY_TYPE_EDDSA)
{
get_ecc_key_parameters (app->app_local->keyattr[number].eddsa.curve,
&n_bits, &curve_oid);
- snprintf (buffer, sizeof buffer, "%d 105 %u %s",
+ snprintf (buffer, sizeof buffer, "%d 22 %u %s",
number+1, n_bits, curve_oid);
}
else
@@ -1214,7 +1196,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
for (;;)
{
char *p;
- char *fields[6];
+ char *fields[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
int nfields;
size_t max_length;
gcry_mpi_t mpi;
@@ -1529,10 +1511,10 @@ get_public_key (app_t app, int keyno)
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)
+ else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
{
const char *curve_name
- = get_curve_name (app->app_local->keyattr[keyno].ecdsa.curve);
+ = get_curve_name (app->app_local->keyattr[keyno].ecc.curve);
err = gcry_sexp_build (&s_pkey, NULL,
"(public-key(ecc(curve%s)(q%b)))",
@@ -3226,23 +3208,6 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
static gpg_error_t
-ecdh_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
- void *pincb_arg, int keyno,
- const unsigned char *buf, size_t buflen, int depth)
-{
- (void)app;
- (void)pincb;
- (void)pincb_arg;
- (void)keyno;
- (void)buf;
- (void)buflen;
- (void)depth;
-
- return GPG_ERR_NOT_IMPLEMENTED;
-}
-
-
-static gpg_error_t
ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, int keyno,
const unsigned char *buf, size_t buflen, int depth)
@@ -3418,15 +3383,15 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
}
err = store_fpr (app, keyno, created_at, fprbuf, app->card_version,
- curve == CURVE_ED25519 ? KEY_TYPE_EDDSA : KEY_TYPE_ECDSA,
+ curve == CURVE_ED25519 ? KEY_TYPE_EDDSA : KEY_TYPE_ECC,
curve == CURVE_ED25519 ?
"\x09\x2b\x06\x01\x04\x01\xda\x47\x0f\x01"
: curve == CURVE_NIST_P256 ?
"\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"
- : "\05\x2b\x81\x04\x00\x0a",
+ : "\x05\x2b\x81\x04\x00\x0a",
curve == CURVE_ED25519 ? 10
: curve == CURVE_NIST_P256? 9 : 6,
- ecc_q, ecc_q_len);
+ ecc_q, ecc_q_len, "\x03\x01\x08\x07", 4);
if (err)
goto leave;
@@ -3500,14 +3465,11 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave;
if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0)
err = rsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
- else if ((tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0
- && (keyno == 0 || keyno == 2))
- || (tok && toklen == 5 && memcmp ("ecdsa", tok, toklen) == 0))
+ else if (tok
+ && ((toklen == 3 && memcmp ("ecc", tok, toklen) == 0)
+ || (toklen == 4 && memcmp ("ecdh", tok, toklen) == 0)
+ || (toklen == 5 && memcmp ("ecdsa", tok, toklen) == 0)))
err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
- else if ((tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0
- && keyno == 1)
- || (tok && toklen == 4 && memcmp ("ecdh", tok, toklen) == 0))
- err = ecdh_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
else
{
err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
@@ -3994,7 +3956,7 @@ do_auth (app_t app, const char *keyidstr,
&& indatalen > 101) /* For a 2048 bit key. */
return gpg_error (GPG_ERR_INV_VALUE);
- if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECDSA
+ if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC
&& (indatalen == 51 || indatalen == 67 || indatalen == 83))
{
const char *p = (const char *)indata + 19;
@@ -4082,6 +4044,8 @@ do_decipher (app_t app, const char *keyidstr,
int n;
const char *fpr = NULL;
int exmode, le_value;
+ unsigned char *fixbuf = NULL;
+ int padind = 0;
if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE);
@@ -4123,11 +4087,12 @@ do_decipher (app_t app, const char *keyidstr,
return rc;
rc = verify_chv2 (app, pincb, pincb_arg);
- if (!rc)
+ if (rc)
+ return rc;
+
+ if (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA)
{
int fixuplen;
- unsigned char *fixbuf = NULL;
- int padind = 0;
/* We might encounter a couple of leading zeroes in the
cryptogram. Due to internal use of MPIs these leading zeroes
@@ -4179,33 +4144,37 @@ do_decipher (app_t app, const char *keyidstr,
/* We use the extra leading zero as the padding byte. */
padind = -1;
}
+ }
+ else if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
+ padind = -1;
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
- if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
- {
- exmode = 1; /* Extended length w/o a limit. */
- le_value = app->app_local->extcap.max_rsp_data;
- }
- else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
- {
- exmode = -254; /* Command chaining with max. 254 bytes. */
- le_value = 0;
- }
- else
- exmode = le_value = 0;
+ if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
+ {
+ exmode = 1; /* Extended length w/o a limit. */
+ le_value = app->app_local->extcap.max_rsp_data;
+ }
+ else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
+ {
+ exmode = -254; /* Command chaining with max. 254 bytes. */
+ le_value = 0;
+ }
+ else
+ exmode = le_value = 0;
- rc = iso7816_decipher (app->slot, exmode,
- indata, indatalen, le_value, padind,
- outdata, outdatalen);
- xfree (fixbuf);
+ rc = iso7816_decipher (app->slot, exmode,
+ indata, indatalen, le_value, padind,
+ outdata, outdatalen);
+ xfree (fixbuf);
- if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
- && app->app_local->manufacturer == 5
- && app->card_version == 0x0200)
- log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
- " do not work with encryption keys > 2048 bits\n");
+ if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
+ && app->app_local->manufacturer == 5
+ && app->card_version == 0x0200)
+ log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
+ " do not work with encryption keys > 2048 bits\n");
- *r_info |= APP_DECIPHER_INFO_NOPAD;
- }
+ *r_info |= APP_DECIPHER_INFO_NOPAD;
return rc;
}
@@ -4454,25 +4423,25 @@ parse_algorithm_attribute (app_t app, int keyno)
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 */
+ else if (*buffer == 18 || *buffer == 19) /* ECDH or ECDSA */
{
- app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDSA;
- app->app_local->keyattr[keyno].ecdsa.curve
+ app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
+ app->app_local->keyattr[keyno].ecc.curve
= parse_ecc_curve (buffer + 1, buflen - 1);
+ if (opt.verbose)
+ log_printf
+ ("ECC, curve=%s\n",
+ get_curve_name (app->app_local->keyattr[keyno].ecc.curve));
}
- else if (*buffer == 18 && buflen == 11) /* ECDH */
- {
- app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDH;
- app->app_local->keyattr[keyno].ecdh.hashalgo = buffer[1];
- app->app_local->keyattr[keyno].ecdh.cipheralgo = buffer[2];
- app->app_local->keyattr[keyno].ecdh.curve
- = parse_ecc_curve (buffer + 3, buflen - 3);
- }
- else if (*buffer == 105) /* EdDSA (experimental) */
+ else if (*buffer == 22) /* EdDSA */
{
app->app_local->keyattr[keyno].key_type = KEY_TYPE_EDDSA;
app->app_local->keyattr[keyno].eddsa.curve
= parse_ecc_curve (buffer + 1, buflen - 1);
+ if (opt.verbose)
+ log_printf
+ ("EdDSA, curve=%s\n",
+ get_curve_name (app->app_local->keyattr[keyno].eddsa.curve));
}
else if (opt.verbose)
log_printhex ("", buffer, buflen);
--
More information about the Gnupg-devel
mailing list