1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

scd:piv: Implement import of private keys for Yubikeys.

* scd/app-piv.c (concat_tlv_list): Add arg 'secure' and adjust
 callers.
(writekey_rsa, writekey_ecc): New.
(do_writekey): New.
(do_writecert): Provide a better error message for an empty cert.
(app_select_piv): Register do_writekey.
* scd/iso7816.c (iso7816_send_apdu): New.
* scd/app-common.h (APP_WRITEKEY_FLAG_FORCE): New.
* agent/command.c (cmd_keytocard): Make the timestamp optional.
* tools/card-call-scd.c (inq_writekey_parms): Remove.
(scd_writekey): Rewrite.
* tools/gpg-card.c (cmd_writekey): New.
(enum cmdids): Add cmdWRITEKEY.
(dispatch_command, interactive_loop): Call cmd_writekey.
--

This has been tested with gpgsm and RSA keys.  For ECC keys only
partly tested using the sample OpenPGP nistp256 and nistp384 keys
because gpgsm does not yet support ECC certificates and thus we can't
write the certificates to the cert object after a writekey.  Note that
they nevertheless show up in "gpgcard list" because gpg-card searches
for them in gpg and gpgsm.  However, this does not work completely.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-03-05 15:49:20 +01:00
parent db87132b10
commit e897e1e255
No known key found for this signature in database
GPG key ID: E3FDFF218E45B72B
9 changed files with 561 additions and 54 deletions

View file

@ -1155,49 +1155,30 @@ scd_writecert (const char *certidstr,
/* Handle a KEYDATA inquiry. Note, we only send the data,
assuan_transact takes care of flushing and writing the end */
static gpg_error_t
inq_writekey_parms (void *opaque, const char *line)
{
gpg_error_t err;
struct writekey_parm_s *parm = opaque;
if (has_leading_keyword (line, "KEYDATA"))
{
err = assuan_send_data (parm->dflt->ctx, parm->keydata, parm->keydatalen);
}
else
err = default_inq_cb (parm->dflt, line);
return err;
}
/* Send a WRITEKEY command to the SCdaemon. */
/* Send a WRITEKEY command to the agent (so that the agent can fetch
* the key to write). KEYGRIP is the hexified keygrip of the source
* key which will be written to tye slot KEYREF. FORCE must be true
* to overwrite an existing key. */
gpg_error_t
scd_writekey (int keyno, const unsigned char *keydata, size_t keydatalen)
scd_writekey (const char *keyref, int force, const char *keygrip)
{
gpg_error_t err;
struct default_inq_parm_s parm;
char line[ASSUAN_LINELENGTH];
struct writekey_parm_s parms;
struct default_inq_parm_s dfltparm;
memset (&parms, 0, sizeof parms);
memset (&dfltparm, 0, sizeof dfltparm);
memset (&parm, 0, sizeof parm);
err = start_agent (0);
if (err)
return err;
snprintf (line, sizeof line, "SCD WRITEKEY --force OPENPGP.%d", keyno);
dfltparm.ctx = agent_ctx;
parms.dflt = &dfltparm;
parms.keydata = keydata;
parms.keydatalen = keydatalen;
/* Note: We don't send the s/n but "-" because gpg-agent has
* currently no use for it. */
/* FIXME: For OpenPGP we should provide the creation time. */
snprintf (line, sizeof line, "KEYTOCARD%s %s - %s",
force? " --force":"", keygrip, keyref);
err = assuan_transact (agent_ctx, line, NULL, NULL,
inq_writekey_parms, &parms, NULL, NULL);
default_inq_cb, &parm, NULL, NULL);
return status_sc_op_failure (err);
}

View file

@ -1667,7 +1667,7 @@ cmd_readcert (card_info_t info, char *argstr)
if (!info)
return print_help
("READCERT CERTREF > FILE\n\n"
"Read the certificate for key 3 and store it in FILE.",
"Read the certificate for key CERTREF and store it in FILE.",
APP_TYPE_OPENPGP, APP_TYPE_PIV, 0);
argstr = skip_options (argstr);
@ -1718,6 +1718,62 @@ cmd_readcert (card_info_t info, char *argstr)
}
static gpg_error_t
cmd_writekey (card_info_t info, char *argstr)
{
gpg_error_t err;
int opt_force;
char *argv[2];
int argc;
char *keyref_buffer = NULL;
char *keyref;
char *keygrip;
if (!info)
return print_help
("WRITEKEY [--force] KEYREF KEYGRIP\n\n"
"Write a private key object identified by KEYGRIP to slot KEYREF.\n"
"Use --force to overwrite an existing key.",
APP_TYPE_OPENPGP, APP_TYPE_PIV, 0);
opt_force = has_leading_option (argstr, "--force");
argstr = skip_options (argstr);
argc = split_fields (argstr, argv, DIM (argv));
if (argc < 2)
{
err = gpg_error (GPG_ERR_INV_ARG);
goto leave;
}
/* Upcase the keyref; prepend cardtype if needed. */
keyref = argv[0];
if (!strchr (keyref, '.'))
keyref_buffer = xstrconcat (app_type_string (info->apptype), ".",
keyref, NULL);
else
keyref_buffer = xstrdup (keyref);
ascii_strupr (keyref_buffer);
keyref = keyref_buffer;
/* Get the keygrip. */
keygrip = argv[1];
if (strlen (keygrip) != 40
&& !(keygrip[0] == '&' && strlen (keygrip+1) == 40))
{
log_error (_("Not a valid keygrip (expecting 40 hex digits)\n"));
err = gpg_error (GPG_ERR_INV_ARG);
goto leave;
}
err = scd_writekey (keyref, opt_force, keygrip);
leave:
xfree (keyref_buffer);
return err;
}
static gpg_error_t
cmd_forcesig (card_info_t info)
{
@ -2683,7 +2739,7 @@ ask_card_keyattr (int keyno, const struct key_attr *current,
(void)algo;
err = GPG_ERR_NOT_IMPLEMENTED;
goto leave;
/* FIXME: We need to mve the ask_cure code out to common or
/* FIXME: We need to move the ask_cure code out to common or
* provide another sultion. */
/* curve = ask_curve (&algo, NULL, curve); */
/* if (curve) */
@ -2747,7 +2803,7 @@ do_change_keyattr (int keyno, const struct key_attr *key_attr)
keyno+1, key_attr->algo, key_attr->curve);
else
{
/* FIXME: Above we use opnepgp algo names but in the error
/* FIXME: Above we use openpgp algo names but in the error
* message we use the gcrypt names. We should settle for a
* consistent solution. */
log_error (_("public key algorithm %d (%s) is not supported\n"),
@ -2918,7 +2974,7 @@ enum cmdids
cmdQUIT, cmdHELP, cmdLIST, cmdRESET, cmdVERIFY,
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
cmdKEYATTR, cmdUIF, cmdAUTH, cmdYUBIKEY,
cmdINVCMD
};
@ -2958,6 +3014,7 @@ static struct
{ "privatedo", cmdPRIVATEDO, N_("change a private data object")},
{ "readcert", cmdREADCERT, N_("read a certificate from a data object")},
{ "writecert", cmdWRITECERT, N_("store a certificate to a data object")},
{ "writekey", cmdWRITEKEY, N_("store a private key to a data object")},
{ "yubikey", cmdYUBIKEY, N_("Yubikey management commands")},
{ NULL, cmdINVCMD, NULL }
};
@ -3084,6 +3141,7 @@ dispatch_command (card_info_t info, const char *orig_command)
case cmdPRIVATEDO: err = cmd_privatedo (info, argstr); break;
case cmdWRITECERT: err = cmd_writecert (info, argstr); break;
case cmdREADCERT: err = cmd_readcert (info, argstr); break;
case cmdWRITEKEY: err = cmd_writekey (info, argstr); break;
case cmdFORCESIG: err = cmd_forcesig (info); break;
case cmdGENERATE: err = cmd_generate (info, argstr); break;
case cmdPASSWD: err = cmd_passwd (info, argstr); break;
@ -3314,6 +3372,7 @@ interactive_loop (void)
case cmdPRIVATEDO: err = cmd_privatedo (info, argstr); break;
case cmdWRITECERT: err = cmd_writecert (info, argstr); break;
case cmdREADCERT: err = cmd_readcert (info, argstr); break;
case cmdWRITEKEY: err = cmd_writekey (info, argstr); break;
case cmdFORCESIG: err = cmd_forcesig (info); break;
case cmdGENERATE: err = cmd_generate (info, argstr); break;
case cmdPASSWD: err = cmd_passwd (info, argstr); break;

View file

@ -208,8 +208,7 @@ gpg_error_t scd_setattr (const char *name,
const unsigned char *value, size_t valuelen);
gpg_error_t scd_writecert (const char *certidstr,
const unsigned char *certdata, size_t certdatalen);
gpg_error_t scd_writekey (int keyno,
const unsigned char *keydata, size_t keydatalen);
gpg_error_t scd_writekey (const char *keyref, int force, const char *keygrip);
gpg_error_t scd_genkey (const char *keyref, int force, const char *algo,
u32 *createtime);
gpg_error_t scd_serialno (char **r_serialno, const char *demand);