From 30f8a3c8736451d8c06ef72521a8da5eabf23016 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 6 Feb 2013 14:00:05 +0900 Subject: [PATCH] agent: Add KEYTOCARD command. * agent/agent.h (divert_writekey, agent_card_writekey): New. * agent/call-scd.c (inq_writekey_parms, agent_card_writekey): New. * agent/command.c (cmd_keytocard, hlp_keytocard): New. (register_commands): Add cmd_keytocard. * agent/divert-scd.c (divert_writekey): New. --- agent/agent.h | 7 +++ agent/call-scd.c | 58 +++++++++++++++++++++ agent/command.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ agent/divert-scd.c | 7 +++ 4 files changed, 197 insertions(+) diff --git a/agent/agent.h b/agent/agent.h index 8b1cae97a..030b29520 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -412,6 +412,8 @@ int divert_pkdecrypt (ctrl_t ctrl, char **r_buf, size_t *r_len); int divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context); +int divert_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, size_t keydatalen); /*-- call-scd.c --*/ @@ -445,6 +447,11 @@ int agent_card_pkdecrypt (ctrl_t ctrl, int agent_card_readcert (ctrl_t ctrl, const char *id, char **r_buf, size_t *r_buflen); int agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf); +int agent_card_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, + size_t keydatalen, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg); gpg_error_t agent_card_getattr (ctrl_t ctrl, const char *name, char **result); int agent_card_scd (ctrl_t ctrl, const char *cmdline, int (*getpin_cb)(void *, const char *, char*, size_t), diff --git a/agent/call-scd.c b/agent/call-scd.c index 00b34edf7..cbe4d1c34 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -1050,6 +1050,64 @@ agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf) } +struct writekey_parm_s +{ + assuan_context_t ctx; + int (*getpin_cb)(void *, const char *, char*, size_t); + void *getpin_cb_arg; + assuan_context_t passthru; + int any_inq_seen; + /**/ + const unsigned char *keydata; + size_t keydatalen; +}; + +/* 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) +{ + struct writekey_parm_s *parm = opaque; + + if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7])) + return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen); + else + return inq_needpin (opaque, line); +} + + +int +agent_card_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, size_t keydatalen, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + struct writekey_parm_s parms; + + (void)serialno; + rc = start_scd (ctrl); + if (rc) + return rc; + + snprintf (line, DIM(line)-1, "WRITEKEY %s%s", force ? "--force " : "", id); + line[DIM(line)-1] = 0; + parms.ctx = ctrl->scd_local->ctx; + parms.getpin_cb = getpin_cb; + parms.getpin_cb_arg = getpin_cb_arg; + parms.passthru = 0; + parms.any_inq_seen = 0; + parms.keydata = keydata; + parms.keydatalen = keydatalen; + + rc = assuan_transact (ctrl->scd_local->ctx, line, NULL, NULL, + inq_writekey_parms, &parms, NULL, NULL); + if (parms.any_inq_seen && (gpg_err_code(rc) == GPG_ERR_CANCELED || + gpg_err_code(rc) == GPG_ERR_ASS_CANCELED)) + rc = cancel_inquire (ctrl, rc); + return unlock_scd (ctrl, rc); +} /* Type used with the card_getattr_cb. */ struct card_getattr_parm_s { diff --git a/agent/command.c b/agent/command.c index 715e70a94..2844398f6 100644 --- a/agent/command.c +++ b/agent/command.c @@ -2119,9 +2119,133 @@ cmd_export_key (assuan_context_t ctx, char *line) return leave_cmd (ctx, err); } + +static const char hlp_keytocard[] = + "KEYTOCARD [--force] \n" + "\n"; +static gpg_error_t +cmd_keytocard (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int force; + gpg_error_t err = 0; + unsigned char grip[20]; + gcry_sexp_t s_skey = NULL; + gcry_sexp_t s_pkey = NULL; + unsigned char *keydata; + size_t keydatalen, timestamplen; + const char *serialno, *timestamp_str, *id; + unsigned char *shadow_info; + unsigned char *shdkey; + time_t timestamp; + force = has_option (line, "--force"); + line = skip_options (line); + err = parse_keygrip (ctx, line, grip); + if (err) + return err; + if (agent_key_available (grip)) + return gpg_error (GPG_ERR_NO_SECKEY); + + line += 40; + while (*line && (*line == ' ' || *line == '\t')) + line++; + serialno = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (!*line) + return gpg_error (GPG_ERR_MISSING_VALUE); + *line = '\0'; + line++; + while (*line && (*line == ' ' || *line == '\t')) + line++; + id = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (!*line) + return gpg_error (GPG_ERR_MISSING_VALUE); + *line = '\0'; + line++; + while (*line && (*line == ' ' || *line == '\t')) + line++; + timestamp_str = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (*line) + *line = '\0'; + timestamplen = line - timestamp_str; + if (timestamplen != 15) + return gpg_error (GPG_ERR_INV_VALUE); + + err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip, + NULL, CACHE_MODE_IGNORE, NULL, &s_skey, NULL); + if (err) + return err; + if (!s_skey) + /* Key is on a smartcard already. */ + return gpg_error (GPG_ERR_UNUSABLE_SECKEY); + + keydatalen = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); + keydata = xtrymalloc_secure (keydatalen + 30); + if (keydata == NULL) + { + gcry_sexp_release (s_skey); + return gpg_error_from_syserror (); + } + + gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, keydata, keydatalen); + gcry_sexp_release (s_skey); + /* Add timestamp "created-at" in the private key */ + timestamp = isotime2epoch (timestamp_str); + snprintf (keydata+keydatalen-1, 30, "(10:created-at10:%010lu))", timestamp); + keydatalen += 10 + 19 - 1; + err = divert_writekey (ctrl, force, serialno, id, keydata, keydatalen); + if (err) + { + xfree (keydata); + goto leave; + } + xfree (keydata); + + err = agent_public_key_from_file (ctrl, grip, &s_pkey); + if (err) + goto leave; + + shadow_info = make_shadow_info (serialno, id); + if (!shadow_info) + { + err = gpg_error (GPG_ERR_ENOMEM); + gcry_sexp_release (s_pkey); + goto leave; + } + keydatalen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); + keydata = xtrymalloc (keydatalen); + if (keydata == NULL) + { + err = gpg_error_from_syserror (); + gcry_sexp_release (s_pkey); + goto leave; + } + gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keydata, keydatalen); + gcry_sexp_release (s_pkey); + err = agent_shadow_key (keydata, shadow_info, &shdkey); + xfree (keydata); + xfree (shadow_info); + if (err) + { + log_error ("shadowing the key failed: %s\n", gpg_strerror (err)); + goto leave; + } + + keydatalen = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); + err = agent_write_private_key (grip, shdkey, keydatalen, 1); + xfree (shdkey); + + leave: + return leave_cmd (ctx, err); +} static const char hlp_getval[] = "GETVAL \n" @@ -2674,6 +2798,7 @@ register_commands (assuan_context_t ctx) { "KILLAGENT", cmd_killagent, hlp_killagent }, { "RELOADAGENT", cmd_reloadagent,hlp_reloadagent }, { "GETINFO", cmd_getinfo, hlp_getinfo }, + { "KEYTOCARD", cmd_keytocard, hlp_keytocard }, { NULL } }; int i, rc; diff --git a/agent/divert-scd.c b/agent/divert-scd.c index 5d53891be..5fb037ee5 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -442,6 +442,13 @@ divert_pkdecrypt (ctrl_t ctrl, return rc; } +int +divert_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, size_t keydatalen) +{ + return agent_card_writekey (ctrl, force, serialno, id, keydata, keydatalen, + getpin_cb, ctrl); +} int divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context)