mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Support the Certifciate DO of the v2 OpenPGP cards.
This commit is contained in:
parent
1377e1fe8d
commit
f899b9683b
@ -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
|
||||
not know about this special scheme. Fixme: We should have a
|
||||
pkcs1-only-padding flag for Libgcrypt. */
|
||||
|
@ -2709,6 +2709,9 @@ make a clear text signature
|
||||
@item gpg -sb @code{file}
|
||||
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}
|
||||
show keys
|
||||
|
||||
|
@ -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>
|
||||
|
||||
* keyserver.c (keyserver_import_cert): Allow keyserver URLs in
|
||||
|
@ -53,6 +53,13 @@ struct cipher_parm_s
|
||||
size_t ciphertextlen;
|
||||
};
|
||||
|
||||
struct writecert_parm_s
|
||||
{
|
||||
assuan_context_t ctx;
|
||||
const unsigned char *certdata;
|
||||
size_t certdatalen;
|
||||
};
|
||||
|
||||
struct writekey_parm_s
|
||||
{
|
||||
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,
|
||||
assuan_transact takes care of flushing and writing the end */
|
||||
|
@ -76,6 +76,10 @@ int agent_scd_setattr (const char *name,
|
||||
const unsigned char *value, size_t valuelen,
|
||||
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. */
|
||||
int agent_scd_writekey (int keyno, const char *serialno,
|
||||
const unsigned char *keydata, size_t keydatalen);
|
||||
|
148
g10/card-util.c
148
g10/card-util.c
@ -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. */
|
||||
void
|
||||
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 :
|
||||
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;
|
||||
|
||||
@ -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
|
||||
change_login (const char *args)
|
||||
{
|
||||
char *data;
|
||||
int n;
|
||||
int rc;
|
||||
|
||||
if (args && *args == '<') /* Read it from a file */
|
||||
get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
|
||||
{
|
||||
FILE *fp;
|
||||
char *data;
|
||||
int n;
|
||||
|
||||
for (args++; spacep (args); args++)
|
||||
;
|
||||
fp = fopen (args, "rb");
|
||||
*r_buffer = NULL;
|
||||
|
||||
fp = fopen (fname, "rb");
|
||||
#if GNUPG_MAJOR_VERSION == 1
|
||||
if (fp && is_secured_file (fileno (fp)))
|
||||
{
|
||||
@ -679,19 +694,48 @@ change_login (const char *args)
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
data = xmalloc (254);
|
||||
n = fread (data, 1, 254, fp);
|
||||
data = xtrymalloc (maxlen? maxlen:1);
|
||||
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);
|
||||
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);
|
||||
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
|
||||
{
|
||||
@ -732,36 +776,12 @@ change_private_do (const char *args, int nr)
|
||||
|
||||
if (args && (args = strchr (args, '<'))) /* Read it from a file */
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
/* Fixme: Factor this duplicated code out. */
|
||||
for (args++; spacep (args); args++)
|
||||
;
|
||||
fp = fopen (args, "rb");
|
||||
#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);
|
||||
n = get_data_from_file (args, 254, &data);
|
||||
if (n < 0)
|
||||
{
|
||||
tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
|
||||
xfree (data);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data = cpr_get ("cardedit.change_private_do",
|
||||
@ -788,6 +808,36 @@ change_private_do (const char *args, int nr)
|
||||
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
|
||||
change_lang (void)
|
||||
{
|
||||
@ -1294,7 +1344,7 @@ enum cmdids
|
||||
cmdNOP = 0,
|
||||
cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
|
||||
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
|
||||
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO,
|
||||
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
|
||||
cmdINVCMD
|
||||
};
|
||||
|
||||
@ -1325,8 +1375,9 @@ static struct
|
||||
{ "generate", cmdGENERATE, 1, N_("generate new keys")},
|
||||
{ "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
|
||||
{ "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 },
|
||||
{ "writecert", cmdWRITECERT, 1, NULL },
|
||||
{ NULL, cmdINVCMD, 0, NULL }
|
||||
};
|
||||
|
||||
@ -1401,6 +1452,7 @@ card_edit (strlist_t commands)
|
||||
{
|
||||
int arg_number;
|
||||
const char *arg_string = "";
|
||||
const char *arg_rest = "";
|
||||
char *p;
|
||||
int i;
|
||||
int cmd_admin_only;
|
||||
@ -1469,6 +1521,11 @@ card_edit (strlist_t commands)
|
||||
trim_spaces (p);
|
||||
arg_number = atoi(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++ )
|
||||
@ -1567,6 +1624,13 @@ card_edit (strlist_t commands)
|
||||
change_private_do (arg_string, arg_number);
|
||||
break;
|
||||
|
||||
case cmdWRITECERT:
|
||||
if ( arg_number != 3 )
|
||||
tty_printf ("usage: writecert 3 < FILE\n");
|
||||
else
|
||||
change_cert (arg_rest);
|
||||
break;
|
||||
|
||||
case cmdFORCESIG:
|
||||
toggle_forcesig ();
|
||||
break;
|
||||
|
@ -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>
|
||||
|
||||
* scdaemon.c (main): Use estream_asprintf instead of asprintf.
|
||||
|
92
scd/apdu.c
92
scd/apdu.c
@ -1,5 +1,5 @@
|
||||
/* 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.
|
||||
*
|
||||
@ -1182,7 +1182,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
||||
err = SW_HOST_INV_VALUE;
|
||||
}
|
||||
/* We need to read any rest of the response, to keep the
|
||||
protocol runnng. */
|
||||
protocol running. */
|
||||
while (full_len)
|
||||
{
|
||||
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)
|
||||
return reader_table[slot].send_apdu_reader (slot,
|
||||
apdu, apdulen,
|
||||
buffer, buflen, pininfo);
|
||||
buffer, buflen,
|
||||
pininfo);
|
||||
else
|
||||
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
|
||||
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
|
||||
send_le (int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data, int le,
|
||||
unsigned char **retbuf, size_t *retbuflen,
|
||||
struct pininfo_s *pininfo)
|
||||
struct pininfo_s *pininfo, int extended_mode)
|
||||
{
|
||||
#define RESULTLEN 258
|
||||
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;
|
||||
long rc; /* We need a long here due to PC/SC. */
|
||||
int did_exact_length_hack = 0;
|
||||
int use_chaining = 0;
|
||||
int lc_chunk;
|
||||
|
||||
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
|
||||
return SW_HOST_NO_DRIVER;
|
||||
|
||||
if (DBG_CARD_IO)
|
||||
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
|
||||
class, ins, p0, p1, lc, le);
|
||||
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, extended_mode);
|
||||
|
||||
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))
|
||||
return SW_WRONG_LENGTH;
|
||||
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)))
|
||||
return sw;
|
||||
|
||||
do
|
||||
{
|
||||
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++] = p0;
|
||||
apdu[apdulen++] = p1;
|
||||
if (lc != -1)
|
||||
if (lc_chunk != -1)
|
||||
{
|
||||
apdu[apdulen++] = lc;
|
||||
memcpy (apdu+apdulen, data, lc);
|
||||
apdulen += lc;
|
||||
apdu[apdulen++] = lc_chunk;
|
||||
memcpy (apdu+apdulen, data, lc_chunk);
|
||||
data += lc_chunk;
|
||||
apdulen += lc_chunk;
|
||||
/* T=0 does not allow the use of Lc together with Le; thus
|
||||
disable Le in this case. */
|
||||
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)
|
||||
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. */
|
||||
assert (sizeof (apdu) >= apdulen);
|
||||
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
|
||||
exact_length_hack:
|
||||
resultlen = RESULTLEN;
|
||||
@ -2666,6 +2712,8 @@ send_le (int slot, int class, int ins, int p0, int p1,
|
||||
did_exact_length_hack = 1;
|
||||
goto exact_length_hack;
|
||||
}
|
||||
}
|
||||
while (use_chaining && sw == SW_SUCCESS);
|
||||
|
||||
/* Store away the returned data but strip the statusword. */
|
||||
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,
|
||||
lc, data, le,
|
||||
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)
|
||||
{
|
||||
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
|
||||
@ -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
|
||||
returned. */
|
||||
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)
|
||||
{
|
||||
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.padlen = pin_padlen;
|
||||
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
|
||||
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
|
||||
status and return all APDUs concatenated with one status word at
|
||||
the end. The function does not return a regular status word but 0
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 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.
|
||||
*
|
||||
@ -29,8 +29,11 @@ enum {
|
||||
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
|
||||
masked of.*/
|
||||
SW_EOF_REACHED = 0x6282,
|
||||
SW_TERM_STATE = 0x6285, /* Selected file is in termination state. */
|
||||
SW_EEPROM_FAILURE = 0x6581,
|
||||
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_BLOCKED = 0x6983,
|
||||
SW_USE_CONDITIONS = 0x6985,
|
||||
@ -97,7 +100,8 @@ int apdu_get_status (int slot, int hang,
|
||||
unsigned int *status, unsigned int *changed);
|
||||
int apdu_check_keypad (int slot, int command, int pin_mode,
|
||||
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 apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 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.
|
||||
*
|
||||
@ -94,8 +94,13 @@ struct app_ctx_s {
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
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,
|
||||
const char *certid, unsigned int flags,
|
||||
const char *keyid, unsigned int flags,
|
||||
gpg_error_t (*pincb)(void*,const char *,char **),
|
||||
void *pincb_arg,
|
||||
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,
|
||||
const void *indata, size_t indatalen,
|
||||
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,
|
||||
const char *keyidstr, unsigned int flags,
|
||||
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||
|
@ -63,6 +63,7 @@ static struct {
|
||||
} data_objects[] = {
|
||||
{ 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" },
|
||||
{ 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"},
|
||||
{ 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
|
||||
{ 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
|
||||
@ -118,7 +119,16 @@ struct app_local_s {
|
||||
implicitly available. */
|
||||
} 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
|
||||
{
|
||||
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 change_force_chv: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_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;
|
||||
|
||||
/* Flags used to control the application. */
|
||||
@ -451,6 +466,9 @@ dump_all_do (int slot)
|
||||
if (data_objects[j].binary)
|
||||
{
|
||||
log_info ("DO `%s': ", data_objects[j].desc);
|
||||
if (valuelen > 200)
|
||||
log_info ("[%u]\n", (unsigned int)valuelen);
|
||||
else
|
||||
log_printhex ("", value, valuelen);
|
||||
}
|
||||
else
|
||||
@ -596,7 +614,8 @@ store_fpr (int slot, int keynumber, u32 timestamp,
|
||||
|
||||
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);
|
||||
if (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[3] = timestamp;
|
||||
|
||||
rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4);
|
||||
rc = iso7816_put_data (slot, 0, 0xCE + keynumber, buf, 4);
|
||||
if (rc)
|
||||
log_error (_("failed to store the creation date: %s\n"),
|
||||
gpg_strerror (rc));
|
||||
@ -1278,10 +1297,10 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
|
||||
#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
|
||||
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
|
||||
do_readcert (app_t app, const char *certid,
|
||||
unsigned char **cert, size_t *certlen)
|
||||
@ -1296,10 +1315,10 @@ do_readcert (app_t app, const char *certid,
|
||||
*certlen = 0;
|
||||
if (strcmp (certid, "OPENPGP.3"))
|
||||
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);
|
||||
|
||||
relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
|
||||
relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL);
|
||||
if (!relptr)
|
||||
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-4", 0x0104, 3 },
|
||||
{ "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 }
|
||||
};
|
||||
|
||||
int exmode;
|
||||
|
||||
for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
|
||||
;
|
||||
if (!table[idx].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. */
|
||||
|
||||
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
|
||||
errors (e.g. data truncated by the card). */
|
||||
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)
|
||||
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. */
|
||||
static gpg_error_t
|
||||
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;
|
||||
|
||||
/* Store the key. */
|
||||
err = iso7816_put_data (app->slot,
|
||||
err = iso7816_put_data (app->slot, 0,
|
||||
(app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
|
||||
template, template_len);
|
||||
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
|
||||
@ -2771,6 +2900,22 @@ app_select_openpgp (app_t app)
|
||||
if (app->card_version >= 0x0200)
|
||||
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);
|
||||
if (!relptr)
|
||||
{
|
||||
@ -2781,6 +2926,7 @@ app_select_openpgp (app_t app)
|
||||
app->force_chv1 = (buflen && *buffer == 0);
|
||||
xfree (relptr);
|
||||
|
||||
/* Read the extended capabilities. */
|
||||
relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
|
||||
if (!relptr)
|
||||
{
|
||||
@ -2790,6 +2936,7 @@ app_select_openpgp (app_t app)
|
||||
}
|
||||
if (buflen)
|
||||
{
|
||||
app->app_local->extcap.sm_supported = !!(*buffer & 0x80);
|
||||
app->app_local->extcap.get_challenge = !!(*buffer & 0x40);
|
||||
app->app_local->extcap.key_import = !!(*buffer & 0x20);
|
||||
app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
|
||||
@ -2798,7 +2945,12 @@ app_select_openpgp (app_t app)
|
||||
if (buflen >= 10)
|
||||
{
|
||||
/* 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_cmd_data = (buffer[6] << 8 | buffer[7]);
|
||||
app->app_local->extcap.max_rsp_data = (buffer[8] << 8 | buffer[9]);
|
||||
}
|
||||
xfree (relptr);
|
||||
|
||||
@ -2809,6 +2961,9 @@ app_select_openpgp (app_t app)
|
||||
|
||||
parse_login_data (app);
|
||||
|
||||
if (opt.verbose)
|
||||
show_caps (app->app_local);
|
||||
|
||||
if (opt.verbose > 1)
|
||||
dump_all_do (slot);
|
||||
|
||||
@ -2818,6 +2973,7 @@ app_select_openpgp (app_t app)
|
||||
app->fnc.readkey = do_readkey;
|
||||
app->fnc.getattr = do_getattr;
|
||||
app->fnc.setattr = do_setattr;
|
||||
app->fnc.writecert = do_writecert;
|
||||
app->fnc.writekey = do_writekey;
|
||||
app->fnc.genkey = do_genkey;
|
||||
app->fnc.sign = do_sign;
|
||||
|
33
scd/app.c
33
scd/app.c
@ -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
|
||||
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
|
||||
PIN cache and other cached data are preserved. */
|
||||
not get reset even if only one session is using the card - this
|
||||
way the PIN cache and other cached data are preserved. */
|
||||
if (!app && lock_table[slot].initialized && 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. */
|
||||
gpg_error_t
|
||||
app_writekey (app_t app, ctrl_t ctrl,
|
||||
@ -759,7 +787,6 @@ app_writekey (app_t app, ctrl_t ctrl,
|
||||
if (opt.verbose)
|
||||
log_info ("operation writekey result: %s\n", gpg_strerror (err));
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
111
scd/atr.c
111
scd/atr.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -1957,7 +1957,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
|
||||
size_t *nresp)
|
||||
{
|
||||
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;
|
||||
size_t apdulen;
|
||||
unsigned char *msg;
|
||||
@ -1971,7 +1971,9 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
|
||||
apdulen = apdu_buflen;
|
||||
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. */
|
||||
|
||||
msg[0] = PC_to_RDR_XfrBlock;
|
||||
@ -2117,6 +2119,7 @@ ccid_transceive (ccid_driver_t handle,
|
||||
assert (apdulen);
|
||||
|
||||
/* Construct an I-Block. */
|
||||
#warning fixme: APDULEN may be larger
|
||||
if (apdulen > 254)
|
||||
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
|
||||
|
||||
|
111
scd/command.c
111
scd/command.c
@ -47,6 +47,9 @@
|
||||
/* Maximum allowed size of key data as used in inquiries. */
|
||||
#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))
|
||||
|
||||
@ -851,14 +854,8 @@ cmd_pksign (assuan_context_t ctx, char *line)
|
||||
hash_algo = GCRY_MD_SHA1;
|
||||
else
|
||||
return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
|
||||
/* Skip over options. */
|
||||
while ( *line == '-' && line[1] == '-' )
|
||||
{
|
||||
while (*line && !spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
|
||||
line = skip_options (line);
|
||||
|
||||
if ( IS_LOCKED (ctrl) )
|
||||
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
|
||||
application. NAME and VALUE must be percent and '+' escaped.
|
||||
|
||||
However, the curent implementation assumes that Name is not escaped;
|
||||
this works as long as noone uses arbitrary escaping.
|
||||
However, the current implementation assumes that NAME is not
|
||||
escaped; this works as long as noone uses arbitrary escaping.
|
||||
|
||||
A PIN will be requested for most NAMEs. See the corresponding
|
||||
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>
|
||||
|
||||
This command is used to store a secret key on a a smartcard. The
|
||||
allowed keyids depend on the currently selected smartcard
|
||||
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.
|
||||
The keydata is expected to be the usual canonical encoded
|
||||
S-expression.
|
||||
@ -1109,14 +1168,8 @@ cmd_writekey (assuan_context_t ctx, char *line)
|
||||
if ( IS_LOCKED (ctrl) )
|
||||
return gpg_error (GPG_ERR_LOCKED);
|
||||
|
||||
/* Skip over options. */
|
||||
while ( *line == '-' && line[1] == '-' )
|
||||
{
|
||||
while (*line && !spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
line = skip_options (line);
|
||||
|
||||
if (!*line)
|
||||
return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given");
|
||||
keyid = line;
|
||||
@ -1294,14 +1347,8 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
||||
if ( IS_LOCKED (ctrl) )
|
||||
return gpg_error (GPG_ERR_LOCKED);
|
||||
|
||||
/* Skip over options. */
|
||||
while (*line == '-' && line[1] == '-')
|
||||
{
|
||||
while (*line && !spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
line = skip_options (line);
|
||||
|
||||
if (!*line)
|
||||
return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given");
|
||||
chvnostr = line;
|
||||
@ -1601,14 +1648,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
|
||||
with_atr = has_option (line, "--atr");
|
||||
handle_more = has_option (line, "--more");
|
||||
|
||||
/* Skip over options. */
|
||||
while ( *line == '-' && line[1] == '-' )
|
||||
{
|
||||
while (*line && !spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
line = skip_options (line);
|
||||
|
||||
if ( IS_LOCKED (ctrl) )
|
||||
return gpg_error (GPG_ERR_LOCKED);
|
||||
@ -1687,6 +1727,7 @@ register_commands (assuan_context_t ctx)
|
||||
{ "OUTPUT", NULL },
|
||||
{ "GETATTR", cmd_getattr },
|
||||
{ "SETATTR", cmd_setattr },
|
||||
{ "WRITECERT", cmd_writecert },
|
||||
{ "WRITEKEY", cmd_writekey },
|
||||
{ "GENKEY", cmd_genkey },
|
||||
{ "RANDOM", cmd_random },
|
||||
|
@ -66,7 +66,10 @@ map_sw (int sw)
|
||||
switch (sw)
|
||||
{
|
||||
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_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_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; 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)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
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 );
|
||||
return map_sw (sw);
|
||||
}
|
||||
@ -192,7 +195,7 @@ iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
|
||||
|
||||
p0 = 0x08;
|
||||
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 );
|
||||
return map_sw (sw);
|
||||
}
|
||||
@ -253,7 +256,7 @@ iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
|
||||
pininfo->maxlen,
|
||||
pininfo->padlen);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -300,7 +303,7 @@ iso7816_change_reference_data_kp (int slot, int chvno,
|
||||
pininfo->maxlen,
|
||||
pininfo->padlen);
|
||||
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);
|
||||
xfree (buf);
|
||||
return map_sw (sw);
|
||||
@ -340,7 +343,7 @@ iso7816_reset_retry_counter_kp (int slot, int chvno,
|
||||
pininfo->maxlen,
|
||||
pininfo->padlen);
|
||||
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);
|
||||
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
|
||||
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
|
||||
iso7816_put_data (int slot, int tag,
|
||||
iso7816_put_data (int slot, int extended_mode, int tag,
|
||||
const unsigned char *data, size_t datalen)
|
||||
{
|
||||
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),
|
||||
datalen, (const char*)data);
|
||||
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 )
|
||||
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);
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
|
||||
iso7816_pininfo_t *pininfo);
|
||||
gpg_error_t iso7816_get_data (int slot, int tag,
|
||||
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);
|
||||
gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
|
||||
const unsigned char *data,
|
||||
|
@ -982,7 +982,7 @@ gpgsm_format_keydesc (ksba_cert_t cert)
|
||||
/* We also escape the quote character to work around a bug in
|
||||
the mingw32 runtime which does not correcty handle command
|
||||
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
|
||||
quote. */
|
||||
if (*s < ' ' || *s == '+' || *s == '\"')
|
||||
|
Loading…
x
Reference in New Issue
Block a user