1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-03 12:11:33 +01:00

Support the Certifciate DO of the v2 OpenPGP cards.

This commit is contained in:
Werner Koch 2008-09-23 09:57:45 +00:00
parent 1377e1fe8d
commit f899b9683b
18 changed files with 746 additions and 174 deletions

View File

@ -75,7 +75,7 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash,
} }
/* Special version of do_encode_md to take care of pckcs#1 padding. /* Special version of do_encode_md to take care of pkcs#1 padding.
For TLS-MD5SHA1 we need to do the padding ourself as Libgrypt does For TLS-MD5SHA1 we need to do the padding ourself as Libgrypt does
not know about this special scheme. Fixme: We should have a not know about this special scheme. Fixme: We should have a
pkcs1-only-padding flag for Libgcrypt. */ pkcs1-only-padding flag for Libgcrypt. */

View File

@ -2709,6 +2709,9 @@ make a clear text signature
@item gpg -sb @code{file} @item gpg -sb @code{file}
make a detached signature make a detached signature
@item gpg -u 0x12345678 -sb @code{file}
make a detached signature with the key 0x12345678
@item gpg --list-keys @code{user_ID} @item gpg --list-keys @code{user_ID}
show keys show keys

View File

@ -1,3 +1,14 @@
2008-09-16 Werner Koch <wk@g10code.com>
* card-util.c (fpr_is_ff): New.
(card_status): Do not print general key info for an all-ff fpr.
(change_login, change_private_do): Factor common code out to ...
(get_data_from_file): .. new.
(change_cert): New.
(card_edit): Add command "writecert".
* call-agent.c (writecert_parm_s): New.
(inq_writecert_parms, agent_scd_writecert): New.
2008-09-04 David Shaw <dshaw@jabberwocky.com> 2008-09-04 David Shaw <dshaw@jabberwocky.com>
* keyserver.c (keyserver_import_cert): Allow keyserver URLs in * keyserver.c (keyserver_import_cert): Allow keyserver URLs in

View File

@ -53,6 +53,13 @@ struct cipher_parm_s
size_t ciphertextlen; size_t ciphertextlen;
}; };
struct writecert_parm_s
{
assuan_context_t ctx;
const unsigned char *certdata;
size_t certdatalen;
};
struct writekey_parm_s struct writekey_parm_s
{ {
assuan_context_t ctx; assuan_context_t ctx;
@ -444,6 +451,56 @@ agent_scd_setattr (const char *name,
} }
/* Handle a CERTDATA inquiry. Note, we only send the data,
assuan_transact takes care of flushing and writing the END
command. */
static int
inq_writecert_parms (void *opaque, const char *line)
{
int rc;
struct writecert_parm_s *parm = opaque;
if (!strncmp (line, "CERTDATA", 8) && (line[8]==' '||!line[8]))
{
rc = assuan_send_data (parm->ctx, parm->certdata, parm->certdatalen);
}
else
rc = default_inq_cb (opaque, line);
return rc;
}
/* Send a WRITECERT command to the SCdaemon. */
int
agent_scd_writecert (const char *certidstr,
const unsigned char *certdata, size_t certdatalen)
{
int rc;
char line[ASSUAN_LINELENGTH];
struct writecert_parm_s parms;
rc = start_agent ();
if (rc)
return rc;
memset (&parms, 0, sizeof parms);
snprintf (line, DIM(line)-1, "SCD WRITECERT %s", certidstr);
line[DIM(line)-1] = 0;
parms.ctx = agent_ctx;
parms.certdata = certdata;
parms.certdatalen = certdatalen;
rc = assuan_transact (agent_ctx, line, NULL, NULL,
inq_writecert_parms, &parms, NULL, NULL);
return rc;
}
/* Handle a KEYDATA inquiry. Note, we only send the data, /* Handle a KEYDATA inquiry. Note, we only send the data,
assuan_transact takes care of flushing and writing the end */ assuan_transact takes care of flushing and writing the end */

View File

@ -76,6 +76,10 @@ int agent_scd_setattr (const char *name,
const unsigned char *value, size_t valuelen, const unsigned char *value, size_t valuelen,
const char *serialno); const char *serialno);
/* Send a WRITECERT command to the SCdaemon. */
int agent_scd_writecert (const char *certidstr,
const unsigned char *certdata, size_t certdatalen);
/* Send a WRITEKEY command to the SCdaemon. */ /* Send a WRITEKEY command to the SCdaemon. */
int agent_scd_writekey (int keyno, const char *serialno, int agent_scd_writekey (int keyno, const char *serialno,
const unsigned char *keydata, size_t keydatalen); const unsigned char *keydata, size_t keydatalen);

View File

@ -288,6 +288,18 @@ fpr_is_zero (const char *fpr)
} }
/* Return true if the SHA1 fingerprint FPR consists only of 0xFF. */
static int
fpr_is_ff (const char *fpr)
{
int i;
for (i=0; i < 20 && fpr[i] == '\xff'; i++)
;
return (i == 20);
}
/* Print all available information about the current card. */ /* Print all available information about the current card. */
void void
card_status (FILE *fp, char *serialno, size_t serialnobuflen) card_status (FILE *fp, char *serialno, size_t serialnobuflen)
@ -467,7 +479,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 :
info.fpr3valid? info.fpr3 : NULL); info.fpr3valid? info.fpr3 : NULL);
if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20)) /* If the fingerprint is all 0xff, the key has no asssociated
OpenPGP certificate. */
if ( thefpr && !fpr_is_ff (thefpr)
&& !get_pubkey_byfprint (pk, thefpr, 20))
{ {
KBNODE keyblock = NULL; KBNODE keyblock = NULL;
@ -655,20 +670,20 @@ fetch_url(void)
} }
/* Read data from file FNAME up to MAXLEN characters. On error return
-1 and store NULl at R_BUFFER; on success return the number of
bytes read and store the address of a newly allocated buffer at
R_BUFFER. */
static int static int
change_login (const char *args) get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
{ {
FILE *fp;
char *data; char *data;
int n; int n;
int rc;
if (args && *args == '<') /* Read it from a file */ *r_buffer = NULL;
{
FILE *fp;
for (args++; spacep (args); args++) fp = fopen (fname, "rb");
;
fp = fopen (args, "rb");
#if GNUPG_MAJOR_VERSION == 1 #if GNUPG_MAJOR_VERSION == 1
if (fp && is_secured_file (fileno (fp))) if (fp && is_secured_file (fileno (fp)))
{ {
@ -679,19 +694,48 @@ change_login (const char *args)
#endif #endif
if (!fp) if (!fp)
{ {
tty_printf (_("can't open `%s': %s\n"), args, strerror (errno)); tty_printf (_("can't open `%s': %s\n"), fname, strerror (errno));
return -1; return -1;
} }
data = xmalloc (254); data = xtrymalloc (maxlen? maxlen:1);
n = fread (data, 1, 254, fp); if (!data)
{
tty_printf (_("error allocating enough memory: %s\n"), strerror (errno));
fclose (fp);
return -1;
}
if (maxlen)
n = fread (data, 1, maxlen, fp);
else
n = 0;
fclose (fp); fclose (fp);
if (n < 0) if (n < 0)
{ {
tty_printf (_("error reading `%s': %s\n"), args, strerror (errno)); tty_printf (_("error reading `%s': %s\n"), fname, strerror (errno));
xfree (data); xfree (data);
return -1; return -1;
} }
*r_buffer = data;
return n;
}
static int
change_login (const char *args)
{
char *data;
int n;
int rc;
if (args && *args == '<') /* Read it from a file */
{
for (args++; spacep (args); args++)
;
n = get_data_from_file (args, 254, &data);
if (n < 0)
return -1;
} }
else else
{ {
@ -732,36 +776,12 @@ change_private_do (const char *args, int nr)
if (args && (args = strchr (args, '<'))) /* Read it from a file */ if (args && (args = strchr (args, '<'))) /* Read it from a file */
{ {
FILE *fp;
/* Fixme: Factor this duplicated code out. */
for (args++; spacep (args); args++) for (args++; spacep (args); args++)
; ;
fp = fopen (args, "rb"); n = get_data_from_file (args, 254, &data);
#if GNUPG_MAJOR_VERSION == 1
if (fp && is_secured_file (fileno (fp)))
{
fclose (fp);
fp = NULL;
errno = EPERM;
}
#endif
if (!fp)
{
tty_printf (_("can't open `%s': %s\n"), args, strerror (errno));
return -1;
}
data = xmalloc (254);
n = fread (data, 1, 254, fp);
fclose (fp);
if (n < 0) if (n < 0)
{
tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
xfree (data);
return -1; return -1;
} }
}
else else
{ {
data = cpr_get ("cardedit.change_private_do", data = cpr_get ("cardedit.change_private_do",
@ -788,6 +808,36 @@ change_private_do (const char *args, int nr)
return rc; return rc;
} }
static int
change_cert (const char *args)
{
char *data;
int n;
int rc;
if (args && *args == '<') /* Read it from a file */
{
for (args++; spacep (args); args++)
;
n = get_data_from_file (args, 16384, &data);
if (n < 0)
return -1;
}
else
{
tty_printf ("usage error: redirectrion to file required\n");
return -1;
}
rc = agent_scd_writecert ("OPENPGP.3", data, n);
if (rc)
log_error ("error writing certificate to card: %s\n", gpg_strerror (rc));
xfree (data);
return rc;
}
static int static int
change_lang (void) change_lang (void)
{ {
@ -1294,7 +1344,7 @@ enum cmdids
cmdNOP = 0, cmdNOP = 0,
cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY, cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR, cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdINVCMD cmdINVCMD
}; };
@ -1325,8 +1375,9 @@ static struct
{ "generate", cmdGENERATE, 1, N_("generate new keys")}, { "generate", cmdGENERATE, 1, N_("generate new keys")},
{ "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")}, { "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
{ "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")}, { "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")},
/* Note, that we do not announce this command yet. */ /* Note, that we do not announce these command yet. */
{ "privatedo", cmdPRIVATEDO, 0, NULL }, { "privatedo", cmdPRIVATEDO, 0, NULL },
{ "writecert", cmdWRITECERT, 1, NULL },
{ NULL, cmdINVCMD, 0, NULL } { NULL, cmdINVCMD, 0, NULL }
}; };
@ -1401,6 +1452,7 @@ card_edit (strlist_t commands)
{ {
int arg_number; int arg_number;
const char *arg_string = ""; const char *arg_string = "";
const char *arg_rest = "";
char *p; char *p;
int i; int i;
int cmd_admin_only; int cmd_admin_only;
@ -1469,6 +1521,11 @@ card_edit (strlist_t commands)
trim_spaces (p); trim_spaces (p);
arg_number = atoi(p); arg_number = atoi(p);
arg_string = p; arg_string = p;
arg_rest = p;
while (digitp (arg_rest))
arg_rest++;
while (spacep (arg_rest))
arg_rest++;
} }
for (i=0; cmds[i].name; i++ ) for (i=0; cmds[i].name; i++ )
@ -1567,6 +1624,13 @@ card_edit (strlist_t commands)
change_private_do (arg_string, arg_number); change_private_do (arg_string, arg_number);
break; break;
case cmdWRITECERT:
if ( arg_number != 3 )
tty_printf ("usage: writecert 3 < FILE\n");
else
change_cert (arg_rest);
break;
case cmdFORCESIG: case cmdFORCESIG:
toggle_forcesig (); toggle_forcesig ();
break; break;

View File

@ -1,3 +1,29 @@
2008-09-23 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_setattr): Use command chaining for long
values.
* iso7816.c (iso7816_put_data): Add arg EXTENDED_MODE. Change all
callers.
* apdu.c (apdu_send_simple): Add arg EXTENDED_MODE. Change all
callers.
(send_le): Implement command chaining.
* ccid-driver.c (ccid_transceive_apdu_level): Increase allowed
APDU size.
* apdu.h: Add new SW_ codes.
2008-09-16 Werner Koch <wk@g10code.com>
* command.c (cmd_writecert): New.
(register_commands): Register it.
* app-common.h (app_ctx_s): Add member WRITECERT.
* app.c (app_writecert): New.
* app-openpgp.c (do_writecert): New.
(parse_historical): New.
(show_extcap): New.
(dump_all_do): Print only the length of longs DOs.
* command.c (cmd_writekey, cmd_apdu, cmd_pksign)
(cmd_passwd): Replace open coding by skip_options.
2008-08-30 Moritz <moritz@gnu.org> 2008-08-30 Moritz <moritz@gnu.org>
* scdaemon.c (main): Use estream_asprintf instead of asprintf. * scdaemon.c (main): Use estream_asprintf instead of asprintf.

View File

@ -1,5 +1,5 @@
/* apdu.c - ISO 7816 APDU functions and low level I/O /* apdu.c - ISO 7816 APDU functions and low level I/O
* Copyright (C) 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -1182,7 +1182,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
err = SW_HOST_INV_VALUE; err = SW_HOST_INV_VALUE;
} }
/* We need to read any rest of the response, to keep the /* We need to read any rest of the response, to keep the
protocol runnng. */ protocol running. */
while (full_len) while (full_len)
{ {
unsigned char dummybuf[128]; unsigned char dummybuf[128];
@ -2587,20 +2587,29 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
if (reader_table[slot].send_apdu_reader) if (reader_table[slot].send_apdu_reader)
return reader_table[slot].send_apdu_reader (slot, return reader_table[slot].send_apdu_reader (slot,
apdu, apdulen, apdu, apdulen,
buffer, buflen, pininfo); buffer, buflen,
pininfo);
else else
return SW_HOST_NOT_SUPPORTED; return SW_HOST_NOT_SUPPORTED;
} }
/* Core APDU trabceiver function. Parameters are described at /* Core APDU tranceiver function. Parameters are described at
apdu_send_le with the exception of PININFO which indicates keypad apdu_send_le with the exception of PININFO which indicates keypad
related operations if not NULL. */ related operations if not NULL. If EXTENDED_MODE is not NULL
command chaining or extended length will be used according to these
values:
n < 0 := Use command chaining without the data part limited to -n
in each chunk. If -1 is used a default value is used.
n == 1 := Use extended length for input and output with out a
length limit.
n > 1 := Use extended length with up to N bytes.
*/
static int static int
send_le (int slot, int class, int ins, int p0, int p1, send_le (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le, int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen, unsigned char **retbuf, size_t *retbuflen,
struct pininfo_s *pininfo) struct pininfo_s *pininfo, int extended_mode)
{ {
#define RESULTLEN 258 #define RESULTLEN 258
unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
@ -2611,16 +2620,37 @@ send_le (int slot, int class, int ins, int p0, int p1,
int sw; int sw;
long rc; /* We need a long here due to PC/SC. */ long rc; /* We need a long here due to PC/SC. */
int did_exact_length_hack = 0; int did_exact_length_hack = 0;
int use_chaining = 0;
int lc_chunk;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER; return SW_HOST_NO_DRIVER;
if (DBG_CARD_IO) if (DBG_CARD_IO)
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n", log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d em=%d\n",
class, ins, p0, p1, lc, le); class, ins, p0, p1, lc, le, extended_mode);
if (lc != -1 && (lc > 255 || lc < 0)) if (lc != -1 && (lc > 255 || lc < 0))
return SW_WRONG_LENGTH; {
/* Data does not fit into an APDU. What we do now dependes on
the EXTENDED_MODE parameter. */
if (!extended_mode)
return SW_WRONG_LENGTH; /* No way. to send such an APDU. */
else if (extended_mode > 0)
return SW_HOST_NOT_SUPPORTED; /* FIXME. */
else if (extended_mode < 0)
{
/* Send APDU using chaining mode. */
if (lc > 16384)
return SW_WRONG_LENGTH; /* Sanity check. */
if ((class&0xf0) != 0)
return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */
use_chaining = extended_mode == -1? 255 : -extended_mode;
use_chaining &= 0xff;
}
else
return SW_HOST_INV_VALUE;
}
if (le != -1 && (le > 256 || le < 0)) if (le != -1 && (le > 256 || le < 0))
return SW_WRONG_LENGTH; return SW_WRONG_LENGTH;
if ((!data && lc != -1) || (data && lc == -1)) if ((!data && lc != -1) || (data && lc == -1))
@ -2629,16 +2659,32 @@ send_le (int slot, int class, int ins, int p0, int p1,
if ((sw = lock_slot (slot))) if ((sw = lock_slot (slot)))
return sw; return sw;
do
{
apdulen = 0; apdulen = 0;
apdu[apdulen++] = class; apdu[apdulen] = class;
if (use_chaining && lc > 255)
{
apdu[apdulen] |= 0x10;
assert (use_chaining < 256);
lc_chunk = use_chaining;
lc -= use_chaining;
}
else
{
use_chaining = 0;
lc_chunk = lc;
}
apdulen++;
apdu[apdulen++] = ins; apdu[apdulen++] = ins;
apdu[apdulen++] = p0; apdu[apdulen++] = p0;
apdu[apdulen++] = p1; apdu[apdulen++] = p1;
if (lc != -1) if (lc_chunk != -1)
{ {
apdu[apdulen++] = lc; apdu[apdulen++] = lc_chunk;
memcpy (apdu+apdulen, data, lc); memcpy (apdu+apdulen, data, lc_chunk);
apdulen += lc; data += lc_chunk;
apdulen += lc_chunk;
/* T=0 does not allow the use of Lc together with Le; thus /* T=0 does not allow the use of Lc together with Le; thus
disable Le in this case. */ disable Le in this case. */
if (reader_table[slot].is_t0) if (reader_table[slot].is_t0)
@ -2646,8 +2692,8 @@ send_le (int slot, int class, int ins, int p0, int p1,
} }
if (le != -1) if (le != -1)
apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */ apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
assert (sizeof (apdu) >= apdulen);
/* As safeguard don't pass any garbage from the stack to the driver. */ /* As safeguard don't pass any garbage from the stack to the driver. */
assert (sizeof (apdu) >= apdulen);
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
exact_length_hack: exact_length_hack:
resultlen = RESULTLEN; resultlen = RESULTLEN;
@ -2666,6 +2712,8 @@ send_le (int slot, int class, int ins, int p0, int p1,
did_exact_length_hack = 1; did_exact_length_hack = 1;
goto exact_length_hack; goto exact_length_hack;
} }
}
while (use_chaining && sw == SW_SUCCESS);
/* Store away the returned data but strip the statusword. */ /* Store away the returned data but strip the statusword. */
resultlen -= 2; resultlen -= 2;
@ -2808,7 +2856,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
return send_le (slot, class, ins, p0, p1, return send_le (slot, class, ins, p0, p1,
lc, data, le, lc, data, le,
retbuf, retbuflen, retbuf, retbuflen,
NULL); NULL, 0);
} }
@ -2826,7 +2874,7 @@ apdu_send (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
{ {
return send_le (slot, class, ins, p0, p1, lc, data, 256, return send_le (slot, class, ins, p0, p1, lc, data, 256,
retbuf, retbuflen, NULL); retbuf, retbuflen, NULL, 0);
} }
/* Send an APDU to the card in SLOT. The APDU is created from all /* Send an APDU to the card in SLOT. The APDU is created from all
@ -2836,10 +2884,12 @@ apdu_send (int slot, int class, int ins, int p0, int p1,
for an invalid SLOT or other non card related error. No data will be for an invalid SLOT or other non card related error. No data will be
returned. */ returned. */
int int
apdu_send_simple (int slot, int class, int ins, int p0, int p1, apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data) int lc, const char *data)
{ {
return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL); return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL,
extended_mode);
} }
@ -2857,13 +2907,13 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
pininfo.maxlen = pinlen_max; pininfo.maxlen = pinlen_max;
pininfo.padlen = pin_padlen; pininfo.padlen = pin_padlen;
return send_le (slot, class, ins, p0, p1, lc, data, -1, return send_le (slot, class, ins, p0, p1, lc, data, -1,
NULL, NULL, &pininfo); NULL, NULL, &pininfo, 0);
} }
/* This is a more generic version of the apdu sending routine. It /* This is a more generic version of the apdu sending routine. It
takes an already formatted APDU in APDUDATA or length APDUDATALEN takes an already formatted APDU in APDUDATA or length APDUDATALEN
and returns the with the APDU including the status word. With and returns the with an APDU including the status word. With
HANDLE_MORE set to true this function will handle the MORE DATA HANDLE_MORE set to true this function will handle the MORE DATA
status and return all APDUs concatenated with one status word at status and return all APDUs concatenated with one status word at
the end. The function does not return a regular status word but 0 the end. The function does not return a regular status word but 0

View File

@ -1,5 +1,5 @@
/* apdu.h - ISO 7816 APDU functions and low level I/O /* apdu.h - ISO 7816 APDU functions and low level I/O
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2008 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -29,8 +29,11 @@ enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
masked of.*/ masked of.*/
SW_EOF_REACHED = 0x6282, SW_EOF_REACHED = 0x6282,
SW_TERM_STATE = 0x6285, /* Selected file is in termination state. */
SW_EEPROM_FAILURE = 0x6581, SW_EEPROM_FAILURE = 0x6581,
SW_WRONG_LENGTH = 0x6700, SW_WRONG_LENGTH = 0x6700,
SW_SM_NOT_SUP = 0x6882, /* Secure Messaging is not supported. */
SW_CC_NOT_SUP = 0x6884, /* Command Chaining is not supported. */
SW_CHV_WRONG = 0x6982, SW_CHV_WRONG = 0x6982,
SW_CHV_BLOCKED = 0x6983, SW_CHV_BLOCKED = 0x6983,
SW_USE_CONDITIONS = 0x6985, SW_USE_CONDITIONS = 0x6985,
@ -97,7 +100,8 @@ int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed); unsigned int *status, unsigned int *changed);
int apdu_check_keypad (int slot, int command, int pin_mode, int apdu_check_keypad (int slot, int command, int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen); int pinlen_min, int pinlen_max, int pin_padlen);
int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data); int lc, const char *data);
int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int lc, const char *data,

View File

@ -1,5 +1,5 @@
/* app-common.h - Common declarations for all card applications /* app-common.h - Common declarations for all card applications
* Copyright (C) 2003, 2005 Free Software Foundation, Inc. * Copyright (C) 2003, 2005, 2008 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -94,8 +94,13 @@ struct app_ctx_s {
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen); unsigned char **outdata, size_t *outdatalen);
gpg_error_t (*writecert) (app_t app, ctrl_t ctrl,
const char *certid,
gpg_error_t (*pincb)(void*,const char *,char **),
void *pincb_arg,
const unsigned char *data, size_t datalen);
gpg_error_t (*writekey) (app_t app, ctrl_t ctrl, gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
const char *certid, unsigned int flags, const char *keyid, unsigned int flags,
gpg_error_t (*pincb)(void*,const char *,char **), gpg_error_t (*pincb)(void*,const char *,char **),
void *pincb_arg, void *pincb_arg,
const unsigned char *pk, size_t pklen); const unsigned char *pk, size_t pklen);
@ -165,6 +170,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr,
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); unsigned char **outdata, size_t *outdatalen );
gpg_error_t app_writecert (app_t app, ctrl_t ctrl,
const char *certidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *keydata, size_t keydatalen);
gpg_error_t app_writekey (app_t app, ctrl_t ctrl, gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
const char *keyidstr, unsigned int flags, const char *keyidstr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),

View File

@ -63,6 +63,7 @@ static struct {
} data_objects[] = { } data_objects[] = {
{ 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" }, { 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" },
{ 0x5F50, 0, 0, 0, 0, 0, 0, "URL" }, { 0x5F50, 0, 0, 0, 0, 0, 0, "URL" },
{ 0x5F52, 0, 0, 1, 0, 0, 0, "Historical Bytes" },
{ 0x0065, 1, 0, 1, 0, 0, 0, "Cardholder Related Data"}, { 0x0065, 1, 0, 1, 0, 0, 0, "Cardholder Related Data"},
{ 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" }, { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
{ 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" }, { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
@ -118,7 +119,16 @@ struct app_local_s {
implicitly available. */ implicitly available. */
} pk[3]; } pk[3];
/* Keep track of card capabilities. */ unsigned char status_indicator; /* The card status indicator. */
/* Keep track of the ISO card capabilities. */
struct
{
unsigned int cmd_chaining:1; /* Command chaining is supported. */
unsigned int ext_lc_le:1; /* Extended Lc and Le are supported. */
} cardcap;
/* Keep track of extended card capabilities. */
struct struct
{ {
unsigned int is_v2:1; /* This is a v2.0 compatible card. */ unsigned int is_v2:1; /* This is a v2.0 compatible card. */
@ -126,7 +136,12 @@ struct app_local_s {
unsigned int key_import:1; unsigned int key_import:1;
unsigned int change_force_chv:1; unsigned int change_force_chv:1;
unsigned int private_dos:1; unsigned int private_dos:1;
unsigned int sm_supported:1; /* Secure Messaging is supported. */
unsigned int sm_aes128:1; /* Use AES-128 for SM. */
unsigned int max_certlen_3:16; unsigned int max_certlen_3:16;
unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */
unsigned int max_cmd_data:16; /* Maximum data size for a command. */
unsigned int max_rsp_data:16; /* Maximum size of a response. */
} extcap; } extcap;
/* Flags used to control the application. */ /* Flags used to control the application. */
@ -451,6 +466,9 @@ dump_all_do (int slot)
if (data_objects[j].binary) if (data_objects[j].binary)
{ {
log_info ("DO `%s': ", data_objects[j].desc); log_info ("DO `%s': ", data_objects[j].desc);
if (valuelen > 200)
log_info ("[%u]\n", (unsigned int)valuelen);
else
log_printhex ("", value, valuelen); log_printhex ("", value, valuelen);
} }
else else
@ -596,7 +614,8 @@ store_fpr (int slot, int keynumber, u32 timestamp,
xfree (buffer); xfree (buffer);
rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6) rc = iso7816_put_data (slot, 0,
(card_version > 0x0007? 0xC7 : 0xC6)
+ keynumber, fpr, 20); + keynumber, fpr, 20);
if (rc) if (rc)
log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc)); log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
@ -610,7 +629,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
buf[2] = timestamp >> 8; buf[2] = timestamp >> 8;
buf[3] = timestamp; buf[3] = timestamp;
rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4); rc = iso7816_put_data (slot, 0, 0xCE + keynumber, buf, 4);
if (rc) if (rc)
log_error (_("failed to store the creation date: %s\n"), log_error (_("failed to store the creation date: %s\n"),
gpg_strerror (rc)); gpg_strerror (rc));
@ -1278,10 +1297,10 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
#endif #endif
} }
/* Read the statdard certificate of an OpenPGP v2 card. It is /* Read the standard certificate of an OpenPGP v2 card. It is
returned in a freshly allocated buffer with that address stored at returned in a freshly allocated buffer with that address stored at
CERT and the length of the certificate stored at CERTLEN. CERTID CERT and the length of the certificate stored at CERTLEN. CERTID
needs to be set to "OpenPGP.3". */ needs to be set to "OPENPGP.3". */
static gpg_error_t static gpg_error_t
do_readcert (app_t app, const char *certid, do_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen) unsigned char **cert, size_t *certlen)
@ -1296,10 +1315,10 @@ do_readcert (app_t app, const char *certid,
*certlen = 0; *certlen = 0;
if (strcmp (certid, "OPENPGP.3")) if (strcmp (certid, "OPENPGP.3"))
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
if (app->app_local->extcap.is_v2) if (!app->app_local->extcap.is_v2)
return gpg_error (GPG_ERR_NOT_FOUND); return gpg_error (GPG_ERR_NOT_FOUND);
relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL); relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL);
if (!relptr) if (!relptr)
return gpg_error (GPG_ERR_NOT_FOUND); return gpg_error (GPG_ERR_NOT_FOUND);
@ -1649,15 +1668,18 @@ do_setattr (app_t app, const char *name,
{ "PRIVATE-DO-3", 0x0103, 2 }, { "PRIVATE-DO-3", 0x0103, 2 },
{ "PRIVATE-DO-4", 0x0104, 3 }, { "PRIVATE-DO-4", 0x0104, 3 },
{ "CERT-3", 0x7F21, 3, 0, 1 }, { "CERT-3", 0x7F21, 3, 0, 1 },
{ "SM-KEY-ENC", 0x00D1, 3, 0, 1 },
{ "SM-KEY-MAC", 0x00D2, 3, 0, 1 },
{ "PW-RESET-CODE",0x00D3, 3, 0, 1 },
{ NULL, 0 } { NULL, 0 }
}; };
int exmode;
for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
; ;
if (!table[idx].name) if (!table[idx].name)
return gpg_error (GPG_ERR_INV_NAME); return gpg_error (GPG_ERR_INV_NAME);
if (table[idx].need_v2) if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */ return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */
switch (table[idx].need_chv) switch (table[idx].need_chv)
@ -1678,7 +1700,12 @@ do_setattr (app_t app, const char *name,
will reread the data from the card and thus get synced in case of will reread the data from the card and thus get synced in case of
errors (e.g. data truncated by the card). */ errors (e.g. data truncated by the card). */
flush_cache_item (app, table[idx].tag); flush_cache_item (app, table[idx].tag);
rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen); /* For command chaining we use a value of 254 for this card. */
if (app->app_local->cardcap.cmd_chaining && valuelen > 254)
exmode = -254;
else
exmode = 0;
rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen);
if (rc) if (rc)
log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc)); log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
@ -1691,6 +1718,34 @@ do_setattr (app_t app, const char *name,
} }
/* Handle the WRITECERT command for OpenPGP. This rites the standard
certifciate to the card; CERTID needs to be set to "OPENPGP.3".
PINCB and PINCB_ARG are the usual arguments for the pinentry
callback. */
static gpg_error_t
do_writecert (app_t app, ctrl_t ctrl,
const char *certidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *certdata, size_t certdatalen)
{
#if GNUPG_MAJOR_VERSION > 1
if (strcmp (certidstr, "OPENPGP.3"))
return gpg_error (GPG_ERR_INV_ID);
if (!certdata || !certdatalen)
return gpg_error (GPG_ERR_INV_ARG);
if (!app->app_local->extcap.is_v2)
return gpg_error (GPG_ERR_NOT_SUPPORTED);
if (certdatalen > app->app_local->extcap.max_certlen_3)
return gpg_error (GPG_ERR_TOO_LARGE);
return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen);
#else
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
#endif
}
/* Handle the PASSWD command. */ /* Handle the PASSWD command. */
static gpg_error_t static gpg_error_t
do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
@ -2074,7 +2129,7 @@ do_writekey (app_t app, ctrl_t ctrl,
goto leave; goto leave;
/* Store the key. */ /* Store the key. */
err = iso7816_put_data (app->slot, err = iso7816_put_data (app->slot, 0,
(app->card_version > 0x0007? 0xE0 : 0xE9) + keyno, (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
template, template_len); template, template_len);
if (err) if (err)
@ -2711,6 +2766,80 @@ do_check_pin (app_t app, const char *keyidstr,
} }
/* Show information about card capabilities. */
static void
show_caps (struct app_local_s *s)
{
log_info ("Version-2 ......: %s\n", s->extcap.is_v2? "yes":"no");
log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no");
if (s->extcap.get_challenge)
log_printf (" (%u bytes max)", s->extcap.max_get_challenge);
log_info ("Key-Import .....: %s\n", s->extcap.key_import? "yes":"no");
log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no");
log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no");
log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no");
if (s->extcap.sm_supported)
log_printf (" (%s)", s->extcap.sm_aes128? "AES-128":"3DES");
log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3);
log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data);
log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data);
log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no");
log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no");
log_info ("Status Indicator: %02X\n", s->status_indicator);
log_info ("GnuPG-No-Sync ..: %s\n", s->flags.no_sync? "yes":"no");
log_info ("GnuPG-Def-PW2 ..: %s\n", s->flags.def_chv2? "yes":"no");
}
/* Parse the historical bytes in BUFFER of BUFLEN and store them in
APPLOC. */
static void
parse_historical (struct app_local_s *apploc,
const unsigned char * buffer, size_t buflen)
{
/* Example buffer: 00 31 C5 73 C0 01 80 00 90 00 */
if (buflen < 4)
{
log_error ("warning: historical bytes are too short\n");
return; /* Too short. */
}
if (*buffer)
{
log_error ("warning: bad category indicator in historical bytes\n");
return;
}
/* Skip category indicator. */
buffer++;
buflen--;
/* Get the status indicator. */
apploc->status_indicator = buffer[buflen-3];
buflen -= 3;
/* Parse the compact TLV. */
while (buflen)
{
unsigned int tag = (*buffer & 0xf0) >> 4;
unsigned int len = (*buffer & 0x0f);
if (len+1 > buflen)
{
log_error ("warning: bad Compact-TLV in historical bytes\n");
return; /* Error. */
}
buffer++;
buflen--;
if (tag == 7 && len == 3)
{
/* Card capabilities. */
apploc->cardcap.cmd_chaining = !!(buffer[2] & 0x80);
apploc->cardcap.ext_lc_le = !!(buffer[2] & 0x40);
}
buffer += len;
buflen -= len;
}
}
/* Select the OpenPGP application on the card in SLOT. This function /* Select the OpenPGP application on the card in SLOT. This function
@ -2771,6 +2900,22 @@ app_select_openpgp (app_t app)
if (app->card_version >= 0x0200) if (app->card_version >= 0x0200)
app->app_local->extcap.is_v2 = 1; app->app_local->extcap.is_v2 = 1;
/* Read the historical bytes. */
relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL);
if (relptr)
{
if (opt.verbose)
{
log_info ("Historical Bytes: ");
log_printhex ("", buffer, buflen);
}
parse_historical (app->app_local, buffer, buflen);
xfree (relptr);
}
/* Read the force-chv1 flag. */
relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL); relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
if (!relptr) if (!relptr)
{ {
@ -2781,6 +2926,7 @@ app_select_openpgp (app_t app)
app->force_chv1 = (buflen && *buffer == 0); app->force_chv1 = (buflen && *buffer == 0);
xfree (relptr); xfree (relptr);
/* Read the extended capabilities. */
relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL); relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
if (!relptr) if (!relptr)
{ {
@ -2790,6 +2936,7 @@ app_select_openpgp (app_t app)
} }
if (buflen) if (buflen)
{ {
app->app_local->extcap.sm_supported = !!(*buffer & 0x80);
app->app_local->extcap.get_challenge = !!(*buffer & 0x40); app->app_local->extcap.get_challenge = !!(*buffer & 0x40);
app->app_local->extcap.key_import = !!(*buffer & 0x20); app->app_local->extcap.key_import = !!(*buffer & 0x20);
app->app_local->extcap.change_force_chv = !!(*buffer & 0x10); app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
@ -2798,7 +2945,12 @@ app_select_openpgp (app_t app)
if (buflen >= 10) if (buflen >= 10)
{ {
/* Available with v2 cards. */ /* Available with v2 cards. */
app->app_local->extcap.sm_aes128 = (buffer[1] == 1);
app->app_local->extcap.max_get_challenge
= (buffer[2] << 8 | buffer[3]);
app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]); app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
app->app_local->extcap.max_cmd_data = (buffer[6] << 8 | buffer[7]);
app->app_local->extcap.max_rsp_data = (buffer[8] << 8 | buffer[9]);
} }
xfree (relptr); xfree (relptr);
@ -2809,6 +2961,9 @@ app_select_openpgp (app_t app)
parse_login_data (app); parse_login_data (app);
if (opt.verbose)
show_caps (app->app_local);
if (opt.verbose > 1) if (opt.verbose > 1)
dump_all_do (slot); dump_all_do (slot);
@ -2818,6 +2973,7 @@ app_select_openpgp (app_t app)
app->fnc.readkey = do_readkey; app->fnc.readkey = do_readkey;
app->fnc.getattr = do_getattr; app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr; app->fnc.setattr = do_setattr;
app->fnc.writecert = do_writecert;
app->fnc.writekey = do_writekey; app->fnc.writekey = do_writekey;
app->fnc.genkey = do_genkey; app->fnc.genkey = do_genkey;
app->fnc.sign = do_sign; app->fnc.sign = do_sign;

View File

@ -240,8 +240,8 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
/* If we don't have an app, check whether we have a saved /* If we don't have an app, check whether we have a saved
application for that slot. This is useful so that a card does application for that slot. This is useful so that a card does
not get reset even if only one session is using the card - so the not get reset even if only one session is using the card - this
PIN cache and other cached data are preserved. */ way the PIN cache and other cached data are preserved. */
if (!app && lock_table[slot].initialized && lock_table[slot].last_app) if (!app && lock_table[slot].initialized && lock_table[slot].last_app)
{ {
app = lock_table[slot].last_app; app = lock_table[slot].last_app;
@ -734,6 +734,34 @@ app_decipher (app_t app, const char *keyidstr,
} }
/* Perform the WRITECERT operation. */
gpg_error_t
app_writecert (app_t app, ctrl_t ctrl,
const char *certidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *data, size_t datalen)
{
gpg_error_t err;
if (!app || !certidstr || !*certidstr || !pincb)
return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.writecert)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
err = lock_reader (app->slot);
if (err)
return err;
err = app->fnc.writecert (app, ctrl, certidstr,
pincb, pincb_arg, data, datalen);
unlock_reader (app->slot);
if (opt.verbose)
log_info ("operation writecert result: %s\n", gpg_strerror (err));
return err;
}
/* Perform the WRITEKEY operation. */ /* Perform the WRITEKEY operation. */
gpg_error_t gpg_error_t
app_writekey (app_t app, ctrl_t ctrl, app_writekey (app_t app, ctrl_t ctrl,
@ -759,7 +787,6 @@ app_writekey (app_t app, ctrl_t ctrl,
if (opt.verbose) if (opt.verbose)
log_info ("operation writekey result: %s\n", gpg_strerror (err)); log_info ("operation writekey result: %s\n", gpg_strerror (err));
return err; return err;
} }

111
scd/atr.c
View File

@ -277,10 +277,121 @@ atr_dump (int slot, FILE *fp)
} }
/* Note: This code has not yet been tested! It shall return -1 on
error or the nu,ber of hiostroical bytes and store them at
HISTORICAL. */
int
atr_get_historical (int slot, unsigned char historical[])
{
int result = -1;
unsigned char *atrbuffer = NULL;
unsigned char *atr;
size_t atrlen;
int have_ta, have_tb, have_tc, have_td;
int n_historical;
int idx;
unsigned char chksum;
atr = atrbuffer = apdu_get_atr (slot, &atrlen);
if (!atr || atrlen < 2)
goto leave;
atrlen--;
atr++;
chksum = *atr;
for (idx=1; idx < atrlen-1; idx++)
chksum ^= atr[idx];
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
n_historical = (*atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
if (have_ta + have_tb + have_tc >= atrlen)
goto leave;
atrlen -= have_ta + have_tb + have_tc;
atr += have_ta + have_tb + have_tc;
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
}
else
have_ta = have_tb = have_tc = have_td = 0;
if (have_ta + have_tb + have_tc >= atrlen)
goto leave;
atrlen -= have_ta + have_tb + have_tc;
atr += have_ta + have_tb + have_tc;
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
}
else
have_ta = have_tb = have_tc = have_td = 0;
for (idx = 3; have_ta || have_tb || have_tc || have_td; idx++)
{
if (have_ta + have_tb + have_tc >= atrlen)
goto leave;
atrlen -= have_ta + have_tb + have_tc;
atr += have_ta + have_tb + have_tc;
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
}
else
have_ta = have_tb = have_tc = have_td = 0;
}
if (n_historical >= atrlen)
goto leave; /* ATR shorter than required for historical bytes. */
if (n_historical)
{
for (idx=0; n_historical && atrlen; n_historical--, atrlen--, atr++)
historical[idx] = *atr;
}
if (!atrlen || *atr != chksum)
goto leave;
/* Don't care about garbage at the end of the ATR. */
result = n_historical;
leave:
xfree (atrbuffer);
return result;
}

View File

@ -1957,7 +1957,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
size_t *nresp) size_t *nresp)
{ {
int rc; int rc;
unsigned char send_buffer[10+259], recv_buffer[10+259]; unsigned char send_buffer[10+261], recv_buffer[10+261];
const unsigned char *apdu; const unsigned char *apdu;
size_t apdulen; size_t apdulen;
unsigned char *msg; unsigned char *msg;
@ -1971,7 +1971,9 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
apdulen = apdu_buflen; apdulen = apdu_buflen;
assert (apdulen); assert (apdulen);
if (apdulen > 254) /* The maximum length for a short APDU T=1 block is 261, for an
extra APDU T=1 block is 65544. */
if (apdulen > 261)
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
msg[0] = PC_to_RDR_XfrBlock; msg[0] = PC_to_RDR_XfrBlock;
@ -2117,6 +2119,7 @@ ccid_transceive (ccid_driver_t handle,
assert (apdulen); assert (apdulen);
/* Construct an I-Block. */ /* Construct an I-Block. */
#warning fixme: APDULEN may be larger
if (apdulen > 254) if (apdulen > 254)
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */

View File

@ -47,6 +47,9 @@
/* Maximum allowed size of key data as used in inquiries. */ /* Maximum allowed size of key data as used in inquiries. */
#define MAXLEN_KEYDATA 4096 #define MAXLEN_KEYDATA 4096
/* Maximum allowed size of certificate data as used in inquiries. */
#define MAXLEN_CERTDATA 16384
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
@ -851,14 +854,8 @@ cmd_pksign (assuan_context_t ctx, char *line)
hash_algo = GCRY_MD_SHA1; hash_algo = GCRY_MD_SHA1;
else else
return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm"); return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
/* Skip over options. */
while ( *line == '-' && line[1] == '-' ) line = skip_options (line);
{
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
}
if ( IS_LOCKED (ctrl) ) if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED); return gpg_error (GPG_ERR_LOCKED);
@ -1036,8 +1033,8 @@ cmd_getattr (assuan_context_t ctx, char *line)
names and values are depend on the currently selected smartcard names and values are depend on the currently selected smartcard
application. NAME and VALUE must be percent and '+' escaped. application. NAME and VALUE must be percent and '+' escaped.
However, the curent implementation assumes that Name is not escaped; However, the current implementation assumes that NAME is not
this works as long as noone uses arbitrary escaping. escaped; this works as long as noone uses arbitrary escaping.
A PIN will be requested for most NAMEs. See the corresponding A PIN will be requested for most NAMEs. See the corresponding
setattr function of the actually used application (app-*.c) for setattr function of the actually used application (app-*.c) for
@ -1083,12 +1080,74 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
/* WRITECERT <hexified_certid>
This command is used to store a certifciate on a smartcard. The
allowed certids depend on the currently selected smartcard
application. The actual certifciate is requested using the inquiry
"CERTDATA" and needs to be provided in its raw (e.g. DER) form.
In almost all cases a a PIN will be requested. See the related
writecert function of the actually used application (app-*.c) for
details. */
static int
cmd_writecert (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
char *certid;
unsigned char *certdata;
size_t certdatalen;
if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED);
line = skip_options (line);
if (!*line)
return set_error (GPG_ERR_ASS_PARAMETER, "no certid given");
certid = line;
while (*line && !spacep (line))
line++;
*line = 0;
if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
certid = xtrystrdup (certid);
if (!certid)
return out_of_core ();
/* Now get the actual keydata. */
rc = assuan_inquire (ctx, "CERTDATA",
&certdata, &certdatalen, MAXLEN_CERTDATA);
if (rc)
{
xfree (certid);
return rc;
}
/* Write the certificate to the card. */
rc = app_writecert (ctrl->app_ctx, ctrl, certid,
pin_cb, ctx, certdata, certdatalen);
xfree (certid);
xfree (certdata);
TEST_CARD_REMOVAL (ctrl, rc);
return rc;
}
/* WRITEKEY [--force] <keyid> /* WRITEKEY [--force] <keyid>
This command is used to store a secret key on a a smartcard. The This command is used to store a secret key on a a smartcard. The
allowed keyids depend on the currently selected smartcard allowed keyids depend on the currently selected smartcard
application. The actual keydata is requested using the inquiry application. The actual keydata is requested using the inquiry
"KETDATA" and need to be provided without any protection. With "KEYDATA" and need to be provided without any protection. With
--force set an existing key under this KEYID will get overwritten. --force set an existing key under this KEYID will get overwritten.
The keydata is expected to be the usual canonical encoded The keydata is expected to be the usual canonical encoded
S-expression. S-expression.
@ -1109,14 +1168,8 @@ cmd_writekey (assuan_context_t ctx, char *line)
if ( IS_LOCKED (ctrl) ) if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED); return gpg_error (GPG_ERR_LOCKED);
/* Skip over options. */ line = skip_options (line);
while ( *line == '-' && line[1] == '-' )
{
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
}
if (!*line) if (!*line)
return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given"); return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given");
keyid = line; keyid = line;
@ -1294,14 +1347,8 @@ cmd_passwd (assuan_context_t ctx, char *line)
if ( IS_LOCKED (ctrl) ) if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED); return gpg_error (GPG_ERR_LOCKED);
/* Skip over options. */ line = skip_options (line);
while (*line == '-' && line[1] == '-')
{
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
}
if (!*line) if (!*line)
return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given"); return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given");
chvnostr = line; chvnostr = line;
@ -1601,14 +1648,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
with_atr = has_option (line, "--atr"); with_atr = has_option (line, "--atr");
handle_more = has_option (line, "--more"); handle_more = has_option (line, "--more");
/* Skip over options. */ line = skip_options (line);
while ( *line == '-' && line[1] == '-' )
{
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
}
if ( IS_LOCKED (ctrl) ) if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED); return gpg_error (GPG_ERR_LOCKED);
@ -1687,6 +1727,7 @@ register_commands (assuan_context_t ctx)
{ "OUTPUT", NULL }, { "OUTPUT", NULL },
{ "GETATTR", cmd_getattr }, { "GETATTR", cmd_getattr },
{ "SETATTR", cmd_setattr }, { "SETATTR", cmd_setattr },
{ "WRITECERT", cmd_writecert },
{ "WRITEKEY", cmd_writekey }, { "WRITEKEY", cmd_writekey },
{ "GENKEY", cmd_genkey }, { "GENKEY", cmd_genkey },
{ "RANDOM", cmd_random }, { "RANDOM", cmd_random },

View File

@ -66,7 +66,10 @@ map_sw (int sw)
switch (sw) switch (sw)
{ {
case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break; case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
case SW_TERM_STATE: ec = GPG_ERR_CARD; break;
case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break; case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break;
case SW_SM_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_CC_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break; case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break;
case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break; case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break;
case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break; case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
@ -130,7 +133,7 @@ iso7816_select_application (int slot, const char *aid, size_t aidlen,
unsigned int flags) unsigned int flags)
{ {
int sw; int sw;
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4,
(flags&1)? 0 :0x0c, aidlen, aid); (flags&1)? 0 :0x0c, aidlen, aid);
return map_sw (sw); return map_sw (sw);
} }
@ -156,7 +159,7 @@ iso7816_select_file (int slot, int tag, int is_dir,
{ {
p0 = (tag == 0x3F00)? 0: is_dir? 1:2; p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
p1 = 0x0c; /* No FC return. */ p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
p0, p1, 2, (char*)tagbuf ); p0, p1, 2, (char*)tagbuf );
return map_sw (sw); return map_sw (sw);
} }
@ -192,7 +195,7 @@ iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
p0 = 0x08; p0 = 0x08;
p1 = 0x0c; /* No FC return. */ p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
p0, p1, buflen, (char*)buffer ); p0, p1, buflen, (char*)buffer );
return map_sw (sw); return map_sw (sw);
} }
@ -253,7 +256,7 @@ iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
pininfo->maxlen, pininfo->maxlen,
pininfo->padlen); pininfo->padlen);
else else
sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
return map_sw (sw); return map_sw (sw);
} }
@ -300,7 +303,7 @@ iso7816_change_reference_data_kp (int slot, int chvno,
pininfo->maxlen, pininfo->maxlen,
pininfo->padlen); pininfo->padlen);
else else
sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
xfree (buf); xfree (buf);
return map_sw (sw); return map_sw (sw);
@ -340,7 +343,7 @@ iso7816_reset_retry_counter_kp (int slot, int chvno,
pininfo->maxlen, pininfo->maxlen,
pininfo->padlen); pininfo->padlen);
else else
sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
2, chvno, newchvlen, newchv); 2, chvno, newchvlen, newchv);
return map_sw (sw); return map_sw (sw);
} }
@ -386,14 +389,16 @@ iso7816_get_data (int slot, int tag,
/* Perform a PUT DATA command on card in SLOT. Write DATA of length /* Perform a PUT DATA command on card in SLOT. Write DATA of length
DATALEN to TAG. */ DATALEN to TAG. EXTENDED_MODE controls whether extended length
headers or command chaining is used instead of single length
bytes. */
gpg_error_t gpg_error_t
iso7816_put_data (int slot, int tag, iso7816_put_data (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen) const unsigned char *data, size_t datalen)
{ {
int sw; int sw;
sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA, sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA,
((tag >> 8) & 0xff), (tag & 0xff), ((tag >> 8) & 0xff), (tag & 0xff),
datalen, (const char*)data); datalen, (const char*)data);
return map_sw (sw); return map_sw (sw);
@ -412,7 +417,7 @@ iso7816_manage_security_env (int slot, int p1, int p2,
if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 ) if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 )
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, sw = apdu_send_simple (slot, 0, 0x00, CMD_MSE, p1, p2,
data? datalen : -1, (const char*)data); data? datalen : -1, (const char*)data);
return map_sw (sw); return map_sw (sw);
} }

View File

@ -79,7 +79,7 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
iso7816_pininfo_t *pininfo); iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_get_data (int slot, int tag, gpg_error_t iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_put_data (int slot, int tag, gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen); const unsigned char *data, size_t datalen);
gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2, gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
const unsigned char *data, const unsigned char *data,

View File

@ -982,7 +982,7 @@ gpgsm_format_keydesc (ksba_cert_t cert)
/* We also escape the quote character to work around a bug in /* We also escape the quote character to work around a bug in
the mingw32 runtime which does not correcty handle command the mingw32 runtime which does not correcty handle command
line quoting. We correctly double the quote mark when line quoting. We correctly double the quote mark when
calling a program (i.e. gpg-protec-tool), but the pre-main calling a program (i.e. gpg-protect-tool), but the pre-main
code does not notice the double quote as an escaped code does not notice the double quote as an escaped
quote. */ quote. */
if (*s < ' ' || *s == '+' || *s == '\"') if (*s < ' ' || *s == '+' || *s == '\"')