diff options
-rw-r--r-- | agent/ChangeLog | 9 | ||||
-rw-r--r-- | agent/agent.h | 9 | ||||
-rw-r--r-- | agent/call-scd.c | 53 | ||||
-rw-r--r-- | agent/divert-scd.c | 60 | ||||
-rw-r--r-- | agent/pkdecrypt.c | 45 | ||||
-rw-r--r-- | scd/card.c | 100 | ||||
-rw-r--r-- | scd/command.c | 35 | ||||
-rw-r--r-- | scd/scdaemon.h | 6 | ||||
-rw-r--r-- | sm/ChangeLog | 5 | ||||
-rw-r--r-- | sm/base64.c | 5 | ||||
-rw-r--r-- | sm/decrypt.c | 8 |
11 files changed, 305 insertions, 30 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog index 94fb00241..bc0ca11ef 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,12 +1,19 @@ +2002-03-06 Werner Koch <[email protected]> + + * pkdecrypt.c (agent_pkdecrypt): Changed the way the diversion is done. + * divert-scd.c (divert_pkdecrypt): Changed interface and + implemented it. + 2002-03-05 Werner Koch <[email protected]> * call-scd.c (inq_needpin): New. (agent_card_pksign): Add getpin_cb args. + (agent_card_pkdecrypt): New. 2002-03-04 Werner Koch <[email protected]> * pksign.c (agent_pksign): Changed how the diversion is done. - * divert-scd.c (divert_pksign): Change interface and implemented it. + * divert-scd.c (divert_pksign): Changed interface and implemented it. (encode_md_for_card): New. * call-scd.c (agent_card_pksign): New. diff --git a/agent/agent.h b/agent/agent.h index 643ed60a1..2d92f94be 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -153,8 +153,8 @@ int agent_marktrusted (const char *name, const char *fpr, int flag); /*-- divert-scd.c --*/ int divert_pksign (const unsigned char *digest, size_t digestlen, int algo, const char *shadow_info, unsigned char **r_sig); -int divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher, - const char *shadow_info); +int divert_pkdecrypt (const unsigned char *cipher, const char *shadow_info, + char **r_buf, size_t *r_len); /*-- call-scd.c --*/ int agent_card_learn (void); @@ -164,6 +164,11 @@ int agent_card_pksign (const char *keyid, void *getpin_cb_arg, const unsigned char *indata, size_t indatalen, char **r_buf, size_t *r_buflen); +int agent_card_pkdecrypt (const char *keyid, + int (*getpin_cb)(void *, const char *, char*,size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen); #endif /*AGENT_H*/ diff --git a/agent/call-scd.c b/agent/call-scd.c index 6cf53fdfd..dd8853f22 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -403,3 +403,56 @@ agent_card_pksign (const char *keyid, return 0; } +/* Decipher INDATA using the current card. Note that the returned value is */ +int +agent_card_pkdecrypt (const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + struct membuf data; + struct inq_needpin_s inqparm; + size_t len; + + *r_buf = NULL; + rc = start_scd (); + if (rc) + return rc; + + /* FIXME: use secure memory where appropriate */ + if (indatalen*2 + 50 > DIM(line)) + return seterr (General_Error); + + sprintf (line, "SETDATA "); + p = line + strlen (line); + for (i=0; i < indatalen ; i++, p += 2 ) + sprintf (p, "%02X", indata[i]); + rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + init_membuf (&data, 1024); + inqparm.ctx = scd_ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + snprintf (line, DIM(line)-1, "PKDECRYPT %s", keyid); + line[DIM(line)-1] = 0; + rc = assuan_transact (scd_ctx, line, + membuf_data_cb, &data, + inq_needpin, &inqparm, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return map_assuan_err (rc); + } + *r_buf = get_membuf (&data, r_buflen); + if (!*r_buf) + return GNUPG_Out_Of_Core; + + return 0; +} + diff --git a/agent/divert-scd.c b/agent/divert-scd.c index d938d2618..33fd5ce26 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -258,21 +258,65 @@ divert_pksign (const unsigned char *digest, size_t digestlen, int algo, } -int -divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher, - const char *shadow_info) +/* Decrypt the the value given asn an S-expression in CIPHER using the + key identified by SHADOW_INFO and return the plaintext in an + allocated buffer in R_BUF. */ +int +divert_pkdecrypt (const unsigned char *cipher, const char *shadow_info, + char **r_buf, size_t *r_len) { int rc; char *kid; + const unsigned char *s; + size_t n; + const unsigned char *ciphertext; + size_t ciphertextlen; + char *plaintext; + size_t plaintextlen; + + s = cipher; + if (*s != '(') + return GNUPG_Invalid_Sexp; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "enc-val")) + return GNUPG_Unknown_Sexp; + if (*s != '(') + return GNUPG_Unknown_Sexp; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "rsa")) + return GNUPG_Unsupported_Algorithm; + if (*s != '(') + return GNUPG_Unknown_Sexp; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "a")) + return GNUPG_Unknown_Sexp; + n = snext (&s); + if (!n) + return GNUPG_Unknown_Sexp; + ciphertext = s; + ciphertextlen = n; rc = ask_for_card (shadow_info, &kid); if (rc) return rc; - + rc = agent_card_pkdecrypt (kid, getpin_cb, NULL, + ciphertext, ciphertextlen, + &plaintext, &plaintextlen); + if (!rc) + { + *r_buf = plaintext; + *r_len = plaintextlen; + } xfree (kid); - return GNUPG_Not_Implemented; + return rc; } - - - diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 33663a9f7..9a32e76d7 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -71,14 +71,27 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, rc = seterr (No_Secret_Key); goto leave; } + if (!s_skey) { /* divert operation to the smartcard */ - rc = divert_pkdecrypt (&s_plain, s_cipher, shadow_info); + + if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL)) + { + rc = GNUPG_Invalid_Sexp; + goto leave; + } + + rc = divert_pkdecrypt (ciphertext, shadow_info, &buf, &len ); if (rc) { log_error ("smartcard decryption failed: %s\n", gnupg_strerror (rc)); goto leave; } + /* FIXME: don't use buffering and change the protocol to return + a complete S-expression and not just a part. */ + fprintf (outfp, "%u:", (unsigned int)len); + fwrite (buf, 1, len, outfp); + putc (0, outfp); } else { /* no smartcard, but a private key */ @@ -95,23 +108,23 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, rc = map_gcry_err (rc); goto leave; } + + if (DBG_CRYPTO) + { + log_debug ("plain: "); + gcry_sexp_dump (s_plain); + } + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xmalloc (len); + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + /* FIXME: we must make sure that no buffering takes place or we are + in full control of the buffer memory (easy to do) - should go + into assuan. */ + fwrite (buf, 1, len, outfp); } - if (DBG_CRYPTO) - { - log_debug ("plain: "); - gcry_sexp_dump (s_plain); - } - len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); - assert (len); - buf = xmalloc (len); - len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len); - assert (len); - - /* FIXME: we must make sure that no buffering takes place or we are - in full control of the buffer memory (easy to do) - should go - into assuan. */ - fwrite (buf, 1, len, outfp); leave: gcry_sexp_release (s_skey); diff --git a/scd/card.c b/scd/card.c index 281c81013..e7640ffcb 100644 --- a/scd/card.c +++ b/scd/card.c @@ -541,3 +541,103 @@ leave: } +/* Create the signature and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; it + should return the PIN in an allocated buffer and put it into PIN. */ +int +card_decipher (CARD card, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen ) +{ + struct sc_pkcs15_id keyid; + struct sc_pkcs15_prkey_info *key; + struct sc_pkcs15_pin_info *pin; + struct sc_pkcs15_object *keyobj, *pinobj; + char *pinvalue; + int rc; + unsigned char *outbuf = NULL; + size_t outbuflen; + + if (!card || !card->p15card || !indata || !indatalen + || !outdata || !outdatalen || !pincb) + return GNUPG_Invalid_Value; + + rc = idstr_to_id (keyidstr, &keyid); + if (rc) + return rc; + + rc = sc_pkcs15_find_prkey_by_id (card->p15card, &keyid, &keyobj); + if (rc < 0) + { + log_error ("private key not found: %s\n", sc_strerror(rc)); + rc = GNUPG_No_Secret_Key; + goto leave; + } + rc = 0; + key = keyobj->data; + + rc = sc_pkcs15_find_pin_by_auth_id (card->p15card, + &keyobj->auth_id, &pinobj); + if (rc) + { + log_error ("failed to find PIN by auth ID: %s\n", sc_strerror (rc)); + rc = GNUPG_Bad_PIN_Method; + goto leave; + } + pin = pinobj->data; + + /* Fixme: pack this into a verification loop */ + /* Fixme: we might want to pass pin->min_length and + pin->stored_length */ + rc = pincb (pincb_arg, pinobj->label, &pinvalue); + if (rc) + { + log_info ("PIN callback returned error: %s\n", gnupg_strerror (rc)); + goto leave; + } + + rc = sc_pkcs15_verify_pin (card->p15card, pin, + pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_info ("PIN verification failed: %s\n", sc_strerror (rc)); + rc = GNUPG_Bad_PIN; + goto leave; + } + + outbuflen = indatalen < 256? 256 : indatalen; + outbuf = xtrymalloc (outbuflen); + if (!outbuf) + return GNUPG_Out_Of_Core; + + /* OpenSC does not yet support decryption for cryptflex cards */ +/* rc = sc_pkcs15_decipher (card->p15card, key, */ +/* indata, indatalen, */ +/* outbuf, outbuflen); */ + rc = sc_pkcs15_compute_signature (card->p15card, key, + 0, + indata, indatalen, + outbuf, outbuflen ); + if (rc < 0) + { + log_error ("failed to decipger the data: %s\n", sc_strerror (rc)); + rc = GNUPG_Card_Error; + } + else + { + *outdatalen = rc; + *outdata = outbuf; + outbuf = NULL; + rc = 0; + } + + +leave: + xfree (outbuf); + return rc; +} + + diff --git a/scd/command.c b/scd/command.c index a171de3df..30191dd95 100644 --- a/scd/command.c +++ b/scd/command.c @@ -400,6 +400,40 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line) return map_to_assuan_status (rc); } +/* PKDECRYPT <hexified_id> + + */ +static int +cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + void *outdata; + size_t outdatalen; + + if ((rc = open_card (ctrl))) + return rc; + + rc = card_decipher (ctrl->card_ctx, + line, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + if (rc) + { + log_error ("card_create_signature failed: %s\n", gnupg_strerror (rc)); + } + else + { + rc = assuan_send_data (ctx, outdata, outdatalen); + xfree (outdata); + if (rc) + return rc; /* that is already an assuan error code */ + } + + return map_to_assuan_status (rc); +} + @@ -417,6 +451,7 @@ register_commands (ASSUAN_CONTEXT ctx) { "READCERT", 0, cmd_readcert }, { "SETDATA", 0, cmd_setdata }, { "PKSIGN", 0, cmd_pksign }, + { "PKDECRYPT", 0,cmd_pkdecrypt }, { "", ASSUAN_CMD_INPUT, NULL }, { "", ASSUAN_CMD_OUTPUT, NULL }, { NULL } diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 8a5506d32..bd604b41c 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -92,7 +92,11 @@ int card_create_signature (CARD card, void *pincb_arg, const void *indata, size_t indatalen, void **outdata, size_t *outdatalen ); - +int card_decipher (CARD card, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + void **outdata, size_t *outdatalen); #endif /*SCDAEMON_H*/ diff --git a/sm/ChangeLog b/sm/ChangeLog index 846388f81..e47057eb7 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,8 @@ +2002-03-06 Werner Koch <[email protected]> + + * base64.c (base64_reader_cb): Use case insensitive compare of the + Content-Type string to detect plain base-64. + 2002-03-05 Werner Koch <[email protected]> * gpgsm.c, gpgsm.h: Add local_user. diff --git a/sm/base64.c b/sm/base64.c index bf1aea179..e7a1ae9ae 100644 --- a/sm/base64.c +++ b/sm/base64.c @@ -224,8 +224,9 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) parm->linelen = parm->readpos = 0; } else if ( parm->have_lf && parm->line_counter == 1 - && !strncmp (parm->line, "Content-Type:", 13)) - { /* Might be a S/MIME body */ + && parm->linelen >= 13 + && !ascii_memcasecmp (parm->line, "Content-Type:", 13)) + { /* might be a S/MIME body */ parm->might_be_smime = 1; parm->linelen = parm->readpos = 0; goto next; diff --git a/sm/decrypt.c b/sm/decrypt.c index d16bb59e4..382ec0975 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -79,6 +79,14 @@ prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val, goto leave; } + /* FIXME: Actually the leading zero is required but due to the way + we encode the output in libgcrypt as an MPI we are not able to + encode that leading zero. However, when using a Smartcard we are + doing it the rightway and therefore we have skip the zero. This + should be fixed in gpg-agent of course. */ + if (!seskey[n]) + n++; + if (seskey[n] != 2 ) /* wrong block type version */ { rc = seterr (Invalid_Session_Key); |