mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-03 22:56:33 +02:00
* app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c
* app-nks.c (get_length_of_cert): Removed. * app-help.c: New. (app_help_read_length_of_cert): New. Code taken from above. New optional arg R_CERTOFF. * card-dinsig.c: Removed. * card.c (card_get_serial_and_stamp): Do not bind to the old and never finsiged card-dinsig.c. * iso7816.c (iso7816_read_binary): Allow for an NMAX > 254.
This commit is contained in:
parent
050b96f790
commit
e209ea3c39
10 changed files with 562 additions and 103 deletions
316
scd/app-dinsig.c
316
scd/app-dinsig.c
|
@ -1,5 +1,5 @@
|
|||
/* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
|
||||
* Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2002, 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
|
@ -65,9 +65,6 @@
|
|||
The letters in brackets indicate optional or mandatory files: The
|
||||
first for card terminals under full control and the second for
|
||||
"business" card terminals.
|
||||
|
||||
FIXME: Needs a lot more explanation.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
@ -85,18 +82,318 @@
|
|||
|
||||
#include "iso7816.h"
|
||||
#include "app-common.h"
|
||||
|
||||
#include "tlv.h"
|
||||
|
||||
|
||||
static int
|
||||
do_learn_status (APP app, CTRL ctrl)
|
||||
do_learn_status (app_t app, ctrl_t ctrl)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char ct_buf[100], id_buf[100];
|
||||
char hexkeygrip[41];
|
||||
size_t len, certoff;
|
||||
unsigned char *der;
|
||||
size_t derlen;
|
||||
ksba_cert_t cert;
|
||||
int fid;
|
||||
|
||||
/* Return the certificate of the card holder. */
|
||||
fid = 0xC000;
|
||||
len = app_help_read_length_of_cert (app->slot, fid, &certoff);
|
||||
if (!len)
|
||||
return 0; /* Card has not been personalized. */
|
||||
|
||||
sprintf (ct_buf, "%d", 101);
|
||||
sprintf (id_buf, "DINSIG.%04X", fid);
|
||||
send_status_info (ctrl, "CERTINFO",
|
||||
ct_buf, strlen (ct_buf),
|
||||
id_buf, strlen (id_buf),
|
||||
NULL, (size_t)0);
|
||||
|
||||
/* Now we need to read the certificate, so that we can get the
|
||||
public key out of it. */
|
||||
err = iso7816_read_binary (app->slot, certoff, len-certoff, &der, &derlen);
|
||||
if (err)
|
||||
{
|
||||
log_info ("error reading entire certificate from FID 0x%04X: %s\n",
|
||||
fid, gpg_strerror (err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = ksba_cert_new (&cert);
|
||||
if (err)
|
||||
{
|
||||
xfree (der);
|
||||
return err;
|
||||
}
|
||||
err = ksba_cert_init_from_mem (cert, der, derlen);
|
||||
xfree (der); der = NULL;
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to parse the certificate at FID 0x%04X: %s\n",
|
||||
fid, gpg_strerror (err));
|
||||
ksba_cert_release (cert);
|
||||
return err;
|
||||
}
|
||||
err = app_help_get_keygrip_string (cert, hexkeygrip);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid);
|
||||
ksba_cert_release (cert);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
ksba_cert_release (cert);
|
||||
|
||||
sprintf (id_buf, "DINSIG.%04X", fid);
|
||||
send_status_info (ctrl, "KEYPAIRINFO",
|
||||
hexkeygrip, 40,
|
||||
id_buf, strlen (id_buf),
|
||||
NULL, (size_t)0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Read the certificate with id CERTID (as returned by learn_status in
|
||||
the CERTINFO status lines) and return it in the freshly allocated
|
||||
buffer put into CERT and the length of the certificate put into
|
||||
CERTLEN.
|
||||
|
||||
FIXME: This needs some cleanups and caching with do_learn_status.
|
||||
*/
|
||||
static int
|
||||
do_readcert (app_t app, const char *certid,
|
||||
unsigned char **cert, size_t *certlen)
|
||||
{
|
||||
int fid;
|
||||
gpg_error_t err;
|
||||
unsigned char *buffer;
|
||||
const unsigned char *p;
|
||||
size_t buflen, n;
|
||||
int class, tag, constructed, ndef;
|
||||
size_t totobjlen, objlen, hdrlen;
|
||||
int rootca = 0;
|
||||
|
||||
*cert = NULL;
|
||||
*certlen = 0;
|
||||
if (strncmp (certid, "DINSIG.", 7) )
|
||||
return gpg_error (GPG_ERR_INV_ID);
|
||||
certid += 7;
|
||||
if (!hexdigitp (certid) || !hexdigitp (certid+1)
|
||||
|| !hexdigitp (certid+2) || !hexdigitp (certid+3)
|
||||
|| certid[4])
|
||||
return gpg_error (GPG_ERR_INV_ID);
|
||||
fid = xtoi_4 (certid);
|
||||
if (fid != 0xC000 )
|
||||
return gpg_error (GPG_ERR_NOT_FOUND);
|
||||
|
||||
/* Read the entire file. fixme: This could be optimized by first
|
||||
reading the header to figure out how long the certificate
|
||||
actually is. */
|
||||
err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error reading certificate from FID 0x%04X: %s\n",
|
||||
fid, gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!buflen || *buffer == 0xff)
|
||||
{
|
||||
log_info ("no certificate contained in FID 0x%04X\n", fid);
|
||||
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Now figure something out about the object. */
|
||||
p = buffer;
|
||||
n = buflen;
|
||||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||
&ndef, &objlen, &hdrlen);
|
||||
if (err)
|
||||
goto leave;
|
||||
if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed )
|
||||
;
|
||||
else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
|
||||
rootca = 1;
|
||||
else
|
||||
return gpg_error (GPG_ERR_INV_OBJ);
|
||||
totobjlen = objlen + hdrlen;
|
||||
assert (totobjlen <= buflen);
|
||||
|
||||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||
&ndef, &objlen, &hdrlen);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
if (rootca)
|
||||
;
|
||||
else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
|
||||
{
|
||||
const unsigned char *save_p;
|
||||
|
||||
/* The certificate seems to be contained in a userCertificate
|
||||
container. Skip this and assume the following sequence is
|
||||
the certificate. */
|
||||
if (n < objlen)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_OBJ);
|
||||
goto leave;
|
||||
}
|
||||
p += objlen;
|
||||
n -= objlen;
|
||||
save_p = p;
|
||||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||||
&ndef, &objlen, &hdrlen);
|
||||
if (err)
|
||||
goto leave;
|
||||
if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
|
||||
return gpg_error (GPG_ERR_INV_OBJ);
|
||||
totobjlen = objlen + hdrlen;
|
||||
assert (save_p + totobjlen <= buffer + buflen);
|
||||
memmove (buffer, save_p, totobjlen);
|
||||
}
|
||||
|
||||
*cert = buffer;
|
||||
buffer = NULL;
|
||||
*certlen = totobjlen;
|
||||
|
||||
leave:
|
||||
xfree (buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Verify the PIN if required. */
|
||||
static int
|
||||
verify_pin (app_t app,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg)
|
||||
{
|
||||
if (!app->did_chv1 || app->force_chv1 )
|
||||
{
|
||||
char *pinvalue;
|
||||
int rc;
|
||||
|
||||
rc = pincb (pincb_arg, "PIN", &pinvalue);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* We require the PIN to be at least 6 and at max 8 bytes.
|
||||
According to the specs, this should all be ASCII but we don't
|
||||
check this. */
|
||||
if (strlen (pinvalue) < 6)
|
||||
{
|
||||
log_error ("PIN is too short; minimum length is 6\n");
|
||||
xfree (pinvalue);
|
||||
return gpg_error (GPG_ERR_BAD_PIN);
|
||||
}
|
||||
else if (strlen (pinvalue) > 8)
|
||||
{
|
||||
log_error ("PIN is too large; maximum length is 8\n");
|
||||
xfree (pinvalue);
|
||||
return gpg_error (GPG_ERR_BAD_PIN);
|
||||
}
|
||||
|
||||
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
|
||||
if (rc)
|
||||
{
|
||||
log_error ("verify PIN failed\n");
|
||||
xfree (pinvalue);
|
||||
return rc;
|
||||
}
|
||||
app->did_chv1 = 1;
|
||||
xfree (pinvalue);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create the signature and return the allocated result in OUTDATA.
|
||||
If a PIN is required the PINCB will be used to ask for the PIN;
|
||||
that callback should return the PIN in an allocated buffer and
|
||||
store that in the 3rd argument. */
|
||||
static int
|
||||
do_sign (app_t app, const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
|
||||
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
|
||||
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
|
||||
static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
|
||||
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
|
||||
0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
|
||||
int rc;
|
||||
int fid;
|
||||
unsigned char data[35]; /* Must be large enough for a SHA-1 digest
|
||||
+ the largest OID _prefix above. */
|
||||
|
||||
if (!keyidstr || !*keyidstr)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (indatalen != 20 && indatalen != 16 && indatalen != 35)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
/* Check that the provided ID is vaid. This is not really needed
|
||||
but we do it to to enforce correct usage by the caller. */
|
||||
if (strncmp (keyidstr, "DINSIG.", 7) )
|
||||
return gpg_error (GPG_ERR_INV_ID);
|
||||
keyidstr += 7;
|
||||
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
|
||||
|| !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
|
||||
|| keyidstr[4])
|
||||
return gpg_error (GPG_ERR_INV_ID);
|
||||
fid = xtoi_4 (keyidstr);
|
||||
if (fid != 0xC000)
|
||||
return gpg_error (GPG_ERR_NOT_FOUND);
|
||||
|
||||
/* Prepare the DER object from INDATA. */
|
||||
if (indatalen == 35)
|
||||
{
|
||||
/* Alright, the caller was so kind to send us an already
|
||||
prepared DER object. Check that it is what we want and that
|
||||
it matches the hash algorithm. */
|
||||
if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
|
||||
;
|
||||
else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15))
|
||||
;
|
||||
else
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||
memcpy (data, indata, indatalen);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hashalgo == GCRY_MD_SHA1)
|
||||
memcpy (data, sha1_prefix, 15);
|
||||
else if (hashalgo == GCRY_MD_RMD160)
|
||||
memcpy (data, rmd160_prefix, 15);
|
||||
else
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||
memcpy (data+15, indata, indatalen);
|
||||
}
|
||||
|
||||
rc = verify_pin (app, pincb, pincb_arg);
|
||||
if (!rc)
|
||||
rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Select the DINSIG application on the card in SLOT. This function
|
||||
must be used before any other DINSIG application functions. */
|
||||
|
@ -113,17 +410,18 @@ app_select_dinsig (APP app)
|
|||
app->apptype = "DINSIG";
|
||||
|
||||
app->fnc.learn_status = do_learn_status;
|
||||
app->fnc.readcert = do_readcert;
|
||||
app->fnc.getattr = NULL;
|
||||
app->fnc.setattr = NULL;
|
||||
app->fnc.genkey = NULL;
|
||||
app->fnc.sign = NULL;
|
||||
app->fnc.sign = do_sign;
|
||||
app->fnc.auth = NULL;
|
||||
app->fnc.decipher = NULL;
|
||||
app->fnc.change_pin = NULL;
|
||||
app->fnc.check_pin = NULL;
|
||||
|
||||
app->force_chv1 = 1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue