aboutsummaryrefslogtreecommitdiffstats
path: root/g10
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--g10/ChangeLog11
-rw-r--r--g10/app-common.h10
-rw-r--r--g10/app-openpgp.c351
-rw-r--r--g10/cardglue.c98
-rw-r--r--g10/cardglue.h17
-rw-r--r--g10/keygen.c172
-rw-r--r--g10/tlv.c73
-rw-r--r--g10/tlv.h17
8 files changed, 554 insertions, 195 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog
index 8f05b7e5b..fc36891dd 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,14 @@
+2005-05-21 Werner Koch <[email protected]>
+
+ * cardglue.c (send_status_info): Make CTRL optional.
+ (agent_scd_writekey, inq_writekey_parms): New.
+ (agent_openpgp_storekey): Removed.
+ * cardglue.h: Add a few more error code mappings.
+ * keygen.c (copy_mpi): Removed.
+ (save_unprotected_key_to_card): Changed to use agent_scd_writekey.
+ * app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer
+ version in gnupg 1.9 CVS.
+
2005-05-20 Werner Koch <[email protected]>
* ccid-driver.c (ccid_transceive): Arghhh. The seqno is another
diff --git a/g10/app-common.h b/g10/app-common.h
index f1058dda1..c2c302395 100644
--- a/g10/app-common.h
+++ b/g10/app-common.h
@@ -86,6 +86,11 @@ struct app_ctx_s {
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen);
+ gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
+ const char *certid, unsigned int flags,
+ gpg_error_t (*pincb)(void*,const char *,char **),
+ void *pincb_arg,
+ const unsigned char *pk, size_t pklen);
gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
@@ -148,6 +153,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr,
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen );
+gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
+ const char *keyidstr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen);
gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c
index 6f4778ba5..1165ec683 100644
--- a/g10/app-openpgp.c
+++ b/g10/app-openpgp.c
@@ -565,7 +565,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
n = 6 + 2 + mlen + 2 + elen;
p = buffer = xtrymalloc (3 + n);
if (!buffer)
- return gpg_error (gpg_err_code_from_errno (errno));
+ return gpg_error_from_errno (errno);
*p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */
@@ -1527,6 +1527,318 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
}
+/* Check whether a key already exists. KEYIDX is the index of the key
+ (0..2). If FORCE is TRUE a diagnositivc will be printed but no
+ error returned if the key already exists. */
+static gpg_error_t
+does_key_exist (app_t app, int keyidx, int force)
+{
+ const unsigned char *fpr;
+ unsigned char *buffer;
+ size_t buflen, n;
+ int i;
+
+ assert (keyidx >=0 && keyidx <= 2);
+
+ if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
+ {
+ log_error (_("error reading application data\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
+ if (!fpr || n < 60)
+ {
+ log_error (_("error reading fingerprint DO\n"));
+ xfree (buffer);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr += 20*keyidx;
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ xfree (buffer);
+ if (i!=20 && !force)
+ {
+ log_error (_("key already exists\n"));
+ return gpg_error (GPG_ERR_EEXIST);
+ }
+ else if (i!=20)
+ log_info (_("existing key will be replaced\n"));
+ else
+ log_info (_("generating new key\n"));
+ return 0;
+}
+
+
+
+/* Handle the WRITEKEY command for OpenPGP. This function expects a
+ canonical encoded S-expression with the secret key in KEYDATA and
+ its length (for assertions) in KEYDATALEN. KEYID needs to be the
+ usual keyid which for OpenPGP is the string "OPENPGP.n" with
+ n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall
+ get overwritten. PINCB and PINCB_ARG are the usual arguments for
+ the pinentry callback. */
+static gpg_error_t
+do_writekey (app_t app, ctrl_t ctrl,
+ const char *keyid, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ gpg_error_t err;
+ int force = (flags & 1);
+ int keyno;
+ const unsigned char *buf, *tok;
+ size_t buflen, toklen;
+ int depth, last_depth1, last_depth2;
+ const unsigned char *rsa_n = NULL;
+ const unsigned char *rsa_e = NULL;
+ const unsigned char *rsa_p = NULL;
+ const unsigned char *rsa_q = NULL;
+ size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+ unsigned int nbits;
+ unsigned char *template = NULL;
+ unsigned char *tp;
+ size_t template_len;
+ unsigned char fprbuf[20];
+ u32 created_at = 0;
+
+ if (!strcmp (keyid, "OPENPGP.1"))
+ keyno = 0;
+ else if (!strcmp (keyid, "OPENPGP.2"))
+ keyno = 1;
+ else if (!strcmp (keyid, "OPENPGP.3"))
+ keyno = 2;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = does_key_exist (app, keyno, force);
+ if (err)
+ return err;
+
+
+ /*
+ Parse the S-expression
+ */
+ buf = keydata;
+ buflen = keydatalen;
+ depth = 0;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
+ {
+ if (!tok)
+ ;
+ else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
+ log_info ("protected-private-key passed to writekey\n");
+ else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
+ log_info ("shadowed-private-key passed to writekey\n");
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
+ {
+ err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+ goto leave;
+ }
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 1)
+ {
+ const unsigned char **mpi;
+ size_t *mpi_len;
+
+ switch (*tok)
+ {
+ case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
+ case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
+ case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
+ case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
+ default: mpi = NULL; mpi_len = NULL; break;
+ }
+ if (mpi && *mpi)
+ {
+ err = gpg_error (GPG_ERR_DUP_VALUE);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && mpi)
+ {
+ /* Strip off leading zero bytes and save. */
+ for (;toklen && !*tok; toklen--, tok++)
+ ;
+ *mpi = tok;
+ *mpi_len = toklen;
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+ /* Parse other attributes. */
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
+ {
+ if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
+ goto leave;
+ if (tok)
+ {
+ for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
+ tok++, toklen--)
+ created_at = created_at*10 + (*tok - '0');
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+
+
+ /* Check that we have all parameters and that they match the card
+ description. */
+ if (!created_at)
+ {
+ log_error (_("creation timestamp missing\n"));
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
+ if (nbits != 1024)
+ {
+ log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
+ if (nbits < 2 || nbits > 32)
+ {
+ log_error (_("RSA public exponent missing or largerr than %d bits\n"),
+ 32);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
+ if (nbits != 512)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
+ if (nbits != 512)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+
+ /* Build the private key template as described in section 4.3.3.6 of
+ the OpenPGP card specs:
+ 0xC0 <length> public exponent
+ 0xC1 <length> prime p
+ 0xC2 <length> prime q
+ */
+ assert (rsa_e_len <= 4);
+ template_len = (1 + 1 + 4
+ + 1 + 1 + rsa_p_len
+ + 1 + 1 + rsa_q_len);
+ template = tp = xtrymalloc_secure (template_len);
+ if (!template)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ *tp++ = 0xC0;
+ *tp++ = 4;
+ memcpy (tp, rsa_e, rsa_e_len);
+ if (rsa_e_len < 4)
+ {
+ /* Right justify E. */
+ memmove (tp+4-rsa_e_len, tp, 4-rsa_e_len);
+ memset (tp, 0, 4-rsa_e_len);
+ }
+ tp += 4;
+
+ *tp++ = 0xC1;
+ *tp++ = rsa_p_len;
+ memcpy (tp, rsa_p, rsa_p_len);
+ tp += rsa_p_len;
+
+ *tp++ = 0xC2;
+ *tp++ = rsa_q_len;
+ memcpy (tp, rsa_q, rsa_q_len);
+ tp += rsa_q_len;
+
+ assert (tp - template == template_len);
+
+
+ /* Obviously we need to remove the cached public key. */
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+ app->app_local->pk[keyno].read_done = 0;
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ goto leave;
+
+ /* Store the key. */
+ err = iso7816_put_data (app->slot,
+ (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
+ template, template_len);
+ if (err)
+ {
+ log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ err = store_fpr (app->slot, keyno, created_at,
+ rsa_n, rsa_n_len, rsa_e, rsa_e_len,
+ fprbuf, app->card_version);
+ if (err)
+ goto leave;
+
+
+ leave:
+ xfree (template);
+ return err;
+}
+
/* Handle the GENKEY command. */
static gpg_error_t
@@ -1535,13 +1847,11 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
void *pincb_arg)
{
int rc;
- int i;
char numbuf[30];
unsigned char fprbuf[20];
- const unsigned char *fpr;
const unsigned char *keydata, *m, *e;
- unsigned char *buffer;
- size_t buflen, keydatalen, n, mlen, elen;
+ unsigned char *buffer = NULL;
+ size_t buflen, keydatalen, mlen, elen;
time_t created_at;
int keyno = atoi (keynostr);
int force = (flags & 1);
@@ -1562,41 +1872,15 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
app->app_local->pk[keyno].read_done = 0;
/* Check whether a key already exists. */
- rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
+ rc = does_key_exist (app, keyno, force);
if (rc)
- {
- log_error (_("error reading application data\n"));
- return gpg_error (GPG_ERR_GENERAL);
- }
- fpr = find_tlv (buffer, buflen, 0x00C5, &n);
- if (!fpr || n != 60)
- {
- rc = gpg_error (GPG_ERR_GENERAL);
- log_error (_("error reading fingerprint DO\n"));
- goto leave;
- }
- fpr += 20*keyno;
- for (i=0; i < 20 && !fpr[i]; i++)
- ;
- if (i!=20 && !force)
- {
- rc = gpg_error (GPG_ERR_EEXIST);
- log_error (_("key already exists\n"));
- goto leave;
- }
- else if (i!=20)
- log_info (_("existing key will be replaced\n"));
- else
- log_info (_("generating new key\n"));
+ return rc;
-
/* Prepare for key generation by verifying the ADmin PIN. */
rc = verify_chv3 (app, pincb, pincb_arg);
if (rc)
goto leave;
- xfree (buffer); buffer = NULL;
-
#if 1
log_info (_("please wait while key is being generated ...\n"));
start_at = time (NULL);
@@ -2220,6 +2504,7 @@ app_select_openpgp (app_t app)
app->fnc.readkey = do_readkey;
app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr;
+ app->fnc.writekey = do_writekey;
app->fnc.genkey = do_genkey;
app->fnc.sign = do_sign;
app->fnc.auth = do_auth;
diff --git a/g10/cardglue.c b/g10/cardglue.c
index 940ec64a3..01d8b4292 100644
--- a/g10/cardglue.c
+++ b/g10/cardglue.c
@@ -63,6 +63,15 @@ struct pincb_parm_s
};
+struct writekey_parm_s
+{
+ assuan_context_t ctx;
+ const unsigned char *keydata;
+ size_t keydatalen;
+};
+
+
+
static char *default_reader_port;
static app_t current_app;
@@ -100,7 +109,7 @@ serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
buffers. The variable elements are pairs of (char *, size_t),
terminated with a (NULL, 0). */
void
-send_status_info (CTRL ctrl, const char *keyword, ...)
+send_status_info (ctrl_t ctrl, const char *keyword, ...)
{
va_list arg_ptr;
const unsigned char *value;
@@ -140,7 +149,8 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
}
}
*p = 0;
- ctrl->status_cb (ctrl->status_cb_arg, buf);
+ if (ctrl && ctrl->status_cb)
+ ctrl->status_cb (ctrl->status_cb_arg, buf);
va_end (arg_ptr);
}
@@ -970,6 +980,59 @@ agent_scd_setattr (const char *name,
}
+/* Handle a KEYDATA inquiry. Note, we only send the data,
+ assuan_transact takes care of flushing and writing the end */
+static assuan_error_t
+inq_writekey_parms (void *opaque, const char *keyword)
+{
+ struct writekey_parm_s *parm = opaque;
+
+ return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen);
+}
+
+
+/* Send a WRITEKEY command to the SCdaemon. */
+int
+agent_scd_writekey (int keyno, const unsigned char *keydata, size_t keydatalen)
+{
+ app_t app;
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ struct writekey_parm_s parms;
+
+ snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno);
+ line[DIM(line)-1] = 0;
+ parms.ctx = app->assuan_ctx;
+ parms.keydata = keydata;
+ parms.keydatalen = keydatalen;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL,
+ inq_writekey_parms, &parms,
+ NULL, NULL),
+ "SCD WRITEKEY");
+ }
+ else
+ {
+ snprintf (line, DIM(line)-1, "OPENPGP.%d", keyno);
+ line[DIM(line)-1] = 0;
+ rc = app->fnc.writekey (app, NULL, line, 0x0001,
+ pin_cb, NULL,
+ keydata, keydatalen);
+ }
+
+ if (rc)
+ write_status (STATUS_SC_OP_FAILURE);
+ return rc;
+}
+
+
+
static assuan_error_t
genkey_status_cb (void *opaque, const char *line)
{
@@ -1281,37 +1344,6 @@ agent_scd_checkpin (const char *serialnobuf)
}
-/* Wrapper to call the store key helper function of app-openpgp.c. */
-int
-agent_openpgp_storekey (int keyno,
- unsigned char *template, size_t template_len,
- time_t created_at,
- const unsigned char *m, size_t mlen,
- const unsigned char *e, size_t elen)
-{
- app_t app;
- int rc;
-
- app = current_app? current_app : open_card ();
- if (!app)
- return gpg_error (GPG_ERR_CARD);
-
- if (app->assuan_ctx)
- {
- rc = gpg_error (GPG_ERR_CARD);
- }
- else
- {
- rc = app_openpgp_storekey (app, keyno, template, template_len,
- created_at, m, mlen, e, elen,
- pin_cb, NULL);
- }
-
- if (rc)
- write_status (STATUS_SC_OP_FAILURE);
- return rc;
-}
-
void
agent_clear_pin_cache (const char *sn)
diff --git a/g10/cardglue.h b/g10/cardglue.h
index 068a1aacf..a679dbb95 100644
--- a/g10/cardglue.h
+++ b/g10/cardglue.h
@@ -81,6 +81,7 @@ typedef struct ctrl_ctx_s *ctrl_t;
#define GPG_ERR_GENERAL G10ERR_GENERAL
#define GPG_ERR_BAD_PIN G10ERR_BAD_PASS
+#define GPG_ERR_BAD_KEy G10ERR_BAD_KEY
#define GPG_ERR_CARD G10ERR_GENERAL
#define GPG_ERR_EEXIST G10ERR_FILE_EXISTS
#define GPG_ERR_ENOMEM G10ERR_RESOURCE_LIMIT
@@ -105,6 +106,10 @@ typedef struct ctrl_ctx_s *ctrl_t;
#define GPG_ERR_EOF (-1)
#define GPG_ERR_CARD_NOT_PRESENT G10ERR_NO_CARD
#define GPG_ERR_CARD_RESET G10ERR_GENERAL
+#define GPG_ERR_WRONG_PUBKEY_ALGO G10ERR_PUBKEY_ALGO
+#define GPG_ERR_UNKNOWN_SEXP G10ERR_INV_ARG
+#define GPG_ERR_DUP_VALUE G10ERR_INV_ARG
+#define GPG_ERR_BAD_SECKEY G10ERR_BAD_SECKEY
#define GPG_ERR_EBUSY G10ERR_GENERAL
#define GPG_ERR_ENOENT G10ERR_OPEN_FILE
@@ -129,6 +134,7 @@ typedef int gpg_err_code_t;
#define xtrymalloc(n) xmalloc((n))
#define xtrycalloc(n,m) xcalloc((n),(m))
#define xtryrealloc(n,m) xrealloc((n),(m))
+#define xtrymalloc_secure(n) xmalloc_secure((n))
#define out_of_core() (-1)
#define gnupg_get_time() make_timestamp ()
@@ -168,6 +174,10 @@ int agent_scd_getattr (const char *name, struct agent_card_info_s *info);
int agent_scd_setattr (const char *name,
const unsigned char *value, size_t valuelen);
+/* Send a WRITEKEY command to the SCdaemon. */
+int agent_scd_writekey (int keyno,
+ const unsigned char *keydata, size_t keydatalen);
+
/* Send a GENKEY command to the SCdaemon. */
int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force);
@@ -187,13 +197,6 @@ int agent_scd_change_pin (int chvno);
/* Send a CHECKPIN command. */
int agent_scd_checkpin (const char *serialnobuf);
-/* Call the store key utility command. */
-int agent_openpgp_storekey (int keyno,
- unsigned char *template, size_t template_len,
- time_t created_at,
- const unsigned char *m, size_t mlen,
- const unsigned char *e, size_t elen);
-
/* Clear a cached PIN. */
void agent_clear_pin_cache (const char *sn);
diff --git a/g10/keygen.c b/g10/keygen.c
index c4b9dab8f..b5408a6aa 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -128,42 +128,6 @@ static int gen_card_key_with_backup (int algo, int keyno, int is_primary,
const char *backup_dir);
-#if GNUPG_MAJOR_VERSION == 1
-#define GET_NBITS(a) mpi_get_nbits (a)
-#else
-#define GET_NBITS(a) gcry_mpi_get_nbits (a)
-#endif
-
-#ifdef ENABLE_CARD_SUPPORT
-static int
-copy_mpi (MPI a, unsigned char *buffer, size_t len, size_t *ncopied)
-{
- int rc;
-#if GNUPG_MAJOR_VERSION == 1
- unsigned char *tmp;
- unsigned int n;
-
- tmp = mpi_get_secure_buffer (a, &n, NULL);
- if (n > len)
- rc = G10ERR_GENERAL;
- else
- {
- rc = 0;
- memcpy (buffer, tmp, n);
- *ncopied = n;
- }
- xfree (tmp);
-#else /* GNUPG_MAJOR_VERSION != 1 */
- rc = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, len, ncopied, a);
-#endif /* GNUPG_MAJOR_VERSION != 1 */
- if (rc)
- log_error ("mpi_copy failed: %s\n", gpg_strerror (rc));
- return rc;
-}
-#endif /* ENABLE_CARD_SUPPORT */
-
-
-
static void
print_status_key_created (int letter, PKT_public_key *pk, const char *handle)
{
@@ -3527,104 +3491,68 @@ int
save_unprotected_key_to_card (PKT_secret_key *sk, int keyno)
{
int rc;
- size_t n;
- MPI rsa_n, rsa_e, rsa_p, rsa_q;
- unsigned int nbits;
- unsigned char *template = NULL;
- unsigned char *tp;
- unsigned char m[128], e[4];
- size_t mlen, elen;
+ unsigned char *rsa_n = NULL;
+ unsigned char *rsa_e = NULL;
+ unsigned char *rsa_p = NULL;
+ unsigned char *rsa_q = NULL;
+ unsigned int rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+ unsigned char *sexp = NULL;
+ unsigned char *p;
+ char numbuf[55], numbuf2[50];
assert (is_RSA (sk->pubkey_algo));
assert (!sk->is_protected);
- rc = -1;
- /* Some basic checks on the key parameters. */
- rsa_n = sk->skey[0];
- rsa_e = sk->skey[1];
- rsa_p = sk->skey[3];
- rsa_q = sk->skey[4];
-
- nbits = GET_NBITS (rsa_n);
- if (nbits != 1024)
+ /* Copy the parameters into straight buffers. */
+ rsa_n = mpi_get_secure_buffer (sk->skey[0], &rsa_n_len, NULL);
+ rsa_e = mpi_get_secure_buffer (sk->skey[1], &rsa_e_len, NULL);
+ rsa_p = mpi_get_secure_buffer (sk->skey[3], &rsa_p_len, NULL);
+ rsa_q = mpi_get_secure_buffer (sk->skey[4], &rsa_q_len, NULL);
+ if (!rsa_n || !rsa_e || !rsa_p || !rsa_q)
{
- log_error (_("length of RSA modulus is not %d\n"), 1024);
- goto leave;
- }
- nbits = GET_NBITS (rsa_e);
- if (nbits < 2 || nbits > 32)
- {
- log_error (_("public exponent too large (more than 32 bits)\n"));
- goto leave;
- }
- nbits = GET_NBITS (rsa_p);
- if (nbits != 512)
- {
- log_error (_("length of an RSA prime is not %d\n"), 512);
- goto leave;
- }
- nbits = GET_NBITS (rsa_q);
- if (nbits != 512)
- {
- log_error (_("length of an RSA prime is not %d\n"), 512);
+ rc = G10ERR_INV_ARG;
goto leave;
}
-
- /* We need the modulus later to calculate the fingerprint. */
- rc = copy_mpi (rsa_n, m, 128, &n);
- if (rc)
- goto leave;
- assert (n == 128);
- mlen = 128;
-
- /* Build the private key template as described in section 4.3.3.6 of
- the OpenPGP card specs:
- 0xC0 <length> public exponent
- 0xC1 <length> prime p
- 0xC2 <length> prime q
- */
- template = tp = xmalloc_secure (1+2 + 1+1+4 + 1+1+(512/8) + 1+1+(512/8));
- *tp++ = 0xC0;
- *tp++ = 4;
- rc = copy_mpi (rsa_e, tp, 4, &n);
- if (rc)
- goto leave;
- assert (n <= 4);
- memcpy (e, tp, n); /* Save a copy of the exponent for later use. */
- elen = n;
- if (n != 4)
- {
- memmove (tp+4-n, tp, 4-n);
- memset (tp, 0, 4-n);
- }
- tp += 4;
-
- *tp++ = 0xC1;
- *tp++ = 64;
- rc = copy_mpi (rsa_p, tp, 64, &n);
- if (rc)
- goto leave;
- assert (n == 64);
- tp += 64;
+ /* Put the key into an S-expression. */
+ sexp = p = xmalloc_secure (30
+ + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len
+ + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20);
- *tp++ = 0xC2;
- *tp++ = 64;
- rc = copy_mpi (rsa_q, tp, 64, &n);
- if (rc)
- goto leave;
- assert (n == 64);
- tp += 64;
- assert (tp - template == 138);
+ p = stpcpy (p,"(11:private-key(3:rsa(1:n");
+ sprintf (numbuf, "%u:", rsa_n_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_n, rsa_n_len);
+ p += rsa_n_len;
+
+ sprintf (numbuf, ")(1:e%u:", rsa_e_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_e, rsa_e_len);
+ p += rsa_e_len;
+
+ sprintf (numbuf, ")(1:p%u:", rsa_p_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_p, rsa_p_len);
+ p += rsa_p_len;
+
+ sprintf (numbuf, ")(1:q%u:", rsa_q_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_q, rsa_q_len);
+ p += rsa_q_len;
+
+ p = stpcpy (p,"))(10:created-at");
+ sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp);
+ sprintf (numbuf, "%d:", strlen (numbuf2));
+ p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))");
- rc = agent_openpgp_storekey (keyno,
- template, tp - template,
- sk->timestamp,
- m, mlen,
- e, elen);
+ rc = agent_scd_writekey (keyno, sexp, p - sexp);
leave:
- xfree (template);
+ xfree (sexp);
+ xfree (rsa_n);
+ xfree (rsa_e);
+ xfree (rsa_p);
+ xfree (rsa_q);
return rc;
}
#endif /*ENABLE_CARD_SUPPORT*/
diff --git a/g10/tlv.c b/g10/tlv.c
index 3a81ea6d9..b5dcd4021 100644
--- a/g10/tlv.c
+++ b/g10/tlv.c
@@ -221,3 +221,76 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
*size = length;
return 0;
}
+
+
+/* FIXME: The following function should not go into this file but for
+ now it is easier to keep it here. */
+
+/* Return the next token of an canconical encoded S-expression. BUF
+ is the pointer to the S-expression and BUFLEN is a pointer to the
+ length of this S-expression (used to validate the syntax). Both
+ are updated to reflect the new position. The token itself is
+ returned as a pointer into the orginal buffer at TOK and TOKLEN.
+ If a parentheses is the next token, TOK will be set to NULL.
+ TOKLEN is checked to be within the bounds. On error a error code
+ is returned and all pointers should are not guaranteed to point to
+ a meanigful value. DEPTH should be initialized to 0 and will
+ reflect on return the actual depth of the tree. To detect the end
+ of the S-expression it is advisable to check DEPTH after a
+ successful return:
+
+ depth = 0;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth)
+ process_token (tok, toklen);
+ if (err)
+ handle_error ();
+ */
+gpg_error_t
+parse_sexp (unsigned char const **buf, size_t *buflen,
+ int *depth, unsigned char const **tok, size_t *toklen)
+{
+ const unsigned char *s;
+ size_t n, vlen;
+
+ s = *buf;
+ n = *buflen;
+ *tok = NULL;
+ *toklen = 0;
+ if (!n)
+ return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
+ if (*s == '(')
+ {
+ s++; n--;
+ (*depth)++;
+ *buf = s;
+ *buflen = n;
+ return 0;
+ }
+ if (*s == ')')
+ {
+ if (!*depth)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ *toklen = 1;
+ s++; n--;
+ (*depth)--;
+ *buf = s;
+ *buflen = n;
+ return 0;
+ }
+ for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
+ vlen = vlen*10 + (*s - '0');
+ if (!n || *s != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++; n--;
+ if (vlen > n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ *tok = s;
+ *toklen = vlen;
+ s += vlen;
+ n -= vlen;
+ *buf = s;
+ *buflen = n;
+ return 0;
+}
+
diff --git a/g10/tlv.h b/g10/tlv.h
index 628580431..f587dd9df 100644
--- a/g10/tlv.h
+++ b/g10/tlv.h
@@ -88,4 +88,21 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
+/* Return the next token of an canconical encoded S-expression. BUF
+ is the pointer to the S-expression and BUFLEN is a pointer to the
+ length of this S-expression (used to validate the syntax). Both
+ are updated to reflect the new position. The token itself is
+ returned as a pointer into the orginal buffer at TOK and TOKLEN.
+ If a parentheses is the next token, TOK will be set to NULL.
+ TOKLEN is checked to be within the bounds. On error a error code
+ is returned and all pointers should are not guaranteed to point to
+ a meanigful value. DEPTH should be initialized to 0 and will
+ reflect on return the actual depth of the tree. To detect the end
+ of the S-expression it is advisable to check DEPTH after a
+ successful return. */
+gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen,
+ int *depth, unsigned char const **tok, size_t *toklen);
+
+
+
#endif /* SCD_TLV_H */