mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
b008274afd
We better do this once and for all instead of cluttering all future commits with diffs of trailing white spaces. In the majority of cases blank or single lines are affected and thus this change won't disturb a git blame too much. For future commits the pre-commit scripts checks that this won't happen again.
575 lines
18 KiB
C
575 lines
18 KiB
C
/* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
|
|
* Copyright (C) 2002, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
/* The German signature law and its bylaw (SigG and SigV) is currently
|
|
used with an interface specification described in DIN V 66291-1.
|
|
The AID to be used is: 'D27600006601'.
|
|
|
|
The file IDs for certificates utilize the generic format:
|
|
Cxyz
|
|
C being the hex digit 'C' (12).
|
|
x being the service indicator:
|
|
'0' := SigG conform digital signature.
|
|
'1' := entity authentication.
|
|
'2' := key encipherment.
|
|
'3' := data encipherment.
|
|
'4' := key agreement.
|
|
other values are reserved for future use.
|
|
y being the security environment number using '0' for cards
|
|
not supporting a SE number.
|
|
z being the certificate type:
|
|
'0' := C.CH (base certificate of card holder) or C.ICC.
|
|
'1' .. '7' := C.CH (business or professional certificate
|
|
of card holder.
|
|
'8' .. 'D' := C.CA (certificate of a CA issue by the Root-CA).
|
|
'E' := C.RCA (self certified certificate of the Root-CA).
|
|
'F' := reserved.
|
|
|
|
The file IDs used by default are:
|
|
'1F00' EF.SSD (security service descriptor). [o,o]
|
|
'2F02' EF.GDO (global data objects) [m,m]
|
|
'A000' EF.PROT (signature log). Cyclic file with 20 records of 53 byte.
|
|
Read and update after user authentication. [o,o]
|
|
'B000' EF.PK.RCA.DS (public keys of Root-CA). Size is 512b or size
|
|
of keys. [m (unless a 'C00E' is present),m]
|
|
'B001' EF.PK.CA.DS (public keys of CAs). Size is 512b or size
|
|
of keys. [o,o]
|
|
'C00n' EF.C.CH.DS (digital signature certificate of card holder)
|
|
with n := 0 .. 7. Size is 2k or size of cert. Read and
|
|
update allowed after user authentication. [m,m]
|
|
'C00m' EF.C.CA.DS (digital signature certificate of CA)
|
|
with m := 8 .. E. Size is 1k or size of cert. Read always
|
|
allowed, update after user authentication. [o,o]
|
|
'C100' EF.C.ICC.AUT (AUT certificate of ICC) [o,m]
|
|
'C108' EF.C.CA.AUT (AUT certificate of CA) [o,m]
|
|
'D000' EF.DM (display message) [-,m]
|
|
|
|
The letters in brackets indicate optional or mandatory files: The
|
|
first for card terminals under full control and the second for
|
|
"business" card terminals.
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
|
|
#include "scdaemon.h"
|
|
|
|
#include "i18n.h"
|
|
#include "iso7816.h"
|
|
#include "app-common.h"
|
|
#include "tlv.h"
|
|
|
|
|
|
static gpg_error_t
|
|
do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
|
|
{
|
|
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;
|
|
|
|
(void)flags;
|
|
|
|
/* 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 gpg_error_t
|
|
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 gpg_error_t
|
|
verify_pin (app_t app,
|
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
|
void *pincb_arg)
|
|
{
|
|
const char *s;
|
|
int rc;
|
|
iso7816_pininfo_t pininfo;
|
|
|
|
if ( app->did_chv1 && !app->force_chv1 )
|
|
return 0; /* No need to verify it again. */
|
|
|
|
memset (&pininfo, 0, sizeof pininfo);
|
|
pininfo.mode = 1;
|
|
pininfo.minlen = 6;
|
|
pininfo.maxlen = 8;
|
|
|
|
if (!opt.disable_keypad
|
|
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
|
|
{
|
|
rc = pincb (pincb_arg,
|
|
_("||Please enter your PIN at the reader's keypad"),
|
|
NULL);
|
|
if (rc)
|
|
{
|
|
log_info (_("PIN callback returned error: %s\n"),
|
|
gpg_strerror (rc));
|
|
return rc;
|
|
}
|
|
rc = iso7816_verify_kp (app->slot, 0x81, "", 0, &pininfo);
|
|
/* Dismiss the prompt. */
|
|
pincb (pincb_arg, NULL, NULL);
|
|
}
|
|
else /* No Keypad. */
|
|
{
|
|
char *pinvalue;
|
|
|
|
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. */
|
|
for (s=pinvalue; digitp (s); s++)
|
|
;
|
|
if (*s)
|
|
{
|
|
log_error ("Non-numeric digits found in PIN\n");
|
|
xfree (pinvalue);
|
|
return gpg_error (GPG_ERR_BAD_PIN);
|
|
}
|
|
|
|
if (strlen (pinvalue) < pininfo.minlen)
|
|
{
|
|
log_error ("PIN is too short; minimum length is %d\n",
|
|
pininfo.minlen);
|
|
xfree (pinvalue);
|
|
return gpg_error (GPG_ERR_BAD_PIN);
|
|
}
|
|
else if (strlen (pinvalue) > pininfo.maxlen)
|
|
{
|
|
log_error ("PIN is too large; maximum length is %d\n",
|
|
pininfo.maxlen);
|
|
xfree (pinvalue);
|
|
return gpg_error (GPG_ERR_BAD_PIN);
|
|
}
|
|
|
|
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
|
|
if (gpg_err_code (rc) == GPG_ERR_INV_VALUE)
|
|
{
|
|
/* We assume that ISO 9564-1 encoding is used and we failed
|
|
because the first nibble we passed was 3 and not 2. DIN
|
|
says something about looking up such an encoding in the
|
|
SSD but I was not able to find any tag relevant to
|
|
this. */
|
|
char paddedpin[8];
|
|
int i, ndigits;
|
|
|
|
for (ndigits=0, s=pinvalue; *s; ndigits++, s++)
|
|
;
|
|
i = 0;
|
|
paddedpin[i++] = 0x20 | (ndigits & 0x0f);
|
|
for (s=pinvalue; i < sizeof paddedpin && *s && s[1]; s = s+2 )
|
|
paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f));
|
|
if (i < sizeof paddedpin && *s)
|
|
paddedpin[i++] = (((*s - '0') << 4) | 0x0f);
|
|
while (i < sizeof paddedpin)
|
|
paddedpin[i++] = 0xff;
|
|
rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin);
|
|
}
|
|
xfree (pinvalue);
|
|
}
|
|
|
|
if (rc)
|
|
{
|
|
log_error ("verify PIN failed\n");
|
|
return rc;
|
|
}
|
|
app->did_chv1 = 1;
|
|
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 gpg_error_t
|
|
do_sign (app_t app, const char *keyidstr, int hashalgo,
|
|
gpg_error_t (*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 };
|
|
static unsigned char sha256_prefix[19] = /* OID is 2.16.840.1.101.3.4.2.1 */
|
|
{ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
|
|
0x00, 0x04, 0x20 };
|
|
int rc;
|
|
int fid;
|
|
unsigned char data[19+32]; /* Must be large enough for a SHA-256 digest
|
|
+ the largest OID _prefix above. */
|
|
int datalen;
|
|
|
|
if (!keyidstr || !*keyidstr)
|
|
return gpg_error (GPG_ERR_INV_VALUE);
|
|
if (indatalen != 20 && indatalen != 16 && indatalen != 32
|
|
&& indatalen != (15+20) && indatalen != (19+32))
|
|
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. */
|
|
datalen = 35;
|
|
if (indatalen == 15+20)
|
|
{
|
|
/* 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 (indatalen == 19+32)
|
|
{
|
|
/* 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. */
|
|
datalen = indatalen;
|
|
if (hashalgo == GCRY_MD_SHA256 && !memcmp (indata, sha256_prefix, 19))
|
|
;
|
|
else if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha256_prefix, 19))
|
|
{
|
|
/* Fixme: This is a kludge. A better solution is not to use
|
|
SHA1 as default but use an autodetection. However this
|
|
needs changes in all app-*.c */
|
|
hashalgo = GCRY_MD_SHA256;
|
|
datalen = indatalen;
|
|
}
|
|
else
|
|
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
|
memcpy (data, indata, indatalen);
|
|
}
|
|
else
|
|
{
|
|
int len = 15;
|
|
if (hashalgo == GCRY_MD_SHA1)
|
|
memcpy (data, sha1_prefix, len);
|
|
else if (hashalgo == GCRY_MD_RMD160)
|
|
memcpy (data, rmd160_prefix, len);
|
|
else if (hashalgo == GCRY_MD_SHA256)
|
|
{
|
|
len = 19;
|
|
datalen = len + indatalen;
|
|
memcpy (data, sha256_prefix, len);
|
|
}
|
|
else
|
|
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
|
memcpy (data+len, indata, indatalen);
|
|
}
|
|
|
|
rc = verify_pin (app, pincb, pincb_arg);
|
|
if (!rc)
|
|
rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0,
|
|
outdata, outdatalen);
|
|
return rc;
|
|
}
|
|
|
|
|
|
#if 0
|
|
#warning test function - works but may brick your card
|
|
/* Handle the PASSWD command. CHVNOSTR is currently ignored; we
|
|
always use VHV0. RESET_MODE is not yet implemented. */
|
|
static gpg_error_t
|
|
do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
|
unsigned int flags,
|
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
|
void *pincb_arg)
|
|
{
|
|
gpg_error_t err;
|
|
char *pinvalue;
|
|
const char *oldpin;
|
|
size_t oldpinlen;
|
|
|
|
if ((flags & APP_CHANGE_FLAG_RESET))
|
|
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
|
|
|
if ((flags & APP_CHANGE_FLAG_NULLPIN))
|
|
{
|
|
/* With the nullpin flag, we do not verify the PIN - it would fail
|
|
if the Nullpin is still set. */
|
|
oldpin = "\0\0\0\0\0";
|
|
oldpinlen = 6;
|
|
}
|
|
else
|
|
{
|
|
err = verify_pin (app, pincb, pincb_arg);
|
|
if (err)
|
|
return err;
|
|
oldpin = NULL;
|
|
oldpinlen = 0;
|
|
}
|
|
|
|
/* TRANSLATORS: Do not translate the "|*|" prefixes but
|
|
keep it at the start of the string. We need this elsewhere
|
|
to get some infos on the string. */
|
|
err = pincb (pincb_arg, _("|N|Initial New PIN"), &pinvalue);
|
|
if (err)
|
|
{
|
|
log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
|
|
return err;
|
|
}
|
|
|
|
err = iso7816_change_reference_data (app->slot, 0x81,
|
|
oldpin, oldpinlen,
|
|
pinvalue, strlen (pinvalue));
|
|
xfree (pinvalue);
|
|
return err;
|
|
}
|
|
#endif /*0*/
|
|
|
|
|
|
/* Select the DINSIG application on the card in SLOT. This function
|
|
must be used before any other DINSIG application functions. */
|
|
gpg_error_t
|
|
app_select_dinsig (app_t app)
|
|
{
|
|
static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
|
|
int slot = app->slot;
|
|
int rc;
|
|
|
|
rc = iso7816_select_application (slot, aid, sizeof aid, 0);
|
|
if (!rc)
|
|
{
|
|
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 = do_sign;
|
|
app->fnc.auth = NULL;
|
|
app->fnc.decipher = NULL;
|
|
app->fnc.change_pin = NULL /*do_change_pin*/;
|
|
app->fnc.check_pin = NULL;
|
|
|
|
app->force_chv1 = 1;
|
|
}
|
|
|
|
return rc;
|
|
}
|