mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-05 12:31:50 +01:00
scd:openpgp: Allow reading and writing user certs for keys 1 and 2
* scd/iso7816.c (CMD_SELECT_DATA): New. (iso7816_select_data): New. * scd/app-openpgp.c (do_readcert): Allow OpenPGP.1 and OPENPGP.2 (do_writecert): Ditto. (do_setattr): Add CERT-1 and CERT-2. -- This has been tested with a Zeitcontrol 3.4 card. A test with a Yubikey 5 (firmware 5.2.6) claiming to support 3.4 failed. Signed-off-by: Werner Koch <wk@gnupg.org> (cherry picked from commit 37b1c5c2004c1147a13b388863aaa8f0caf7d71f)
This commit is contained in:
parent
b2363c1dd9
commit
57bfad2c39
@ -2155,39 +2155,73 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
|
|||||||
|
|
||||||
/* Read the standard 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. */
|
||||||
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)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
unsigned char *buffer;
|
int occurrence = 0;
|
||||||
size_t buflen;
|
|
||||||
void *relptr;
|
|
||||||
|
|
||||||
*cert = NULL;
|
*cert = NULL;
|
||||||
*certlen = 0;
|
*certlen = 0;
|
||||||
if (strcmp (certid, "OPENPGP.3"))
|
if (!ascii_strcasecmp (certid, "OPENPGP.3"))
|
||||||
|
;
|
||||||
|
else if (!ascii_strcasecmp (certid, "OPENPGP.2"))
|
||||||
|
occurrence = 1;
|
||||||
|
else if (!ascii_strcasecmp (certid, "OPENPGP.1"))
|
||||||
|
occurrence = 2;
|
||||||
|
else
|
||||||
return gpg_error (GPG_ERR_INV_ID);
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
if (!app->app_local->extcap.is_v3 && occurrence)
|
||||||
|
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||||
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, 0x7F21, &buffer, &buflen, NULL);
|
if (occurrence)
|
||||||
if (!relptr)
|
{
|
||||||
return gpg_error (GPG_ERR_NOT_FOUND);
|
int exmode;
|
||||||
|
|
||||||
if (!buflen)
|
err = iso7816_select_data (app_get_slot (app), occurrence, 0x7F21);
|
||||||
err = gpg_error (GPG_ERR_NOT_FOUND);
|
if (!err)
|
||||||
else if (!(*cert = xtrymalloc (buflen)))
|
{
|
||||||
err = gpg_error_from_syserror ();
|
if (app->app_local->cardcap.ext_lc_le)
|
||||||
|
exmode = app->app_local->extcap.max_certlen;
|
||||||
|
else
|
||||||
|
exmode = 0;
|
||||||
|
|
||||||
|
err = iso7816_get_data (app_get_slot (app), exmode, 0x7F21,
|
||||||
|
cert, certlen);
|
||||||
|
/* We reset the curDO even for an error. */
|
||||||
|
iso7816_select_data (app_get_slot (app), 0, 0x7F21);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy (*cert, buffer, buflen);
|
unsigned char *buffer;
|
||||||
*certlen = buflen;
|
size_t buflen;
|
||||||
err = 0;
|
void *relptr;
|
||||||
|
|
||||||
|
relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL);
|
||||||
|
if (!relptr)
|
||||||
|
return gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
|
||||||
|
if (!buflen)
|
||||||
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
else if (!(*cert = xtrymalloc (buflen)))
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy (*cert, buffer, buflen);
|
||||||
|
*certlen = buflen;
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
xfree (relptr);
|
||||||
}
|
}
|
||||||
xfree (relptr);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2691,6 +2725,7 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name,
|
|||||||
int need_chv;
|
int need_chv;
|
||||||
int special;
|
int special;
|
||||||
unsigned int need_v2:1;
|
unsigned int need_v2:1;
|
||||||
|
unsigned int need_v3:1;
|
||||||
} table[] = {
|
} table[] = {
|
||||||
{ "DISP-NAME", 0x005B, 0, 3 },
|
{ "DISP-NAME", 0x005B, 0, 3 },
|
||||||
{ "LOGIN-DATA", 0x005E, 0, 3, 2 },
|
{ "LOGIN-DATA", 0x005E, 0, 3, 2 },
|
||||||
@ -2705,6 +2740,8 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name,
|
|||||||
{ "PRIVATE-DO-2", 0x0102, 0, 3 },
|
{ "PRIVATE-DO-2", 0x0102, 0, 3 },
|
||||||
{ "PRIVATE-DO-3", 0x0103, 0, 2 },
|
{ "PRIVATE-DO-3", 0x0103, 0, 2 },
|
||||||
{ "PRIVATE-DO-4", 0x0104, 0, 3 },
|
{ "PRIVATE-DO-4", 0x0104, 0, 3 },
|
||||||
|
{ "CERT-1", 0x7F21, 0, 3,11, 1, 1 },
|
||||||
|
{ "CERT-2", 0x7F21, 0, 3,12, 1, 1 },
|
||||||
{ "CERT-3", 0x7F21, 0, 3, 0, 1 },
|
{ "CERT-3", 0x7F21, 0, 3, 0, 1 },
|
||||||
{ "SM-KEY-ENC", 0x00D1, 0, 3, 0, 1 },
|
{ "SM-KEY-ENC", 0x00D1, 0, 3, 0, 1 },
|
||||||
{ "SM-KEY-MAC", 0x00D2, 0, 3, 0, 1 },
|
{ "SM-KEY-MAC", 0x00D2, 0, 3, 0, 1 },
|
||||||
@ -2720,7 +2757,9 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name,
|
|||||||
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 && !app->app_local->extcap.is_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);
|
||||||
|
if (table[idx].need_v3 && !app->app_local->extcap.is_v3)
|
||||||
|
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||||
|
|
||||||
if (table[idx].special == 3)
|
if (table[idx].special == 3)
|
||||||
return change_keyattr_from_string (app, ctrl, pincb, pincb_arg,
|
return change_keyattr_from_string (app, ctrl, pincb, pincb_arg,
|
||||||
@ -2752,8 +2791,24 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name,
|
|||||||
exmode = -254; /* Command chaining with max. 254 bytes. */
|
exmode = -254; /* Command chaining with max. 254 bytes. */
|
||||||
else
|
else
|
||||||
exmode = 0;
|
exmode = 0;
|
||||||
rc = iso7816_put_data (app_get_slot (app),
|
|
||||||
exmode, table[idx].tag, value, valuelen);
|
if (table[idx].special == 11 || table[idx].special == 12) /* CERT-1 or -2 */
|
||||||
|
{
|
||||||
|
rc = iso7816_select_data (app_get_slot (app),
|
||||||
|
table[idx].special == 11? 2 : 1,
|
||||||
|
table[idx].tag);
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
rc = iso7816_put_data (app_get_slot (app),
|
||||||
|
exmode, table[idx].tag, value, valuelen);
|
||||||
|
/* We better reset the curDO. */
|
||||||
|
iso7816_select_data (app_get_slot (app), 0, table[idx].tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* Standard. */
|
||||||
|
rc = iso7816_put_data (app_get_slot (app),
|
||||||
|
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));
|
||||||
|
|
||||||
@ -2789,15 +2844,25 @@ do_writecert (app_t app, ctrl_t ctrl,
|
|||||||
void *pincb_arg,
|
void *pincb_arg,
|
||||||
const unsigned char *certdata, size_t certdatalen)
|
const unsigned char *certdata, size_t certdatalen)
|
||||||
{
|
{
|
||||||
if (strcmp (certidstr, "OPENPGP.3"))
|
const char *name;
|
||||||
|
if (!ascii_strcasecmp (certidstr, "OPENPGP.3"))
|
||||||
|
name = "CERT-3";
|
||||||
|
else if (!ascii_strcasecmp (certidstr, "OPENPGP.2"))
|
||||||
|
name = "CERT-2";
|
||||||
|
else if (!ascii_strcasecmp (certidstr, "OPENPGP.1"))
|
||||||
|
name = "CERT-1";
|
||||||
|
else
|
||||||
return gpg_error (GPG_ERR_INV_ID);
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
|
||||||
if (!certdata || !certdatalen)
|
if (!certdata || !certdatalen)
|
||||||
return gpg_error (GPG_ERR_INV_ARG);
|
return gpg_error (GPG_ERR_INV_ARG);
|
||||||
if (!app->app_local->extcap.is_v2)
|
if (!app->app_local->extcap.is_v2)
|
||||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||||
|
/* do_setattr checks that CERT-2 and CERT-1 requires a v3 card. */
|
||||||
|
|
||||||
if (certdatalen > app->app_local->extcap.max_certlen)
|
if (certdatalen > app->app_local->extcap.max_certlen)
|
||||||
return gpg_error (GPG_ERR_TOO_LARGE);
|
return gpg_error (GPG_ERR_TOO_LARGE);
|
||||||
return do_setattr (app, ctrl, "CERT-3", pincb, pincb_arg,
|
return do_setattr (app, ctrl, name, pincb, pincb_arg,
|
||||||
certdata, certdatalen);
|
certdata, certdatalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define CMD_SELECT_FILE 0xA4
|
#define CMD_SELECT_FILE 0xA4
|
||||||
|
#define CMD_SELECT_DATA 0xA5
|
||||||
#define CMD_VERIFY ISO7816_VERIFY
|
#define CMD_VERIFY ISO7816_VERIFY
|
||||||
#define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA
|
#define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA
|
||||||
#define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER
|
#define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER
|
||||||
@ -445,6 +446,44 @@ iso7816_reset_retry_counter (int slot, int chvno,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Perform a SELECT DATA command to OCCURANCE of TAG. */
|
||||||
|
gpg_error_t
|
||||||
|
iso7816_select_data (int slot, int occurrence, int tag)
|
||||||
|
{
|
||||||
|
int sw;
|
||||||
|
int datalen;
|
||||||
|
unsigned char data[7];
|
||||||
|
|
||||||
|
data[0] = 0x60;
|
||||||
|
data[2] = 0x5c;
|
||||||
|
if (tag <= 0xff)
|
||||||
|
{
|
||||||
|
data[3] = 1;
|
||||||
|
data[4] = tag;
|
||||||
|
datalen = 5;
|
||||||
|
}
|
||||||
|
else if (tag <= 0xffff)
|
||||||
|
{
|
||||||
|
data[3] = 2;
|
||||||
|
data[4] = (tag >> 8);
|
||||||
|
data[5] = tag;
|
||||||
|
datalen = 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data[3] = 3;
|
||||||
|
data[4] = (tag >> 16);
|
||||||
|
data[5] = (tag >> 8);
|
||||||
|
data[6] = tag;
|
||||||
|
datalen = 7;
|
||||||
|
}
|
||||||
|
data[1] = datalen - 2;
|
||||||
|
|
||||||
|
sw = apdu_send_le (slot, 0, 0x00, CMD_SELECT_DATA,
|
||||||
|
occurrence, 0x04, datalen, data, 0, NULL, NULL);
|
||||||
|
return map_sw (sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Perform a GET DATA command requesting TAG and storing the result in
|
/* Perform a GET DATA command requesting TAG and storing the result in
|
||||||
a newly allocated buffer at the address passed by RESULT. Return
|
a newly allocated buffer at the address passed by RESULT. Return
|
||||||
|
@ -93,6 +93,7 @@ gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
|
|||||||
gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
|
gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
|
||||||
const char *data,
|
const char *data,
|
||||||
size_t datalen);
|
size_t datalen);
|
||||||
|
gpg_error_t iso7816_select_data (int slot, int occurrence, int tag);
|
||||||
gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag,
|
gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag,
|
||||||
unsigned char **result, size_t *resultlen);
|
unsigned char **result, size_t *resultlen);
|
||||||
gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
|
gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user