From bdc8efbdd124d836c36cf482216e375421f72891 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Mon, 22 Dec 2014 09:27:00 +0900 Subject: [PATCH] scd: ECDH Support. * 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. -- Following the gpg-agent protocol, SCDaemon's counter part is now implemented. --- agent/divert-scd.c | 48 ++++++++++--- scd/app-openpgp.c | 175 +++++++++++++++++++-------------------------- 2 files changed, 110 insertions(+), 113 deletions(-) diff --git a/agent/divert-scd.c b/agent/divert-scd.c index ceef588fb..1408d65af 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 daf0310e8..475d84433 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 @@ -1215,7 +1197,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; @@ -1530,10 +1512,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, @@ -3419,15 +3384,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; @@ -3501,14 +3466,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); @@ -3995,7 +3957,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; @@ -4083,6 +4045,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); @@ -4124,11 +4088,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 @@ -4180,33 +4145,37 @@ do_decipher (app_t app, const char *keyidstr, /* We use the extra leading zero as the padding byte. */ padind = -1; } - - 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); - - 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; } + 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; + + 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"); + + *r_info |= APP_DECIPHER_INFO_NOPAD; return rc; } @@ -4455,25 +4424,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);