1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-01 22:28:02 +02:00

scd:openpgp: Allow auto-changing of the key attributes in genkey.

* scd/app-openpgp.c (struct app_local_s): Add field keyalgo.
(parse_algorithm_attribute): Store the new keyalgo field.
(change_keyattr): Change info message.
(change_keyattr_from_string): Rewrite to also accept a keyref and a
keyalgo string.
(do_genkey): Change the keyattr if a keyalgo string is given.
* scd/command.c (cmd_genkey): Add option --algo.
--

Having this feature makes it easier to use OpenPGP cards in a similar
way to other cards.  Note that the explicit changing via SETATTR is
still supported.

Signed-off-by: Werner Koch <wk@gnupg.org>
(cherry picked from commit d7d75da505)
(cherry picked from commit b349adc5c0)
This commit is contained in:
Werner Koch 2019-02-08 11:53:34 +01:00
parent 2e39fed109
commit 210ba98355
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 215 additions and 69 deletions

View File

@ -226,5 +226,8 @@ gpg_error_t compute_openpgp_fpr_ecc (int keyversion,
unsigned char *result, unsigned char *result,
unsigned int *r_resultlen); unsigned int *r_resultlen);
/*-- openpgp-oid.c --*/
enum gcry_pk_algos map_openpgp_pk_to_gcry (pubkey_algo_t algo);
#endif /*GNUPG_COMMON_OPENPGPDEFS_H*/ #endif /*GNUPG_COMMON_OPENPGPDEFS_H*/

View File

@ -223,6 +223,7 @@ struct app_local_s {
struct struct
{ {
key_type_t key_type; key_type_t key_type;
const char *keyalgo; /* Algorithm in standard string format. */
union { union {
struct { 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
@ -257,6 +258,7 @@ static gpg_error_t change_keyattr_from_string
(app_t app, (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const char *keyref, const char *keyalgo,
const void *value, size_t valuelen); const void *value, size_t valuelen);
@ -2612,7 +2614,8 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name,
return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */ return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */
if (table[idx].special == 3) if (table[idx].special == 3)
return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen); return change_keyattr_from_string (app, pincb, pincb_arg,
NULL, NULL, value, valuelen);
switch (table[idx].need_chv) switch (table[idx].need_chv)
{ {
@ -3389,9 +3392,10 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
/* Change the attribute. */ /* Change the attribute. */
err = iso7816_put_data (app_get_slot (app), 0, 0xC1+keyno, buf, buflen); err = iso7816_put_data (app_get_slot (app), 0, 0xC1+keyno, buf, buflen);
if (err) if (err)
log_error ("error changing key attribute (key=%d)\n", keyno+1); log_error ("error changing key attribute of OPENPGP.%d\n",
keyno+1);
else else
log_info ("key attribute changed (key=%d)\n", keyno+1); log_info ("key attribute of OPENPGP.%d changed\n", keyno+1);
flush_cache (app); flush_cache (app);
err = parse_algorithm_attribute (app, keyno); err = parse_algorithm_attribute (app, keyno);
app->did_chv1 = 0; app->did_chv1 = 0;
@ -3451,51 +3455,138 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
/* Helper to process an setattr command for name KEY-ATTR. /* Helper to process an setattr command for name KEY-ATTR.
In (VALUE,VALUELEN), it expects following string: *
RSA: "--force <key> <algo> rsa<nbits>" * If KEYREF and KEYALGO are NULL (VALUE,VALUELEN) are expected to
ECC: "--force <key> <algo> <curvename>" * contain one of the following strings:
*/ * RSA: "--force <key> <algo> rsa<nbits>"
* ECC: "--force <key> <algo> <curvename>"
*
* If KEYREF and KEYALGO is given the key attribute for KEYREF are
* changed to what is described by KEYALGO (e.g. "rsa3072", "rsa2048",
* or "ed25519").
*/
static gpg_error_t static gpg_error_t
change_keyattr_from_string (app_t app, change_keyattr_from_string (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const char *keyref, const char *keyalgo,
const void *value, size_t valuelen) const void *value, size_t valuelen)
{ {
gpg_error_t err = 0; gpg_error_t err = 0;
char *string; char *string = NULL;
int key, keyno, algo; int key, keyno, algo;
int n = 0; unsigned int nbits = 0;
const char *oidstr = NULL; /* OID of the curve. */
char *endp;
/* VALUE is expected to be a string but not guaranteed to be if (keyref && keyalgo && *keyref && *keyalgo)
terminated. Thus copy it to an allocated buffer first. */
string = xtrymalloc (valuelen+1);
if (!string)
return gpg_error_from_syserror ();
memcpy (string, value, valuelen);
string[valuelen] = 0;
/* Because this function deletes the key we require the string
"--force" in the data to make clear that something serious might
happen. */
sscanf (string, "--force %d %d %n", &key, &algo, &n);
if (n < 12)
{ {
err = gpg_error (GPG_ERR_INV_DATA); if (!ascii_strcasecmp (keyref, "OPENPGP.1"))
keyno = 0;
else if (!ascii_strcasecmp (keyref, "OPENPGP.2"))
keyno = 1;
else if (!ascii_strcasecmp (keyref, "OPENPGP.3"))
keyno = 2;
else
{
err = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
if (!strncmp (keyalgo, "rsa", 3) && digitp (keyalgo+3))
{
errno = 0;
nbits = strtoul (keyalgo+3, &endp, 10);
if (errno || *endp)
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
algo = PUBKEY_ALGO_RSA;
}
else if ((!strncmp (keyalgo, "dsa", 3) || !strncmp (keyalgo, "elg", 3))
&& digitp (keyalgo+3))
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
else
{
nbits = 0;
oidstr = openpgp_curve_to_oid (keyalgo, NULL, &algo);
if (!oidstr)
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
if (!algo)
algo = keyno == 1? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA;
}
}
else if (!keyref && !keyalgo && value)
{
int n;
/* VALUE is expected to be a string but not guaranteed to be
* terminated. Thus copy it to an allocated buffer first. */
string = xtrymalloc (valuelen+1);
if (!string)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (string, value, valuelen);
string[valuelen] = 0;
/* Because this function deletes the key we require the string
* "--force" in the data to make clear that something serious
* might happen. */
n = 0;
sscanf (string, "--force %d %d %n", &key, &algo, &n);
if (n < 12)
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
keyno = key - 1;
if (algo == PUBKEY_ALGO_RSA)
{
errno = 0;
nbits = strtoul (string+n+3, NULL, 10);
if (errno)
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
}
else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA)
{
oidstr = openpgp_curve_to_oid (string+n, NULL, NULL);
if (!oidstr)
{
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
}
else
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
}
else
{
err = gpg_error (GPG_ERR_INV_ARG);
goto leave; goto leave;
} }
keyno = key - 1;
if (keyno < 0 || keyno > 2) if (keyno < 0 || keyno > 2)
err = gpg_error (GPG_ERR_INV_ID); err = gpg_error (GPG_ERR_INV_ID);
else if (algo == PUBKEY_ALGO_RSA) else if (algo == PUBKEY_ALGO_RSA)
{ {
unsigned int nbits; if (nbits < 1024)
errno = 0;
nbits = strtoul (string+n+3, NULL, 10);
if (errno)
err = gpg_error (GPG_ERR_INV_DATA);
else if (nbits < 1024)
err = gpg_error (GPG_ERR_TOO_SHORT); err = gpg_error (GPG_ERR_TOO_SHORT);
else if (nbits > 4096) else if (nbits > 4096)
err = gpg_error (GPG_ERR_TOO_LARGE); err = gpg_error (GPG_ERR_TOO_LARGE);
@ -3505,18 +3596,23 @@ change_keyattr_from_string (app_t app,
else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA) || algo == PUBKEY_ALGO_EDDSA)
{ {
const char *oidstr;
gcry_mpi_t oid; gcry_mpi_t oid;
const unsigned char *oidbuf; const unsigned char *oidbuf;
size_t oid_len; size_t oid_len;
unsigned int n;
oidstr = openpgp_curve_to_oid (string+n, NULL, NULL); /* Check that the requested algo matches the properties of the
if (!oidstr) * key slot. */
{ if (keyno == 1 && algo != PUBKEY_ALGO_ECDH)
err = gpg_error (GPG_ERR_INV_DATA); err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
goto leave; else if (keyno != 1 && algo == PUBKEY_ALGO_ECDH)
} err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
else
err = 0;
if (err)
goto leave;
/* Convert the OID string to an OpenPGP formatted OID. */
err = openpgp_oid_from_str (oidstr, &oid); err = openpgp_oid_from_str (oidstr, &oid);
if (err) if (err)
goto leave; goto leave;
@ -3524,7 +3620,14 @@ change_keyattr_from_string (app_t app,
oidbuf = gcry_mpi_get_opaque (oid, &n); oidbuf = gcry_mpi_get_opaque (oid, &n);
oid_len = (n+7)/8; oid_len = (n+7)/8;
/* We have enough room at STRING. */ /* Create the template. */
xfree (string);
string = xtrymalloc (1 + oid_len);
if (!string)
{
err = gpg_error_from_syserror ();
goto leave;
}
string[0] = algo; string[0] = algo;
memcpy (string+1, oidbuf+1, oid_len-1); memcpy (string+1, oidbuf+1, oid_len-1);
err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg); err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
@ -4204,26 +4307,31 @@ do_writekey (app_t app, ctrl_t ctrl,
/* Handle the GENKEY command. */ /* Handle the GENKEY command. */
static gpg_error_t static gpg_error_t
do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype, do_genkey (app_t app, ctrl_t ctrl, const char *keyref, const char *keyalgo,
unsigned int flags, time_t createtime, unsigned int flags, time_t createtime,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
gpg_error_t err; gpg_error_t err;
char numbuf[30]; char numbuf[30];
const char *keynostr;
unsigned char *buffer = NULL; unsigned char *buffer = NULL;
const unsigned char *keydata; const unsigned char *keydata;
size_t buflen, keydatalen; size_t buflen, keydatalen;
u32 created_at; u32 created_at;
int keyno = atoi (keynostr) - 1; int keyno;
int force = (flags & 1); int force = !!(flags & APP_GENKEY_FLAG_FORCE);
time_t start_at; time_t start_at;
int exmode = 0; int exmode = 0;
int le_value = 256; /* Use legacy value. */ int le_value = 256; /* Use legacy value. */
(void)keytype; /* Ignored for OpenPGP cards. */ /* Strip the OpenPGP prefix which is for historical reasons optional. */
keynostr = keyref;
if (!ascii_strncasecmp (keynostr, "OPENPGP.", 8))
keynostr += 8;
if (keyno < 0 || keyno > 2) keyno = atoi (keynostr) - 1;
if (!digitp (keynostr) || keyno < 0 || keyno > 2)
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
/* We flush the cache to increase the traffic before a key /* We flush the cache to increase the traffic before a key
@ -4241,6 +4349,19 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype,
if (err) if (err)
return err; return err;
if (keyalgo && app->app_local->keyattr[keyno].keyalgo
&& strcmp (keyalgo, app->app_local->keyattr[keyno].keyalgo))
{
/* Specific algorithm requested which is not the currently
* configured algorithm. Change it. */
log_info ("openpgp: changing key attribute from %s to %s\n",
app->app_local->keyattr[keyno].keyalgo, keyalgo);
err = change_keyattr_from_string (app, pincb, pincb_arg,
keyref, keyalgo, NULL, 0);
if (err)
return err;
}
if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA) if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
{ {
unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits; unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits;
@ -5164,7 +5285,7 @@ parse_historical (struct app_local_s *apploc,
/* /*
* Check if the OID in an DER encoding is available by GnuPG/libgcrypt, * Check if the OID in an DER encoding is available by GnuPG/libgcrypt,
* and return the curve name. Return NULL if not available. * and return the canonical curve name. Return NULL if not available.
* The constant string is not allocated dynamically, never free it. * The constant string is not allocated dynamically, never free it.
*/ */
static const char * static const char *
@ -5208,9 +5329,12 @@ parse_algorithm_attribute (app_t app, int keyno)
size_t buflen; size_t buflen;
void *relptr; void *relptr;
const char desc[3][5] = {"sign", "encr", "auth"}; const char desc[3][5] = {"sign", "encr", "auth"};
enum gcry_pk_algos galgo;
unsigned int nbits;
const char *curve;
gpg_error_t err = 0; gpg_error_t err = 0;
assert (keyno >=0 && keyno <= 2); log_assert (keyno >=0 && keyno <= 2);
app->app_local->keyattr[keyno].key_type = KEY_TYPE_RSA; app->app_local->keyattr[keyno].key_type = KEY_TYPE_RSA;
app->app_local->keyattr[keyno].rsa.n_bits = 0; app->app_local->keyattr[keyno].rsa.n_bits = 0;
@ -5230,6 +5354,11 @@ parse_algorithm_attribute (app_t app, int keyno)
if (opt.verbose) if (opt.verbose)
log_info ("Key-Attr-%s ..: ", desc[keyno]); log_info ("Key-Attr-%s ..: ", desc[keyno]);
galgo = map_openpgp_pk_to_gcry (*buffer);
nbits = 0;
curve = NULL;
if (*buffer == PUBKEY_ALGO_RSA && (buflen == 5 || buflen == 6)) if (*buffer == PUBKEY_ALGO_RSA && (buflen == 5 || buflen == 6))
{ {
app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]); app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]);
@ -5244,6 +5373,7 @@ parse_algorithm_attribute (app_t app, int keyno)
buffer[5] == 3? RSA_CRT_N : buffer[5] == 3? RSA_CRT_N :
RSA_UNKNOWN_FMT); RSA_UNKNOWN_FMT);
nbits = app->app_local->keyattr[keyno].rsa.n_bits;
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",
@ -5257,7 +5387,6 @@ parse_algorithm_attribute (app_t app, int keyno)
else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA
|| *buffer == PUBKEY_ALGO_EDDSA) || *buffer == PUBKEY_ALGO_EDDSA)
{ {
const char *curve;
int oidlen = buflen - 1; int oidlen = buflen - 1;
app->app_local->keyattr[keyno].ecc.flags = 0; app->app_local->keyattr[keyno].ecc.flags = 0;
@ -5309,6 +5438,13 @@ parse_algorithm_attribute (app_t app, int keyno)
else if (opt.verbose) else if (opt.verbose)
log_printhex (buffer, buflen, ""); log_printhex (buffer, buflen, "");
app->app_local->keyattr[keyno].keyalgo
= get_keyalgo_string (galgo, nbits, curve);
if (opt.verbose)
log_info ("Key-Algo-%s ..: %s\n",
desc[keyno], app->app_local->keyattr[keyno].keyalgo);
xfree (relptr); xfree (relptr);
return err; return err;
} }

View File

@ -1083,10 +1083,10 @@ cmd_writekey (assuan_context_t ctx, char *line)
static const char hlp_genkey[] = static const char hlp_genkey[] =
"GENKEY [--force] [--timestamp=<isodate>] <no>\n" "GENKEY [--force] [--timestamp=<isodate>] [--algo=ALGO] <keyref>\n"
"\n" "\n"
"Generate a key on-card identified by NO, which is application\n" "Generate a key on-card identified by <keyref>, which is application\n"
"specific. Return values are application specific. For OpenPGP\n" "specific. Return values are also application specific. For OpenPGP\n"
"cards 3 status lines are returned:\n" "cards 3 status lines are returned:\n"
"\n" "\n"
" S KEY-FPR <hexstring>\n" " S KEY-FPR <hexstring>\n"
@ -1105,16 +1105,21 @@ static const char hlp_genkey[] =
"value. The value needs to be in ISO Format; e.g.\n" "value. The value needs to be in ISO Format; e.g.\n"
"\"--timestamp=20030316T120000\" and after 1970-01-01 00:00:00.\n" "\"--timestamp=20030316T120000\" and after 1970-01-01 00:00:00.\n"
"\n" "\n"
"The option --algo can be used to request creation using a specific\n"
"algorithm. The possible algorithms are card dependent.\n"
"\n"
"The public part of the key can also later be retrieved using the\n" "The public part of the key can also later be retrieved using the\n"
"READKEY command."; "READKEY command.";
static gpg_error_t static gpg_error_t
cmd_genkey (assuan_context_t ctx, char *line) cmd_genkey (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
int rc; gpg_error_t err;
char *save_line; char *keyref_buffer = NULL;
char *keyref;
int force; int force;
const char *s; const char *s;
char *opt_algo = NULL;
time_t timestamp; time_t timestamp;
force = has_option (line, "--force"); force = has_option (line, "--force");
@ -1130,39 +1135,41 @@ cmd_genkey (assuan_context_t ctx, char *line)
else else
timestamp = 0; timestamp = 0;
err = get_option_value (line, "--algo", &opt_algo);
if (err)
goto leave;
line = skip_options (line); line = skip_options (line);
if (!*line) if (!*line)
{ return set_error (GPG_ERR_ASS_PARAMETER, "no key number given");
rc = set_error (GPG_ERR_ASS_PARAMETER, "no key number given"); keyref = line;
goto leave;
}
save_line = line;
while (*line && !spacep (line)) while (*line && !spacep (line))
line++; line++;
*line = 0; *line = 0;
if ((rc = open_card (ctrl))) if ((err = open_card (ctrl)))
goto leave; goto leave;
if (!ctrl->app_ctx) if (!ctrl->app_ctx)
{ {
rc = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
goto leave; goto leave;
} }
{ keyref = keyref_buffer = xtrystrdup (keyref);
char *tmp = xtrystrdup (save_line); if (!keyref)
if (!tmp) {
return gpg_error_from_syserror (); err = gpg_error_from_syserror ();
rc = app_genkey (ctrl->app_ctx, ctrl, tmp, NULL, goto leave;
force? APP_GENKEY_FLAG_FORCE : 0, }
timestamp, pin_cb, ctx); err = app_genkey (ctrl->app_ctx, ctrl, keyref, opt_algo,
xfree (tmp); force? APP_GENKEY_FLAG_FORCE : 0,
} timestamp, pin_cb, ctx);
leave: leave:
return rc; xfree (keyref_buffer);
xfree (opt_algo);
return err;
} }