mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-10 21:38:50 +01:00
scd: Remove unused files.
* scd/Makefile.am (sc_copykeys_*): Remove. * scd/sc-copykeys.c: Remove. * scd/pcsc-wrapper.c: Remove. * scd/{card-common.h,card-dinsig.c,card-p15.c,card.c}: Remove. -- sc-copykeys doesn't work any more because it's based on old API. pcsc-wrapper has gone because of nPth which is compatible to pthreads. The card* files are old files, now we have app*.
This commit is contained in:
parent
5b46726931
commit
67b2dc7636
@ -49,22 +49,3 @@ scdaemon_LDADD = $(libcommonpth) \
|
|||||||
$(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
|
$(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
|
||||||
$(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \
|
$(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \
|
||||||
$(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(resource_objs)
|
$(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(resource_objs)
|
||||||
|
|
||||||
# Removed for now: We need to decide whether it makes sense to
|
|
||||||
# continue it at all, given that gpg has now all required
|
|
||||||
# functionality.
|
|
||||||
#sc_copykeys_SOURCES = \
|
|
||||||
# sc-copykeys.c scdaemon.h \
|
|
||||||
# apdu.c apdu.h \
|
|
||||||
# ccid-driver.c ccid-driver.h \
|
|
||||||
# iso7816.c iso7816.h \
|
|
||||||
# atr.c atr.h \
|
|
||||||
# app.c app-common.h app-help.c $(card_apps)
|
|
||||||
#
|
|
||||||
#sc_copykeys_LDADD = \
|
|
||||||
# ../common/libcommon.a \
|
|
||||||
# ../common/libsimple-pwquery.a \
|
|
||||||
# $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
|
|
||||||
# $(LIBUSB_LIBS) \
|
|
||||||
# -lgpg-error @LIBINTL@ @DL_LIBS@
|
|
||||||
#
|
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
/* card-common.h - Common declarations for all card types
|
|
||||||
* Copyright (C) 2001, 2002 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CARD_COMMON_H
|
|
||||||
#define CARD_COMMON_H
|
|
||||||
|
|
||||||
/* Declaration of private data structure used by card-p15.c */
|
|
||||||
struct p15private_s;
|
|
||||||
|
|
||||||
|
|
||||||
struct card_ctx_s {
|
|
||||||
int reader; /* used reader */
|
|
||||||
struct sc_context *ctx;
|
|
||||||
struct sc_card *scard;
|
|
||||||
struct sc_pkcs15_card *p15card; /* only if there is a pkcs15 application */
|
|
||||||
struct p15private_s *p15priv; /* private data used by card-p15.c */
|
|
||||||
|
|
||||||
struct {
|
|
||||||
int initialized; /* the card has been initialied and the function
|
|
||||||
pointers may be used. However for
|
|
||||||
unsupported operations the particular
|
|
||||||
function pointer is set to NULL */
|
|
||||||
|
|
||||||
int (*enum_keypairs) (CARD card, int idx,
|
|
||||||
unsigned char *keygrip, char **keyid);
|
|
||||||
int (*enum_certs) (CARD card, int idx, char **certid, int *certtype);
|
|
||||||
int (*read_cert) (CARD card, const char *certidstr,
|
|
||||||
unsigned char **cert, size_t *ncert);
|
|
||||||
int (*sign) (CARD card,
|
|
||||||
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 );
|
|
||||||
int (*decipher) (CARD card, const char *keyidstr,
|
|
||||||
int (pincb)(void*, const char *, char **),
|
|
||||||
void *pincb_arg,
|
|
||||||
const void *indata, size_t indatalen,
|
|
||||||
unsigned char **outdata, size_t *outdatalen);
|
|
||||||
} fnc;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/*-- card.c --*/
|
|
||||||
gpg_error_t map_sc_err (int rc);
|
|
||||||
int card_help_get_keygrip (ksba_cert_t cert, unsigned char *array);
|
|
||||||
|
|
||||||
/*-- card-15.c --*/
|
|
||||||
void p15_release_private_data (CARD card);
|
|
||||||
|
|
||||||
/* constructors */
|
|
||||||
void card_p15_bind (CARD card);
|
|
||||||
void card_dinsig_bind (CARD card);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /*CARD_COMMON_H*/
|
|
@ -1,257 +0,0 @@
|
|||||||
/* card-dinsig.c - German signature law (DINSIG) functions
|
|
||||||
* Copyright (C) 2002 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 ard 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 uder 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.
|
|
||||||
|
|
||||||
FIXME: Needs a lot more explanation.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
#include <opensc/pkcs15.h>
|
|
||||||
#include "scdaemon.h"
|
|
||||||
#include <ksba.h>
|
|
||||||
|
|
||||||
#include "card-common.h"
|
|
||||||
|
|
||||||
static int dinsig_read_cert (CARD card, const char *certidstr,
|
|
||||||
unsigned char **cert, size_t *ncert);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* See card.c for interface description. Frankly we don't do any real
|
|
||||||
enumeration but just check whether the well know files are
|
|
||||||
available. */
|
|
||||||
static int
|
|
||||||
dinsig_enum_keypairs (CARD card, int idx,
|
|
||||||
unsigned char *keygrip, char **keyid)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
unsigned char *buf;
|
|
||||||
size_t buflen;
|
|
||||||
ksba_cert_t cert;
|
|
||||||
|
|
||||||
/* fixme: We should locate the application via the EF(DIR) and not
|
|
||||||
assume a Netkey card */
|
|
||||||
if (!idx)
|
|
||||||
rc = dinsig_read_cert (card, "DINSIG-DF01.C000", &buf, &buflen);
|
|
||||||
else if (idx == 1)
|
|
||||||
rc = dinsig_read_cert (card, "DINSIG-DF01.C200", &buf, &buflen);
|
|
||||||
else
|
|
||||||
rc = -1;
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = ksba_cert_new (&cert);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
xfree (buf);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = ksba_cert_init_from_mem (cert, buf, buflen);
|
|
||||||
xfree (buf);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("failed to parse the certificate at idx %d: %s\n",
|
|
||||||
idx, gpg_strerror (rc));
|
|
||||||
ksba_cert_release (cert);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if (card_help_get_keygrip (cert, keygrip))
|
|
||||||
{
|
|
||||||
log_error ("failed to calculate the keygrip at index %d\n", idx);
|
|
||||||
ksba_cert_release (cert);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
ksba_cert_release (cert);
|
|
||||||
|
|
||||||
/* return the iD */
|
|
||||||
if (keyid)
|
|
||||||
{
|
|
||||||
*keyid = xtrymalloc (17);
|
|
||||||
if (!*keyid)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
if (!idx)
|
|
||||||
strcpy (*keyid, "DINSIG-DF01.C000");
|
|
||||||
else
|
|
||||||
strcpy (*keyid, "DINSIG-DF01.C200");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* See card.c for interface description */
|
|
||||||
static int
|
|
||||||
dinsig_read_cert (CARD card, const char *certidstr,
|
|
||||||
unsigned char **cert, size_t *ncert)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
struct sc_path path;
|
|
||||||
struct sc_file *file;
|
|
||||||
unsigned char *buf;
|
|
||||||
int buflen;
|
|
||||||
|
|
||||||
if (!strcmp (certidstr, "DINSIG-DF01.C000"))
|
|
||||||
sc_format_path ("3F00DF01C000", &path);
|
|
||||||
else if (!strcmp (certidstr, "DINSIG-DF01.C200"))
|
|
||||||
sc_format_path ("3F00DF01C200", &path);
|
|
||||||
else
|
|
||||||
return gpg_error (GPG_ERR_INV_ID);
|
|
||||||
|
|
||||||
rc = sc_select_file (card->scard, &path, &file);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("sc_select_file failed: %s\n", sc_strerror (rc));
|
|
||||||
return map_sc_err (rc);
|
|
||||||
}
|
|
||||||
if (file->type != SC_FILE_TYPE_WORKING_EF
|
|
||||||
|| file->ef_structure != SC_FILE_EF_TRANSPARENT)
|
|
||||||
{
|
|
||||||
log_error ("wrong type or structure of certificate EF\n");
|
|
||||||
sc_file_free (file);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
if (file->size < 20) /* check against a somewhat arbitrary length */
|
|
||||||
{
|
|
||||||
log_error ("certificate EF too short\n");
|
|
||||||
sc_file_free (file);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
buf = xtrymalloc (file->size);
|
|
||||||
if (!buf)
|
|
||||||
{
|
|
||||||
gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
sc_file_free (file);
|
|
||||||
return tmperr;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = sc_read_binary (card->scard, 0, buf, file->size, 0);
|
|
||||||
if (rc >= 0 && rc != file->size)
|
|
||||||
{
|
|
||||||
log_error ("short read on certificate EF\n");
|
|
||||||
sc_file_free (file);
|
|
||||||
xfree (buf);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
sc_file_free (file);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
log_error ("error reading certificate EF: %s\n", sc_strerror (rc));
|
|
||||||
xfree (buf);
|
|
||||||
return map_sc_err (rc);
|
|
||||||
}
|
|
||||||
buflen = rc;
|
|
||||||
|
|
||||||
/* The object is not a plain certificate but wrapped into id-at
|
|
||||||
userCertificate - fixme: we should check the specs and decided
|
|
||||||
whether libksba should support it */
|
|
||||||
if (buflen > 9 && buf[0] == 0x30 && buf[4] == 6 && buf[5] == 3
|
|
||||||
&& buf[6] == 0x55 && buf[7] == 4 && buf[8] == 0x24)
|
|
||||||
{
|
|
||||||
/* We have to strip the padding. Although this is a good idea
|
|
||||||
anyway, we have to do it due to a KSBA problem; KSBA does not
|
|
||||||
work correct when the buffer is larger than the ASN.1
|
|
||||||
structure and the certificates here are padded with FF. So
|
|
||||||
as a workaround we look at the outer structure to get the
|
|
||||||
size of the entire thing and adjust the buflen. We can only
|
|
||||||
do this when there is a 2 byte length field */
|
|
||||||
size_t seqlen;
|
|
||||||
if (buf[1] == 0x82)
|
|
||||||
{
|
|
||||||
seqlen = ((buf[2] << 8) | buf[3]) + 4;
|
|
||||||
if (seqlen < buflen)
|
|
||||||
buflen = seqlen;
|
|
||||||
}
|
|
||||||
memmove (buf, buf+9, buflen-9);
|
|
||||||
buflen -= 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
*cert = buf;
|
|
||||||
*ncert = buflen;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Bind our operations to the card */
|
|
||||||
void
|
|
||||||
card_dinsig_bind (CARD card)
|
|
||||||
{
|
|
||||||
card->fnc.enum_keypairs = dinsig_enum_keypairs;
|
|
||||||
card->fnc.read_cert = dinsig_read_cert;
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif /*HAVE_OPENSC*/
|
|
494
scd/card-p15.c
494
scd/card-p15.c
@ -1,494 +0,0 @@
|
|||||||
/* card-p15.c - PKCS-15 based card access
|
|
||||||
* Copyright (C) 2002 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
#include <opensc/pkcs15.h>
|
|
||||||
|
|
||||||
#include "scdaemon.h"
|
|
||||||
#include <ksba.h>
|
|
||||||
#include "card-common.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct p15private_s {
|
|
||||||
int n_prkey_rsa_objs;
|
|
||||||
struct sc_pkcs15_object *prkey_rsa_objs[32];
|
|
||||||
int n_cert_objs;
|
|
||||||
struct sc_pkcs15_object *cert_objs[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Allocate private data. */
|
|
||||||
static int
|
|
||||||
init_private_data (CARD card)
|
|
||||||
{
|
|
||||||
struct p15private_s *priv;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (card->p15priv)
|
|
||||||
return 0; /* already done. */
|
|
||||||
|
|
||||||
priv = xtrycalloc (1, sizeof *priv);
|
|
||||||
if (!priv)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
|
|
||||||
/* OpenSC (0.7.0) is a bit strange in that the get_objects functions
|
|
||||||
tries to be a bit too clever and implicitly does an enumeration
|
|
||||||
which eventually leads to the fact that every call to this
|
|
||||||
fucntion returns one more macthing object. The old code in
|
|
||||||
p15_enum_keypairs assume that it would alwyas return the same
|
|
||||||
numer of objects and used this to figure out what the last object
|
|
||||||
enumerated is. We now do an enum_objects just once and keep it
|
|
||||||
in the private data. */
|
|
||||||
rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_PRKEY_RSA,
|
|
||||||
priv->prkey_rsa_objs,
|
|
||||||
DIM (priv->prkey_rsa_objs));
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
|
|
||||||
xfree (priv);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
priv->n_prkey_rsa_objs = rc;
|
|
||||||
|
|
||||||
/* Read all certificate objects. */
|
|
||||||
rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_CERT_X509,
|
|
||||||
priv->cert_objs,
|
|
||||||
DIM (priv->cert_objs));
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
|
|
||||||
xfree (priv);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
priv->n_cert_objs = rc;
|
|
||||||
|
|
||||||
card->p15priv = priv;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Release private data used in this module. */
|
|
||||||
void
|
|
||||||
p15_release_private_data (CARD card)
|
|
||||||
{
|
|
||||||
if (!card->p15priv)
|
|
||||||
return;
|
|
||||||
xfree (card->p15priv);
|
|
||||||
card->p15priv = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* See card.c for interface description */
|
|
||||||
static int
|
|
||||||
p15_enum_keypairs (CARD card, int idx,
|
|
||||||
unsigned char *keygrip, char **keyid)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
struct p15private_s *priv;
|
|
||||||
struct sc_pkcs15_object *tmpobj;
|
|
||||||
int nobjs;
|
|
||||||
struct sc_pkcs15_prkey_info *pinfo;
|
|
||||||
struct sc_pkcs15_cert_info *certinfo;
|
|
||||||
struct sc_pkcs15_cert *certder;
|
|
||||||
ksba_cert_t cert;
|
|
||||||
|
|
||||||
rc = init_private_data (card);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
priv = card->p15priv;
|
|
||||||
nobjs = priv->n_prkey_rsa_objs;
|
|
||||||
rc = 0;
|
|
||||||
if (idx >= nobjs)
|
|
||||||
return -1;
|
|
||||||
pinfo = priv->prkey_rsa_objs[idx]->data;
|
|
||||||
|
|
||||||
/* now we need to read the certificate so that we can calculate the
|
|
||||||
keygrip */
|
|
||||||
rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &tmpobj);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_info ("certificate for private key %d not found: %s\n",
|
|
||||||
idx, sc_strerror (rc));
|
|
||||||
/* note, that we return the ID anyway */
|
|
||||||
rc = gpg_error (GPG_ERR_MISSING_CERT);
|
|
||||||
goto return_keyid;
|
|
||||||
}
|
|
||||||
certinfo = tmpobj->data;
|
|
||||||
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_info ("failed to read certificate for private key %d: %s\n",
|
|
||||||
idx, sc_strerror (rc));
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = ksba_cert_new (&cert);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
sc_pkcs15_free_certificate (certder);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
rc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
|
|
||||||
sc_pkcs15_free_certificate (certder);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("failed to parse the certificate for private key %d: %s\n",
|
|
||||||
idx, gpg_strerror (rc));
|
|
||||||
ksba_cert_release (cert);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if (card_help_get_keygrip (cert, keygrip))
|
|
||||||
{
|
|
||||||
log_error ("failed to calculate the keygrip of private key %d\n", idx);
|
|
||||||
ksba_cert_release (cert);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
ksba_cert_release (cert);
|
|
||||||
|
|
||||||
rc = 0;
|
|
||||||
return_keyid:
|
|
||||||
if (keyid)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
*keyid = p = xtrymalloc (9+pinfo->id.len*2+1);
|
|
||||||
if (!*keyid)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
p = stpcpy (p, "P15-5015.");
|
|
||||||
bin2hex (pinfo->id.value, pinfo->id.len, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See card.c for interface description */
|
|
||||||
static int
|
|
||||||
p15_enum_certs (CARD card, int idx, char **certid, int *type)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
struct p15private_s *priv;
|
|
||||||
struct sc_pkcs15_object *obj;
|
|
||||||
struct sc_pkcs15_cert_info *cinfo;
|
|
||||||
int nobjs;
|
|
||||||
|
|
||||||
rc = init_private_data (card);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
priv = card->p15priv;
|
|
||||||
nobjs = priv->n_cert_objs;
|
|
||||||
rc = 0;
|
|
||||||
if (idx >= nobjs)
|
|
||||||
return -1;
|
|
||||||
obj = priv->cert_objs[idx];
|
|
||||||
cinfo = obj->data;
|
|
||||||
|
|
||||||
if (certid)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
*certid = p = xtrymalloc (9+cinfo->id.len*2+1);
|
|
||||||
if (!*certid)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
p = stpcpy (p, "P15-5015.");
|
|
||||||
bin2hex (cinfo->id.value, cinfo->id.len, p);
|
|
||||||
}
|
|
||||||
if (type)
|
|
||||||
{
|
|
||||||
if (!obj->df)
|
|
||||||
*type = 0; /* unknown */
|
|
||||||
else if (obj->df->type == SC_PKCS15_CDF)
|
|
||||||
*type = 100;
|
|
||||||
else if (obj->df->type == SC_PKCS15_CDF_TRUSTED)
|
|
||||||
*type = 101;
|
|
||||||
else if (obj->df->type == SC_PKCS15_CDF_USEFUL)
|
|
||||||
*type = 102;
|
|
||||||
else
|
|
||||||
*type = 0; /* error -> unknown */
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
idstr_to_id (const char *idstr, struct sc_pkcs15_id *id)
|
|
||||||
{
|
|
||||||
const char *s;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
/* For now we only support the standard DF */
|
|
||||||
if (strncmp (idstr, "P15-5015.", 9) )
|
|
||||||
return gpg_error (GPG_ERR_INV_ID);
|
|
||||||
for (s=idstr+9, n=0; hexdigitp (s); s++, n++)
|
|
||||||
;
|
|
||||||
if (*s || (n&1))
|
|
||||||
return gpg_error (GPG_ERR_INV_ID); /*invalid or odd number of digits*/
|
|
||||||
n /= 2;
|
|
||||||
if (!n || n > SC_PKCS15_MAX_ID_SIZE)
|
|
||||||
return gpg_error (GPG_ERR_INV_ID); /* empty or too large */
|
|
||||||
for (s=idstr+9, n=0; *s; s += 2, n++)
|
|
||||||
id->value[n] = xtoi_2 (s);
|
|
||||||
id->len = n;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* See card.c for interface description */
|
|
||||||
static int
|
|
||||||
p15_read_cert (CARD card, const char *certidstr,
|
|
||||||
unsigned char **cert, size_t *ncert)
|
|
||||||
{
|
|
||||||
struct sc_pkcs15_object *tmpobj;
|
|
||||||
struct sc_pkcs15_id certid;
|
|
||||||
struct sc_pkcs15_cert_info *certinfo;
|
|
||||||
struct sc_pkcs15_cert *certder;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!card || !certidstr || !cert || !ncert)
|
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
|
||||||
if (!card->p15card)
|
|
||||||
return gpg_error (GPG_ERR_NO_PKCS15_APP);
|
|
||||||
|
|
||||||
rc = idstr_to_id (certidstr, &certid);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &tmpobj);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_info ("certificate '%s' not found: %s\n",
|
|
||||||
certidstr, sc_strerror (rc));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
certinfo = tmpobj->data;
|
|
||||||
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_info ("failed to read certificate '%s': %s\n",
|
|
||||||
certidstr, sc_strerror (rc));
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
*cert = xtrymalloc (certder->data_len);
|
|
||||||
if (!*cert)
|
|
||||||
{
|
|
||||||
gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
sc_pkcs15_free_certificate (certder);
|
|
||||||
return tmperr;
|
|
||||||
}
|
|
||||||
memcpy (*cert, certder->data, certder->data_len);
|
|
||||||
*ncert = certder->data_len;
|
|
||||||
sc_pkcs15_free_certificate (certder);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
p15_prepare_key (CARD card, const char *keyidstr,
|
|
||||||
int (pincb)(void*, const char *, char **),
|
|
||||||
void *pincb_arg, struct sc_pkcs15_object **r_keyobj)
|
|
||||||
{
|
|
||||||
struct sc_pkcs15_id keyid;
|
|
||||||
struct sc_pkcs15_pin_info *pin;
|
|
||||||
struct sc_pkcs15_object *keyobj, *pinobj;
|
|
||||||
char *pinvalue;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = idstr_to_id (keyidstr, &keyid);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = sc_pkcs15_find_prkey_by_id (card->p15card, &keyid, &keyobj);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
log_error ("private key not found: %s\n", sc_strerror(rc));
|
|
||||||
return gpg_error (GPG_ERR_NO_SECKEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = sc_pkcs15_find_pin_by_auth_id (card->p15card,
|
|
||||||
&keyobj->auth_id, &pinobj);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("failed to find PIN by auth ID: %s\n", sc_strerror (rc));
|
|
||||||
return gpg_error (GPG_ERR_BAD_PIN_METHOD);
|
|
||||||
}
|
|
||||||
pin = pinobj->data;
|
|
||||||
|
|
||||||
/* Fixme: pack this into a verification loop */
|
|
||||||
/* Fixme: we might want to pass pin->min_length and
|
|
||||||
pin->stored_length */
|
|
||||||
rc = pincb (pincb_arg, pinobj->label, &pinvalue);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = sc_pkcs15_verify_pin (card->p15card, pin,
|
|
||||||
pinvalue, strlen (pinvalue));
|
|
||||||
xfree (pinvalue);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_info ("PIN verification failed: %s\n", sc_strerror (rc));
|
|
||||||
return gpg_error (GPG_ERR_BAD_PIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fixme: check wheter we need to release KEYOBJ in case of an error */
|
|
||||||
*r_keyobj = keyobj;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* See card.c for interface description */
|
|
||||||
static int
|
|
||||||
p15_sign (CARD card, 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 )
|
|
||||||
{
|
|
||||||
unsigned int cryptflags;
|
|
||||||
struct sc_pkcs15_object *keyobj;
|
|
||||||
int rc;
|
|
||||||
unsigned char *outbuf = NULL;
|
|
||||||
size_t outbuflen;
|
|
||||||
|
|
||||||
if (hashalgo != GCRY_MD_SHA1)
|
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
|
||||||
|
|
||||||
rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
cryptflags = SC_ALGORITHM_RSA_PAD_PKCS1;
|
|
||||||
|
|
||||||
outbuflen = 1024;
|
|
||||||
outbuf = xtrymalloc (outbuflen);
|
|
||||||
if (!outbuf)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
|
|
||||||
rc = sc_pkcs15_compute_signature (card->p15card, keyobj,
|
|
||||||
cryptflags,
|
|
||||||
indata, indatalen,
|
|
||||||
outbuf, outbuflen );
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
log_error ("failed to create signature: %s\n", sc_strerror (rc));
|
|
||||||
rc = gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*outdatalen = rc;
|
|
||||||
*outdata = outbuf;
|
|
||||||
outbuf = NULL;
|
|
||||||
rc = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree (outbuf);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* See card.c for description */
|
|
||||||
static int
|
|
||||||
p15_decipher (CARD card, const char *keyidstr,
|
|
||||||
int (pincb)(void*, const char *, char **),
|
|
||||||
void *pincb_arg,
|
|
||||||
const void *indata, size_t indatalen,
|
|
||||||
unsigned char **outdata, size_t *outdatalen )
|
|
||||||
{
|
|
||||||
struct sc_pkcs15_object *keyobj;
|
|
||||||
int rc;
|
|
||||||
unsigned char *outbuf = NULL;
|
|
||||||
size_t outbuflen;
|
|
||||||
|
|
||||||
rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (card && card->scard && card->scard->driver
|
|
||||||
&& !strcasecmp (card->scard->driver->short_name, "tcos"))
|
|
||||||
{
|
|
||||||
/* very ugly hack to force the use of a local key. We need this
|
|
||||||
until we have fixed the initialization code for TCOS cards */
|
|
||||||
struct sc_pkcs15_prkey_info *prkey = keyobj->data;
|
|
||||||
if ( !(prkey->key_reference & 0x80))
|
|
||||||
{
|
|
||||||
prkey->key_reference |= 0x80;
|
|
||||||
log_debug ("using TCOS hack to force the use of local keys\n");
|
|
||||||
}
|
|
||||||
if (*keyidstr && keyidstr[strlen(keyidstr)-1] == '6')
|
|
||||||
{
|
|
||||||
prkey->key_reference |= 1;
|
|
||||||
log_debug ("warning: using even more TCOS hacks\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outbuflen = indatalen < 256? 256 : indatalen;
|
|
||||||
outbuf = xtrymalloc (outbuflen);
|
|
||||||
if (!outbuf)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
|
|
||||||
rc = sc_pkcs15_decipher (card->p15card, keyobj,
|
|
||||||
0,
|
|
||||||
indata, indatalen,
|
|
||||||
outbuf, outbuflen);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
log_error ("failed to decipher the data: %s\n", sc_strerror (rc));
|
|
||||||
rc = gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*outdatalen = rc;
|
|
||||||
*outdata = outbuf;
|
|
||||||
outbuf = NULL;
|
|
||||||
rc = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree (outbuf);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Bind our operations to the card */
|
|
||||||
void
|
|
||||||
card_p15_bind (CARD card)
|
|
||||||
{
|
|
||||||
card->fnc.enum_keypairs = p15_enum_keypairs;
|
|
||||||
card->fnc.enum_certs = p15_enum_certs;
|
|
||||||
card->fnc.read_cert = p15_read_cert;
|
|
||||||
card->fnc.sign = p15_sign;
|
|
||||||
card->fnc.decipher = p15_decipher;
|
|
||||||
}
|
|
||||||
#endif /*HAVE_OPENSC*/
|
|
568
scd/card.c
568
scd/card.c
@ -1,568 +0,0 @@
|
|||||||
/* card.c - SCdaemon card functions
|
|
||||||
* Copyright (C) 2002 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
#include <opensc/pkcs15.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "scdaemon.h"
|
|
||||||
#include <ksba.h>
|
|
||||||
|
|
||||||
#include "card-common.h"
|
|
||||||
|
|
||||||
/* Map the SC error codes to the GNUPG ones */
|
|
||||||
gpg_error_t
|
|
||||||
map_sc_err (int rc)
|
|
||||||
{
|
|
||||||
gpg_err_code_t e;
|
|
||||||
|
|
||||||
switch (rc)
|
|
||||||
{
|
|
||||||
case 0: e = 0; break;
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
case SC_ERROR_NOT_SUPPORTED: e = GPG_ERR_NOT_SUPPORTED; break;
|
|
||||||
case SC_ERROR_PKCS15_APP_NOT_FOUND: e = GPG_ERR_NO_PKCS15_APP; break;
|
|
||||||
case SC_ERROR_OUT_OF_MEMORY: e = GPG_ERR_ENOMEM; break;
|
|
||||||
case SC_ERROR_CARD_NOT_PRESENT: e = GPG_ERR_CARD_NOT_PRESENT; break;
|
|
||||||
case SC_ERROR_CARD_REMOVED: e = GPG_ERR_CARD_REMOVED; break;
|
|
||||||
case SC_ERROR_INVALID_CARD: e = GPG_ERR_INV_CARD; break;
|
|
||||||
#endif
|
|
||||||
default: e = GPG_ERR_CARD; break;
|
|
||||||
}
|
|
||||||
/* It does not make much sense to further distingusih the error
|
|
||||||
source between OpenSC and SCD. Thus we use SCD as source
|
|
||||||
here. */
|
|
||||||
return gpg_err_make (GPG_ERR_SOURCE_SCD, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the keygrip from CERT, return 0 on success */
|
|
||||||
int
|
|
||||||
card_help_get_keygrip (ksba_cert_t cert, unsigned char *array)
|
|
||||||
{
|
|
||||||
gcry_sexp_t s_pkey;
|
|
||||||
int rc;
|
|
||||||
ksba_sexp_t p;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
p = ksba_cert_get_public_key (cert);
|
|
||||||
if (!p)
|
|
||||||
return -1; /* oops */
|
|
||||||
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
|
|
||||||
if (!n)
|
|
||||||
return -1; /* libksba did not return a proper S-expression */
|
|
||||||
rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
|
|
||||||
xfree (p);
|
|
||||||
if (rc)
|
|
||||||
return -1; /* can't parse that S-expression */
|
|
||||||
array = gcry_pk_get_keygrip (s_pkey, array);
|
|
||||||
gcry_sexp_release (s_pkey);
|
|
||||||
if (!array)
|
|
||||||
return -1; /* failed to calculate the keygrip */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Create a new context for the card and figures out some basic
|
|
||||||
information of the card. Detects whether a PKCS_15 application is
|
|
||||||
stored.
|
|
||||||
|
|
||||||
Common errors: GPG_ERR_CARD_NOT_PRESENT */
|
|
||||||
int
|
|
||||||
card_open (CARD *rcard)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
CARD card;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (opt.disable_opensc)
|
|
||||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
|
||||||
|
|
||||||
card = xtrycalloc (1, sizeof *card);
|
|
||||||
if (!card)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
card->reader = 0;
|
|
||||||
|
|
||||||
rc = sc_establish_context (&card->ctx, "scdaemon");
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("failed to establish SC context: %s\n", sc_strerror (rc));
|
|
||||||
rc = map_sc_err (rc);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
if (card->reader >= card->ctx->reader_count)
|
|
||||||
{
|
|
||||||
log_error ("no card reader available\n");
|
|
||||||
rc = gpg_error (GPG_ERR_CARD);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
card->ctx->error_file = log_get_stream ();
|
|
||||||
card->ctx->debug = opt.debug_sc;
|
|
||||||
card->ctx->debug_file = log_get_stream ();
|
|
||||||
|
|
||||||
if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1)
|
|
||||||
{
|
|
||||||
rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = sc_connect_card (card->ctx->reader[card->reader], 0, &card->scard);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("failed to connect card in reader %d: %s\n",
|
|
||||||
card->reader, sc_strerror (rc));
|
|
||||||
rc = map_sc_err (rc);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
if (opt.verbose)
|
|
||||||
log_info ("connected to card in reader %d using driver '%s'\n",
|
|
||||||
card->reader, card->scard->driver->name);
|
|
||||||
|
|
||||||
rc = sc_lock (card->scard);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("can't lock card in reader %d: %s\n",
|
|
||||||
card->reader, sc_strerror (rc));
|
|
||||||
rc = map_sc_err (rc);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
leave:
|
|
||||||
if (rc)
|
|
||||||
card_close (card);
|
|
||||||
else
|
|
||||||
*rcard = card;
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
#else
|
|
||||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Close a card and release all resources */
|
|
||||||
void
|
|
||||||
card_close (CARD card)
|
|
||||||
{
|
|
||||||
if (card)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
if (card->p15card)
|
|
||||||
{
|
|
||||||
sc_pkcs15_unbind (card->p15card);
|
|
||||||
card->p15card = NULL;
|
|
||||||
}
|
|
||||||
if (card->p15priv)
|
|
||||||
p15_release_private_data (card);
|
|
||||||
if (card->scard)
|
|
||||||
{
|
|
||||||
sc_unlock (card->scard);
|
|
||||||
sc_disconnect_card (card->scard, 0);
|
|
||||||
card->scard = NULL;
|
|
||||||
}
|
|
||||||
if (card->ctx)
|
|
||||||
{
|
|
||||||
sc_release_context (card->ctx);
|
|
||||||
card->ctx = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
xfree (card);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Locate a simple TLV encoded data object in BUFFER of LENGTH and
|
|
||||||
return a pointer to value as well as its length in NBYTES. Return
|
|
||||||
NULL if it was not found. Note, that the function does not check
|
|
||||||
whether the value fits into the provided buffer. */
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
static const char *
|
|
||||||
find_simple_tlv (const unsigned char *buffer, size_t length,
|
|
||||||
int tag, size_t *nbytes)
|
|
||||||
{
|
|
||||||
const char *s = buffer;
|
|
||||||
size_t n = length;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
buffer = s;
|
|
||||||
if (n < 2)
|
|
||||||
return NULL; /* buffer too short for tag and length. */
|
|
||||||
len = s[1];
|
|
||||||
s += 2; n -= 2;
|
|
||||||
if (len == 255)
|
|
||||||
{
|
|
||||||
if (n < 2)
|
|
||||||
return NULL; /* we expected 2 more bytes with the length. */
|
|
||||||
len = (s[0] << 8) | s[1];
|
|
||||||
s += 2; n -= 2;
|
|
||||||
}
|
|
||||||
if (*buffer == tag)
|
|
||||||
{
|
|
||||||
*nbytes = len;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
if (len > n)
|
|
||||||
return NULL; /* buffer too short to skip to the next tag. */
|
|
||||||
s += len; n -= len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /*HAVE_OPENSC*/
|
|
||||||
|
|
||||||
/* Find the ICC Serial Number within the provided BUFFER of LENGTH
|
|
||||||
(which should contain the GDO file) and return it as a hex encoded
|
|
||||||
string and allocated string in SERIAL. Return an error code when
|
|
||||||
the ICCSN was not found. */
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
static int
|
|
||||||
find_iccsn (const unsigned char *buffer, size_t length, char **serial)
|
|
||||||
{
|
|
||||||
size_t n;
|
|
||||||
const unsigned char *s;
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
s = find_simple_tlv (buffer, length, 0x5A, &n);
|
|
||||||
if (!s)
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
length -= s - buffer;
|
|
||||||
if (n > length)
|
|
||||||
{
|
|
||||||
/* Oops, it does not fit into the buffer. This is an invalid
|
|
||||||
encoding (or the buffer is too short. However, I have some
|
|
||||||
test cards with such an invalid encoding and therefore I use
|
|
||||||
this ugly workaround to return something I can further
|
|
||||||
experiment with. */
|
|
||||||
if (n == 0x0D && length+1 == n)
|
|
||||||
{
|
|
||||||
log_debug ("enabling BMI testcard workaround\n");
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return gpg_error (GPG_ERR_CARD); /* Bad encoding; does
|
|
||||||
not fit into buffer. */
|
|
||||||
}
|
|
||||||
if (!n)
|
|
||||||
return gpg_error (GPG_ERR_CARD); /* Well, that is too short. */
|
|
||||||
|
|
||||||
*serial = p = xtrymalloc (2*n+1);
|
|
||||||
if (!*serial)
|
|
||||||
return gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
for (; n; n--, p += 2, s++)
|
|
||||||
sprintf (p, "%02X", *s);
|
|
||||||
*p = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /*HAVE_OPENSC*/
|
|
||||||
|
|
||||||
/* Retrieve the serial number and the time of the last update of the
|
|
||||||
card. The serial number is returned as a malloced string (hex
|
|
||||||
encoded) in SERIAL and the time of update is returned in STAMP.
|
|
||||||
If no update time is available the returned value is 0. The serial
|
|
||||||
is mandatory for a PKCS_15 application and an error will be
|
|
||||||
returned if this value is not availbale. For non-PKCS-15 cards a
|
|
||||||
serial number is constructed by other means. Caller must free
|
|
||||||
SERIAL unless the function returns an error. */
|
|
||||||
int
|
|
||||||
card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
int rc;
|
|
||||||
struct sc_path path;
|
|
||||||
struct sc_file *file;
|
|
||||||
unsigned char buf[256];
|
|
||||||
int buflen;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!card || !serial || !stamp)
|
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
|
||||||
|
|
||||||
*serial = NULL;
|
|
||||||
*stamp = 0; /* not available */
|
|
||||||
|
|
||||||
#ifdef HAVE_OPENSC
|
|
||||||
if (!card->fnc.initialized)
|
|
||||||
{
|
|
||||||
card->fnc.initialized = 1;
|
|
||||||
/* The first use of this card tries to figure out the type of the card
|
|
||||||
and sets up the function pointers. */
|
|
||||||
rc = sc_pkcs15_bind (card->scard, &card->p15card);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
if (rc != SC_ERROR_PKCS15_APP_NOT_FOUND)
|
|
||||||
log_error ("binding of existing PKCS-15 failed in reader %d: %s\n",
|
|
||||||
card->reader, sc_strerror (rc));
|
|
||||||
card->p15card = NULL;
|
|
||||||
rc = 0;
|
|
||||||
}
|
|
||||||
if (card->p15card)
|
|
||||||
card_p15_bind (card);
|
|
||||||
card->fnc.initialized = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* We should lookup the iso 7812-1 and 8583-3 - argh ISO
|
|
||||||
practice is suppressing innovation - IETF rules! So we
|
|
||||||
always get the serialnumber from the 2F02 GDO file. */
|
|
||||||
/* FIXME: in case we can't parse the 2F02 EF and we have a P15 card,
|
|
||||||
we should get the serial number from the respective P15 file */
|
|
||||||
sc_format_path ("3F002F02", &path);
|
|
||||||
rc = sc_select_file (card->scard, &path, &file);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("sc_select_file failed: %s\n", sc_strerror (rc));
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
if (file->type != SC_FILE_TYPE_WORKING_EF
|
|
||||||
|| file->ef_structure != SC_FILE_EF_TRANSPARENT)
|
|
||||||
{
|
|
||||||
log_error ("wrong type or structure of GDO file\n");
|
|
||||||
sc_file_free (file);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file->size || file->size >= DIM(buf) )
|
|
||||||
{ /* FIXME: Use a real parser */
|
|
||||||
log_error ("unsupported size of GDO file (%d)\n", file->size);
|
|
||||||
sc_file_free (file);
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
buflen = file->size;
|
|
||||||
|
|
||||||
rc = sc_read_binary (card->scard, 0, buf, buflen, 0);
|
|
||||||
sc_file_free (file);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
log_error ("error reading GDO file: %s\n", sc_strerror (rc));
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
if (rc != buflen)
|
|
||||||
{
|
|
||||||
log_error ("short read on GDO file\n");
|
|
||||||
return gpg_error (GPG_ERR_CARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = find_iccsn (buf, buflen, serial);
|
|
||||||
if (gpg_err_code (rc) == GPG_ERR_CARD)
|
|
||||||
log_error ("invalid structure of GDO file\n");
|
|
||||||
if (!rc && card->p15card && !strcmp (*serial, "D27600000000000000000000"))
|
|
||||||
{ /* This is a German card with a silly serial number. Try to get
|
|
||||||
the serial number from the EF(TokenInfo). We indicate such a
|
|
||||||
serial number by the using the prefix: "FF0100". */
|
|
||||||
const char *efser = card->p15card->serial_number;
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
if (!efser)
|
|
||||||
efser = "";
|
|
||||||
|
|
||||||
xfree (*serial);
|
|
||||||
*serial = NULL;
|
|
||||||
p = xtrymalloc (strlen (efser) + 7);
|
|
||||||
if (!p)
|
|
||||||
rc = gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strcpy (p, "FF0100");
|
|
||||||
strcpy (p+6, efser);
|
|
||||||
*serial = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!rc && **serial == 'F' && (*serial)[1] == 'F')
|
|
||||||
{ /* The serial number starts with our special prefix. This
|
|
||||||
requires that we put our default prefix "FF0000" in front. */
|
|
||||||
char *p = xtrymalloc (strlen (*serial) + 7);
|
|
||||||
if (!p)
|
|
||||||
{
|
|
||||||
xfree (*serial);
|
|
||||||
*serial = NULL;
|
|
||||||
rc = gpg_error (gpg_err_code_from_errno (errno));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strcpy (p, "FF0000");
|
|
||||||
strcpy (p+6, *serial);
|
|
||||||
xfree (*serial);
|
|
||||||
*serial = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
#else
|
|
||||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Enumerate all keypairs on the card and return the Keygrip as well
|
|
||||||
as the internal identification of the key. KEYGRIP must be a
|
|
||||||
caller provided buffer with a size of 20 bytes which will receive
|
|
||||||
the KEYGRIP of the keypair. If KEYID is not NULL, it returns the
|
|
||||||
ID field of the key in allocated memory; this is a string without
|
|
||||||
spaces. The function returns -1 when all keys have been
|
|
||||||
enumerated. Note that the error GPG_ERR_MISSING_CERTIFICATE may be
|
|
||||||
returned if there is just the private key but no public key (ie.e a
|
|
||||||
certificate) available. Applications might want to continue
|
|
||||||
enumerating after this error.*/
|
|
||||||
int
|
|
||||||
card_enum_keypairs (CARD card, int idx,
|
|
||||||
unsigned char *keygrip,
|
|
||||||
char **keyid)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (keyid)
|
|
||||||
*keyid = NULL;
|
|
||||||
|
|
||||||
if (!card || !keygrip)
|
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
|
||||||
if (idx < 0)
|
|
||||||
return gpg_error (GPG_ERR_INV_INDEX);
|
|
||||||
if (!card->fnc.initialized)
|
|
||||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
|
||||||
if (!card->fnc.enum_keypairs)
|
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
|
||||||
rc = card->fnc.enum_keypairs (card, idx, keygrip, keyid);
|
|
||||||
if (opt.verbose)
|
|
||||||
log_info ("card operation enum_keypairs result: %s\n",
|
|
||||||
gpg_strerror (rc));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Enumerate all trusted certificates available on the card, return
|
|
||||||
their ID in CERT and the type in CERTTYPE. Types of certificates
|
|
||||||
are:
|
|
||||||
0 := Unknown
|
|
||||||
100 := Regular X.509 cert
|
|
||||||
101 := Trusted X.509 cert
|
|
||||||
102 := Useful X.509 cert
|
|
||||||
110 := Root CA cert (DINSIG)
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
card_enum_certs (CARD card, int idx, char **certid, int *certtype)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (certid)
|
|
||||||
*certid = NULL;
|
|
||||||
|
|
||||||
if (!card)
|
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
|
||||||
if (idx < 0)
|
|
||||||
return gpg_error (GPG_ERR_INV_INDEX);
|
|
||||||
if (!card->fnc.initialized)
|
|
||||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
|
||||||
if (!card->fnc.enum_certs)
|
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
|
||||||
rc = card->fnc.enum_certs (card, idx, certid, certtype);
|
|
||||||
if (opt.verbose)
|
|
||||||
log_info ("card operation enum_certs result: %s\n",
|
|
||||||
gpg_strerror (rc));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Read the certificate identified by CERTIDSTR which is the
|
|
||||||
hexadecimal encoded ID of the certificate, prefixed with the string
|
|
||||||
"3F005015.". The certificate is return in DER encoded form in CERT
|
|
||||||
and NCERT. */
|
|
||||||
int
|
|
||||||
card_read_cert (CARD card, const char *certidstr,
|
|
||||||
unsigned char **cert, size_t *ncert)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!card || !certidstr || !cert || !ncert)
|
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
|
||||||
if (!card->fnc.initialized)
|
|
||||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
|
||||||
if (!card->fnc.read_cert)
|
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
|
||||||
rc = card->fnc.read_cert (card, certidstr, cert, ncert);
|
|
||||||
if (opt.verbose)
|
|
||||||
log_info ("card operation read_cert result: %s\n", gpg_strerror (rc));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* 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; it
|
|
||||||
should return the PIN in an allocated buffer and put it into PIN. */
|
|
||||||
int
|
|
||||||
card_sign (CARD card, 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 )
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
|
||||||
if (!card->fnc.initialized)
|
|
||||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
|
||||||
if (!card->fnc.sign)
|
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
|
||||||
rc = card->fnc.sign (card, keyidstr, hashalgo,
|
|
||||||
pincb, pincb_arg,
|
|
||||||
indata, indatalen,
|
|
||||||
outdata, outdatalen);
|
|
||||||
if (opt.verbose)
|
|
||||||
log_info ("card operation sign result: %s\n", gpg_strerror (rc));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* 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; it
|
|
||||||
should return the PIN in an allocated buffer and put it into PIN. */
|
|
||||||
int
|
|
||||||
card_decipher (CARD card, const char *keyidstr,
|
|
||||||
int (pincb)(void*, const char *, char **),
|
|
||||||
void *pincb_arg,
|
|
||||||
const void *indata, size_t indatalen,
|
|
||||||
unsigned char **outdata, size_t *outdatalen )
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
|
||||||
return gpg_error (GPG_ERR_INV_VALUE);
|
|
||||||
if (!card->fnc.initialized)
|
|
||||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
|
||||||
if (!card->fnc.decipher)
|
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
|
||||||
rc = card->fnc.decipher (card, keyidstr,
|
|
||||||
pincb, pincb_arg,
|
|
||||||
indata, indatalen,
|
|
||||||
outdata, outdatalen);
|
|
||||||
if (opt.verbose)
|
|
||||||
log_info ("card operation decipher result: %s\n", gpg_strerror (rc));
|
|
||||||
return rc;
|
|
||||||
}
|
|
@ -1,911 +0,0 @@
|
|||||||
/* pcsc-wrapper.c - Wrapper for accessing the PC/SC service
|
|
||||||
* Copyright (C) 2003, 2004 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This wrapper is required to handle problems with the libpscslite
|
|
||||||
library. That library assumes that pthreads are used and fails
|
|
||||||
badly if one tries to use it with a process using Pth. Note that
|
|
||||||
the wrapper is not required if nPth is used.
|
|
||||||
|
|
||||||
The operation model is pretty simple: It reads requests from stdin
|
|
||||||
and returns the answer on stdout. There is no direct mapping to the
|
|
||||||
pcsc interface but to a higher level one which resembles the code
|
|
||||||
used in scdaemon (apdu.c) when not using Pth or while running under
|
|
||||||
Windows.
|
|
||||||
|
|
||||||
The interface is binary consisting of a command tag and the length
|
|
||||||
of the parameter list. The calling process needs to pass the
|
|
||||||
version number of the interface on the command line to make sure
|
|
||||||
that both agree on the same interface. For each port a separate
|
|
||||||
instance of this process needs to be started.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define PGM "pcsc-wrapper"
|
|
||||||
|
|
||||||
/* Allow for a standalone build. */
|
|
||||||
#ifdef VERSION
|
|
||||||
#define MYVERSION_LINE PGM " ("GNUPG_NAME") " VERSION
|
|
||||||
#define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
|
|
||||||
#else
|
|
||||||
#define MYVERSION_LINE PGM
|
|
||||||
#define BUGREPORT_LINE ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEFAULT_PCSC_DRIVER "libpcsclite.so"
|
|
||||||
|
|
||||||
|
|
||||||
static int verbose;
|
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__)
|
|
||||||
typedef unsigned int pcsc_dword_t;
|
|
||||||
#else
|
|
||||||
typedef unsigned long pcsc_dword_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* PC/SC constants and function pointer. */
|
|
||||||
#define PCSC_SCOPE_USER 0
|
|
||||||
#define PCSC_SCOPE_TERMINAL 1
|
|
||||||
#define PCSC_SCOPE_SYSTEM 2
|
|
||||||
#define PCSC_SCOPE_GLOBAL 3
|
|
||||||
|
|
||||||
#define PCSC_PROTOCOL_T0 1
|
|
||||||
#define PCSC_PROTOCOL_T1 2
|
|
||||||
#define PCSC_PROTOCOL_RAW 4
|
|
||||||
|
|
||||||
#define PCSC_SHARE_EXCLUSIVE 1
|
|
||||||
#define PCSC_SHARE_SHARED 2
|
|
||||||
#define PCSC_SHARE_DIRECT 3
|
|
||||||
|
|
||||||
#define PCSC_LEAVE_CARD 0
|
|
||||||
#define PCSC_RESET_CARD 1
|
|
||||||
#define PCSC_UNPOWER_CARD 2
|
|
||||||
#define PCSC_EJECT_CARD 3
|
|
||||||
|
|
||||||
#define PCSC_UNKNOWN 0x0001
|
|
||||||
#define PCSC_ABSENT 0x0002 /* Card is absent. */
|
|
||||||
#define PCSC_PRESENT 0x0004 /* Card is present. */
|
|
||||||
#define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
|
|
||||||
#define PCSC_POWERED 0x0010 /* Card is powered. */
|
|
||||||
#define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
|
|
||||||
#define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
|
|
||||||
|
|
||||||
#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
|
|
||||||
#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
|
|
||||||
#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
|
|
||||||
#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
|
|
||||||
#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
|
|
||||||
#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
|
|
||||||
#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
|
|
||||||
#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
|
|
||||||
#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
|
|
||||||
#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
|
|
||||||
#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
|
|
||||||
|
|
||||||
struct pcsc_io_request_s {
|
|
||||||
unsigned long protocol;
|
|
||||||
unsigned long pci_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct pcsc_io_request_s *pcsc_io_request_t;
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#pragma pack(1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct pcsc_readerstate_s
|
|
||||||
{
|
|
||||||
const char *reader;
|
|
||||||
void *user_data;
|
|
||||||
pcsc_dword_t current_state;
|
|
||||||
pcsc_dword_t event_state;
|
|
||||||
pcsc_dword_t atrlen;
|
|
||||||
unsigned char atr[33];
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#pragma pack()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
|
|
||||||
|
|
||||||
|
|
||||||
static int driver_is_open; /* True if the PC/SC driver has been
|
|
||||||
initialzied and is ready for
|
|
||||||
operations. The following variables
|
|
||||||
are then valid. */
|
|
||||||
static long pcsc_context; /* The current PC/CS context. */
|
|
||||||
static char *current_rdrname;
|
|
||||||
static long pcsc_card;
|
|
||||||
static pcsc_dword_t pcsc_protocol;
|
|
||||||
static unsigned char current_atr[33];
|
|
||||||
static size_t current_atrlen;
|
|
||||||
|
|
||||||
long (* pcsc_establish_context) (pcsc_dword_t scope,
|
|
||||||
const void *reserved1,
|
|
||||||
const void *reserved2,
|
|
||||||
long *r_context);
|
|
||||||
long (* pcsc_release_context) (long context);
|
|
||||||
long (* pcsc_list_readers) (long context,
|
|
||||||
const char *groups,
|
|
||||||
char *readers, pcsc_dword_t *readerslen);
|
|
||||||
long (* pcsc_get_status_change) (long context,
|
|
||||||
pcsc_dword_t timeout,
|
|
||||||
pcsc_readerstate_t readerstates,
|
|
||||||
pcsc_dword_t nreaderstates);
|
|
||||||
long (* pcsc_connect) (long context,
|
|
||||||
const char *reader,
|
|
||||||
pcsc_dword_t share_mode,
|
|
||||||
pcsc_dword_t preferred_protocols,
|
|
||||||
long *r_card,
|
|
||||||
pcsc_dword_t *r_active_protocol);
|
|
||||||
long (* pcsc_reconnect) (long card,
|
|
||||||
pcsc_dword_t share_mode,
|
|
||||||
pcsc_dword_t preferred_protocols,
|
|
||||||
pcsc_dword_t initialization,
|
|
||||||
pcsc_dword_t *r_active_protocol);
|
|
||||||
long (* pcsc_disconnect) (long card,
|
|
||||||
pcsc_dword_t disposition);
|
|
||||||
long (* pcsc_status) (long card,
|
|
||||||
char *reader, pcsc_dword_t *readerlen,
|
|
||||||
pcsc_dword_t *r_state,
|
|
||||||
pcsc_dword_t *r_protocol,
|
|
||||||
unsigned char *atr, pcsc_dword_t *atrlen);
|
|
||||||
long (* pcsc_begin_transaction) (long card);
|
|
||||||
long (* pcsc_end_transaction) (long card,
|
|
||||||
pcsc_dword_t disposition);
|
|
||||||
long (* pcsc_transmit) (long card,
|
|
||||||
const pcsc_io_request_t send_pci,
|
|
||||||
const unsigned char *send_buffer,
|
|
||||||
pcsc_dword_t send_len,
|
|
||||||
pcsc_io_request_t recv_pci,
|
|
||||||
unsigned char *recv_buffer,
|
|
||||||
pcsc_dword_t *recv_len);
|
|
||||||
long (* pcsc_set_timeout) (long context,
|
|
||||||
pcsc_dword_t timeout);
|
|
||||||
long (* pcsc_control) (long card,
|
|
||||||
pcsc_dword_t control_code,
|
|
||||||
const void *send_buffer,
|
|
||||||
pcsc_dword_t send_len,
|
|
||||||
void *recv_buffer,
|
|
||||||
pcsc_dword_t recv_len,
|
|
||||||
pcsc_dword_t *bytes_returned);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
bad_request (const char *type)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": bad '%s' request\n", type);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
request_failed (int err)
|
|
||||||
{
|
|
||||||
if (!err)
|
|
||||||
err = -1;
|
|
||||||
|
|
||||||
putchar (0x81); /* Simple error/success response. */
|
|
||||||
|
|
||||||
putchar (0);
|
|
||||||
putchar (0);
|
|
||||||
putchar (0);
|
|
||||||
putchar (4);
|
|
||||||
|
|
||||||
putchar ((err >> 24) & 0xff);
|
|
||||||
putchar ((err >> 16) & 0xff);
|
|
||||||
putchar ((err >> 8) & 0xff);
|
|
||||||
putchar ((err ) & 0xff);
|
|
||||||
|
|
||||||
fflush (stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
request_succeeded (const void *buffer, size_t buflen)
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
putchar (0x81); /* Simple error/success response. */
|
|
||||||
|
|
||||||
len = 4 + buflen;
|
|
||||||
putchar ((len >> 24) & 0xff);
|
|
||||||
putchar ((len >> 16) & 0xff);
|
|
||||||
putchar ((len >> 8) & 0xff);
|
|
||||||
putchar ((len ) & 0xff);
|
|
||||||
|
|
||||||
/* Error code. */
|
|
||||||
putchar (0);
|
|
||||||
putchar (0);
|
|
||||||
putchar (0);
|
|
||||||
putchar (0);
|
|
||||||
|
|
||||||
/* Optional reponse string. */
|
|
||||||
if (buffer)
|
|
||||||
fwrite (buffer, buflen, 1, stdout);
|
|
||||||
|
|
||||||
fflush (stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static unsigned long
|
|
||||||
read_32 (FILE *fp)
|
|
||||||
{
|
|
||||||
int c1, c2, c3, c4;
|
|
||||||
|
|
||||||
c1 = getc (fp);
|
|
||||||
c2 = getc (fp);
|
|
||||||
c3 = getc (fp);
|
|
||||||
c4 = getc (fp);
|
|
||||||
if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": premature EOF while parsing request\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
pcsc_error_string (long err)
|
|
||||||
{
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
if (!err)
|
|
||||||
return "okay";
|
|
||||||
if ((err & 0x80100000) != 0x80100000)
|
|
||||||
return "invalid PC/SC error code";
|
|
||||||
err &= 0xffff;
|
|
||||||
switch (err)
|
|
||||||
{
|
|
||||||
case 0x0002: s = "cancelled"; break;
|
|
||||||
case 0x000e: s = "can't dispose"; break;
|
|
||||||
case 0x0008: s = "insufficient buffer"; break;
|
|
||||||
case 0x0015: s = "invalid ATR"; break;
|
|
||||||
case 0x0003: s = "invalid handle"; break;
|
|
||||||
case 0x0004: s = "invalid parameter"; break;
|
|
||||||
case 0x0005: s = "invalid target"; break;
|
|
||||||
case 0x0011: s = "invalid value"; break;
|
|
||||||
case 0x0006: s = "no memory"; break;
|
|
||||||
case 0x0013: s = "comm error"; break;
|
|
||||||
case 0x0001: s = "internal error"; break;
|
|
||||||
case 0x0014: s = "unknown error"; break;
|
|
||||||
case 0x0007: s = "waited too long"; break;
|
|
||||||
case 0x0009: s = "unknown reader"; break;
|
|
||||||
case 0x000a: s = "timeout"; break;
|
|
||||||
case 0x000b: s = "sharing violation"; break;
|
|
||||||
case 0x000c: s = "no smartcard"; break;
|
|
||||||
case 0x000d: s = "unknown card"; break;
|
|
||||||
case 0x000f: s = "proto mismatch"; break;
|
|
||||||
case 0x0010: s = "not ready"; break;
|
|
||||||
case 0x0012: s = "system cancelled"; break;
|
|
||||||
case 0x0016: s = "not transacted"; break;
|
|
||||||
case 0x0017: s = "reader unavailable"; break;
|
|
||||||
case 0x0065: s = "unsupported card"; break;
|
|
||||||
case 0x0066: s = "unresponsive card"; break;
|
|
||||||
case 0x0067: s = "unpowered card"; break;
|
|
||||||
case 0x0068: s = "reset card"; break;
|
|
||||||
case 0x0069: s = "removed card"; break;
|
|
||||||
case 0x006a: s = "inserted card"; break;
|
|
||||||
case 0x001f: s = "unsupported feature"; break;
|
|
||||||
case 0x0019: s = "PCI too small"; break;
|
|
||||||
case 0x001a: s = "reader unsupported"; break;
|
|
||||||
case 0x001b: s = "duplicate reader"; break;
|
|
||||||
case 0x001c: s = "card unsupported"; break;
|
|
||||||
case 0x001d: s = "no service"; break;
|
|
||||||
case 0x001e: s = "service stopped"; break;
|
|
||||||
default: s = "unknown PC/SC error code"; break;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
load_pcsc_driver (const char *libname)
|
|
||||||
{
|
|
||||||
void *handle;
|
|
||||||
|
|
||||||
handle = dlopen (libname, RTLD_LAZY);
|
|
||||||
if (!handle)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": failed to open driver '%s': %s",
|
|
||||||
libname, dlerror ());
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
|
|
||||||
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
|
|
||||||
pcsc_list_readers = dlsym (handle, "SCardListReaders");
|
|
||||||
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
|
|
||||||
pcsc_connect = dlsym (handle, "SCardConnect");
|
|
||||||
pcsc_reconnect = dlsym (handle, "SCardReconnect");
|
|
||||||
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
|
|
||||||
pcsc_status = dlsym (handle, "SCardStatus");
|
|
||||||
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
|
|
||||||
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
|
|
||||||
pcsc_transmit = dlsym (handle, "SCardTransmit");
|
|
||||||
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
|
|
||||||
pcsc_control = dlsym (handle, "SCardControl");
|
|
||||||
|
|
||||||
if (!pcsc_establish_context
|
|
||||||
|| !pcsc_release_context
|
|
||||||
|| !pcsc_list_readers
|
|
||||||
|| !pcsc_get_status_change
|
|
||||||
|| !pcsc_connect
|
|
||||||
|| !pcsc_reconnect
|
|
||||||
|| !pcsc_disconnect
|
|
||||||
|| !pcsc_status
|
|
||||||
|| !pcsc_begin_transaction
|
|
||||||
|| !pcsc_end_transaction
|
|
||||||
|| !pcsc_transmit
|
|
||||||
|| !pcsc_control
|
|
||||||
/* || !pcsc_set_timeout */)
|
|
||||||
{
|
|
||||||
/* Note that set_timeout is currently not used and also not
|
|
||||||
available under Windows. */
|
|
||||||
fprintf (stderr,
|
|
||||||
"apdu_open_reader: invalid PC/SC driver "
|
|
||||||
"(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
|
|
||||||
!!pcsc_establish_context,
|
|
||||||
!!pcsc_release_context,
|
|
||||||
!!pcsc_list_readers,
|
|
||||||
!!pcsc_get_status_change,
|
|
||||||
!!pcsc_connect,
|
|
||||||
!!pcsc_reconnect,
|
|
||||||
!!pcsc_disconnect,
|
|
||||||
!!pcsc_status,
|
|
||||||
!!pcsc_begin_transaction,
|
|
||||||
!!pcsc_end_transaction,
|
|
||||||
!!pcsc_transmit,
|
|
||||||
!!pcsc_set_timeout,
|
|
||||||
!!pcsc_control );
|
|
||||||
dlclose (handle);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle a open request. The argument is expected to be a string
|
|
||||||
with the port identification. ARGBUF is always guaranteed to be
|
|
||||||
terminted by a 0 which is not counted in ARGLEN. We may modifiy
|
|
||||||
ARGBUF. */
|
|
||||||
static void
|
|
||||||
handle_open (unsigned char *argbuf, size_t arglen)
|
|
||||||
{
|
|
||||||
long err;
|
|
||||||
const char * portstr;
|
|
||||||
char *list = NULL;
|
|
||||||
pcsc_dword_t nreader, atrlen;
|
|
||||||
char *p;
|
|
||||||
pcsc_dword_t card_state, card_protocol;
|
|
||||||
unsigned char atr[33];
|
|
||||||
|
|
||||||
/* Make sure there is only the port string */
|
|
||||||
if (arglen != strlen ((char*)argbuf))
|
|
||||||
bad_request ("OPEN");
|
|
||||||
portstr = (char*)argbuf;
|
|
||||||
|
|
||||||
if (driver_is_open)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": PC/SC has already been opened\n");
|
|
||||||
request_failed (-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": pcsc_establish_context failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pcsc_list_readers (pcsc_context, NULL, NULL, &nreader);
|
|
||||||
if (!err)
|
|
||||||
{
|
|
||||||
list = malloc (nreader+1); /* Better add 1 for safety reasons. */
|
|
||||||
if (!list)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": error allocating memory for reader list\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
err = pcsc_list_readers (pcsc_context, NULL, list, &nreader);
|
|
||||||
}
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": pcsc_list_readers failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
pcsc_release_context (pcsc_context);
|
|
||||||
free (list);
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = list;
|
|
||||||
while (nreader)
|
|
||||||
{
|
|
||||||
if (!*p && !p[1])
|
|
||||||
break;
|
|
||||||
fprintf (stderr, PGM": detected reader '%s'\n", p);
|
|
||||||
if (nreader < (strlen (p)+1))
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": invalid response from pcsc_list_readers\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
nreader -= strlen (p)+1;
|
|
||||||
p += strlen (p) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_rdrname = malloc (strlen (portstr && *portstr? portstr:list)+1);
|
|
||||||
if (!current_rdrname)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": error allocating memory for reader name\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
strcpy (current_rdrname, portstr && *portstr? portstr:list);
|
|
||||||
free (list);
|
|
||||||
|
|
||||||
err = pcsc_connect (pcsc_context,
|
|
||||||
current_rdrname,
|
|
||||||
PCSC_SHARE_EXCLUSIVE,
|
|
||||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
|
||||||
&pcsc_card,
|
|
||||||
&pcsc_protocol);
|
|
||||||
if (err == 0x8010000c) /* No smartcard. */
|
|
||||||
{
|
|
||||||
pcsc_card = 0;
|
|
||||||
}
|
|
||||||
else if (err)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
pcsc_release_context (pcsc_context);
|
|
||||||
free (current_rdrname);
|
|
||||||
current_rdrname = NULL;
|
|
||||||
pcsc_card = 0;
|
|
||||||
pcsc_protocol = 0;
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_atrlen = 0;
|
|
||||||
if (!err)
|
|
||||||
{
|
|
||||||
char reader[250];
|
|
||||||
pcsc_dword_t readerlen;
|
|
||||||
|
|
||||||
atrlen = 33;
|
|
||||||
readerlen = sizeof reader -1;
|
|
||||||
err = pcsc_status (pcsc_card,
|
|
||||||
reader, &readerlen,
|
|
||||||
&card_state, &card_protocol,
|
|
||||||
atr, &atrlen);
|
|
||||||
if (err)
|
|
||||||
fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": ATR returned by pcsc_status"
|
|
||||||
" is too large\n");
|
|
||||||
exit (4);
|
|
||||||
}
|
|
||||||
memcpy (current_atr, atr, atrlen);
|
|
||||||
current_atrlen = atrlen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
driver_is_open = 1;
|
|
||||||
request_succeeded (current_atr, current_atrlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle a close request. We expect no arguments. We may modifiy
|
|
||||||
ARGBUF. */
|
|
||||||
static void
|
|
||||||
handle_close (unsigned char *argbuf, size_t arglen)
|
|
||||||
{
|
|
||||||
(void)argbuf;
|
|
||||||
(void)arglen;
|
|
||||||
|
|
||||||
if (!driver_is_open)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
|
||||||
request_failed (-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
free (current_rdrname);
|
|
||||||
current_rdrname = NULL;
|
|
||||||
pcsc_release_context (pcsc_context);
|
|
||||||
pcsc_card = 0;
|
|
||||||
pcsc_protocol = 0;
|
|
||||||
|
|
||||||
request_succeeded (NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle a status request. We expect no arguments. We may modifiy
|
|
||||||
ARGBUF. */
|
|
||||||
static void
|
|
||||||
handle_status (unsigned char *argbuf, size_t arglen)
|
|
||||||
{
|
|
||||||
long err;
|
|
||||||
struct pcsc_readerstate_s rdrstates[1];
|
|
||||||
int status;
|
|
||||||
unsigned char buf[20];
|
|
||||||
|
|
||||||
(void)argbuf;
|
|
||||||
(void)arglen;
|
|
||||||
|
|
||||||
if (!driver_is_open)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
|
||||||
request_failed (-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset (rdrstates, 0, sizeof *rdrstates);
|
|
||||||
rdrstates[0].reader = current_rdrname;
|
|
||||||
rdrstates[0].current_state = PCSC_STATE_UNAWARE;
|
|
||||||
err = pcsc_get_status_change (pcsc_context,
|
|
||||||
0,
|
|
||||||
rdrstates, 1);
|
|
||||||
if (err == 0x8010000a) /* Timeout. */
|
|
||||||
err = 0;
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": pcsc_get_status_change failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = 0;
|
|
||||||
if ( !(rdrstates[0].event_state & PCSC_STATE_UNKNOWN) )
|
|
||||||
{
|
|
||||||
if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
|
|
||||||
{
|
|
||||||
status |= 2;
|
|
||||||
if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
|
|
||||||
status |= 4;
|
|
||||||
}
|
|
||||||
/* We indicate a useful card if it is not in use by another
|
|
||||||
application. This is because we only use exclusive access
|
|
||||||
mode. */
|
|
||||||
if ( (status & 6) == 6
|
|
||||||
&& !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
|
|
||||||
status |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First word is identical to the one used by apdu.c. */
|
|
||||||
buf[0] = 0;
|
|
||||||
buf[1] = 0;
|
|
||||||
buf[2] = 0;
|
|
||||||
buf[3] = status;
|
|
||||||
/* The second word is the native PCSC state. */
|
|
||||||
buf[4] = (rdrstates[0].event_state >> 24);
|
|
||||||
buf[5] = (rdrstates[0].event_state >> 16);
|
|
||||||
buf[6] = (rdrstates[0].event_state >> 8);
|
|
||||||
buf[7] = (rdrstates[0].event_state >> 0);
|
|
||||||
/* The third word is the protocol. */
|
|
||||||
buf[8] = (pcsc_protocol >> 24);
|
|
||||||
buf[9] = (pcsc_protocol >> 16);
|
|
||||||
buf[10] = (pcsc_protocol >> 8);
|
|
||||||
buf[11] = (pcsc_protocol);
|
|
||||||
|
|
||||||
request_succeeded (buf, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle a reset request. We expect no arguments. We may modifiy
|
|
||||||
ARGBUF. */
|
|
||||||
static void
|
|
||||||
handle_reset (unsigned char *argbuf, size_t arglen)
|
|
||||||
{
|
|
||||||
long err;
|
|
||||||
char reader[250];
|
|
||||||
pcsc_dword_t nreader, atrlen;
|
|
||||||
pcsc_dword_t card_state, card_protocol;
|
|
||||||
|
|
||||||
(void)argbuf;
|
|
||||||
(void)arglen;
|
|
||||||
|
|
||||||
if (!driver_is_open)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
|
||||||
request_failed (-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcsc_card)
|
|
||||||
{
|
|
||||||
err = pcsc_disconnect (pcsc_card, PCSC_LEAVE_CARD);
|
|
||||||
if (err == 0x80100003) /* Invalid handle. (already disconnected) */
|
|
||||||
err = 0;
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": pcsc_disconnect failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pcsc_card = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pcsc_connect (pcsc_context,
|
|
||||||
current_rdrname,
|
|
||||||
PCSC_SHARE_EXCLUSIVE,
|
|
||||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
|
||||||
&pcsc_card,
|
|
||||||
&pcsc_protocol);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
pcsc_card = 0;
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
atrlen = 33;
|
|
||||||
nreader = sizeof reader - 1;
|
|
||||||
err = pcsc_status (pcsc_card,
|
|
||||||
reader, &nreader,
|
|
||||||
&card_state, &card_protocol,
|
|
||||||
current_atr, &atrlen);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
current_atrlen = 0;
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
request_succeeded (current_atr, current_atrlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle a transmit request. The argument is expected to be a buffer
|
|
||||||
with the APDU. We may modifiy ARGBUF. */
|
|
||||||
static void
|
|
||||||
handle_transmit (unsigned char *argbuf, size_t arglen)
|
|
||||||
{
|
|
||||||
long err;
|
|
||||||
struct pcsc_io_request_s send_pci;
|
|
||||||
pcsc_dword_t recv_len;
|
|
||||||
unsigned char buffer[4096];
|
|
||||||
|
|
||||||
/* The apdu should at least be one byte. */
|
|
||||||
if (!arglen)
|
|
||||||
bad_request ("TRANSMIT");
|
|
||||||
|
|
||||||
if (!driver_is_open)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
|
||||||
request_failed (-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((pcsc_protocol & PCSC_PROTOCOL_T1))
|
|
||||||
send_pci.protocol = PCSC_PROTOCOL_T1;
|
|
||||||
else
|
|
||||||
send_pci.protocol = PCSC_PROTOCOL_T0;
|
|
||||||
send_pci.pci_len = sizeof send_pci;
|
|
||||||
recv_len = sizeof (buffer);
|
|
||||||
err = pcsc_transmit (pcsc_card, &send_pci, argbuf, arglen,
|
|
||||||
NULL, buffer, &recv_len);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
fprintf (stderr, PGM": pcsc_transmit failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
request_succeeded (buffer, recv_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle a control request. The argument is expected to be a buffer
|
|
||||||
which contains CONTROL_CODE (4-byte) and INPUT_BYTES.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
handle_control (unsigned char *argbuf, size_t arglen)
|
|
||||||
{
|
|
||||||
long err;
|
|
||||||
pcsc_dword_t ioctl_code;
|
|
||||||
pcsc_dword_t recv_len = 1024;
|
|
||||||
unsigned char buffer[1024];
|
|
||||||
|
|
||||||
if (arglen < 4)
|
|
||||||
bad_request ("CONTROL");
|
|
||||||
|
|
||||||
ioctl_code = (argbuf[0] << 24) | (argbuf[1] << 16) | (argbuf[2] << 8) | argbuf[3];
|
|
||||||
argbuf += 4;
|
|
||||||
arglen -= 4;
|
|
||||||
|
|
||||||
recv_len = sizeof (buffer);
|
|
||||||
err = pcsc_control (pcsc_card, ioctl_code, argbuf, arglen,
|
|
||||||
buffer, recv_len, &recv_len);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
fprintf (stderr, PGM": pcsc_control failed: %s (0x%lx)\n",
|
|
||||||
pcsc_error_string (err), err);
|
|
||||||
request_failed (err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
request_succeeded (buffer, recv_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_version (int with_help)
|
|
||||||
{
|
|
||||||
fputs (MYVERSION_LINE "\n"
|
|
||||||
"Copyright (C) 2004 Free Software Foundation, Inc.\n"
|
|
||||||
"This program comes with ABSOLUTELY NO WARRANTY.\n"
|
|
||||||
"This is free software, and you are welcome to redistribute it\n"
|
|
||||||
"under certain conditions. See the file COPYING for details.\n",
|
|
||||||
stdout);
|
|
||||||
|
|
||||||
if (with_help)
|
|
||||||
fputs ("\n"
|
|
||||||
"Usage: " PGM " [OPTIONS] API-NUMBER [LIBNAME]\n"
|
|
||||||
"Helper to connect scdaemon to the PC/SC library\n"
|
|
||||||
"\n"
|
|
||||||
" --verbose enable extra informational output\n"
|
|
||||||
" --version print version of the program and exit\n"
|
|
||||||
" --help display this help and exit\n"
|
|
||||||
BUGREPORT_LINE, stdout );
|
|
||||||
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int last_argc = -1;
|
|
||||||
int api_number = 0;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
if (argc)
|
|
||||||
{
|
|
||||||
argc--; argv++;
|
|
||||||
}
|
|
||||||
while (argc && last_argc != argc )
|
|
||||||
{
|
|
||||||
last_argc = argc;
|
|
||||||
if (!strcmp (*argv, "--"))
|
|
||||||
{
|
|
||||||
argc--; argv++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (!strcmp (*argv, "--version"))
|
|
||||||
print_version (0);
|
|
||||||
else if (!strcmp (*argv, "--help"))
|
|
||||||
print_version (1);
|
|
||||||
else if (!strcmp (*argv, "--verbose"))
|
|
||||||
{
|
|
||||||
verbose = 1;
|
|
||||||
argc--; argv++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (argc != 1 && argc != 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "usage: " PGM " API-NUMBER [LIBNAME]\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
api_number = atoi (*argv);
|
|
||||||
argv++; argc--;
|
|
||||||
if (api_number != 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": api-number %d is not valid\n", api_number);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
load_pcsc_driver (argc? *argv : DEFAULT_PCSC_DRIVER);
|
|
||||||
|
|
||||||
while ((c = getc (stdin)) != EOF)
|
|
||||||
{
|
|
||||||
size_t arglen;
|
|
||||||
unsigned char argbuffer[2048];
|
|
||||||
|
|
||||||
arglen = read_32 (stdin);
|
|
||||||
if (arglen >= sizeof argbuffer - 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": request too long\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
if (arglen && fread (argbuffer, arglen, 1, stdin) != 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, PGM ": error reading request: %s\n",
|
|
||||||
strerror (errno));
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
argbuffer[arglen] = 0;
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
handle_open (argbuffer, arglen);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
handle_close (argbuffer, arglen);
|
|
||||||
exit (0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
handle_transmit (argbuffer, arglen);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
handle_status (argbuffer, arglen);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
handle_reset (argbuffer, arglen);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
handle_control (argbuffer, arglen);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Local Variables:
|
|
||||||
compile-command: "gcc -Wall -g -o pcsc-wrapper pcsc-wrapper.c -ldl"
|
|
||||||
End:
|
|
||||||
*/
|
|
@ -1,715 +0,0 @@
|
|||||||
/* sc-copykeys.c - A tool to store keys on a smartcard.
|
|
||||||
* Copyright (C) 2003 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "scdaemon.h"
|
|
||||||
#include <gcrypt.h>
|
|
||||||
|
|
||||||
#include "../common/ttyio.h"
|
|
||||||
#include "../common/simple-pwquery.h"
|
|
||||||
#include "iso7816.h"
|
|
||||||
#include "apdu.h" /* for open_reader */
|
|
||||||
#include "atr.h"
|
|
||||||
#include "app-common.h"
|
|
||||||
|
|
||||||
#define _(a) (a)
|
|
||||||
|
|
||||||
|
|
||||||
enum cmd_and_opt_values
|
|
||||||
{ oVerbose = 'v',
|
|
||||||
oReaderPort = 500,
|
|
||||||
octapiDriver,
|
|
||||||
oDebug,
|
|
||||||
oDebugAll,
|
|
||||||
|
|
||||||
aTest };
|
|
||||||
|
|
||||||
|
|
||||||
static ARGPARSE_OPTS opts[] = {
|
|
||||||
|
|
||||||
{ 301, NULL, 0, "@Options:\n " },
|
|
||||||
|
|
||||||
{ oVerbose, "verbose", 0, "verbose" },
|
|
||||||
{ oReaderPort, "reader-port", 2, "|N|connect to reader at port N"},
|
|
||||||
{ octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"},
|
|
||||||
{ oDebug, "debug" ,4|16, "set debugging flags"},
|
|
||||||
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void copykeys (APP app, const char *fname);
|
|
||||||
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
my_strusage (int level)
|
|
||||||
{
|
|
||||||
const char *p;
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
case 11: p = "sc-copykeys (GnuPG)";
|
|
||||||
break;
|
|
||||||
case 13: p = VERSION; break;
|
|
||||||
case 17: p = PRINTABLE_OS_NAME; break;
|
|
||||||
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
case 40: p = _("Usage: sc-copykeys [options] (-h for help)\n");
|
|
||||||
break;
|
|
||||||
case 41: p = _("Syntax: sc-copykeys [options] "
|
|
||||||
"file-with-key\n"
|
|
||||||
"Copy keys to a smartcards\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: p = NULL;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv )
|
|
||||||
{
|
|
||||||
ARGPARSE_ARGS pargs;
|
|
||||||
int slot, rc;
|
|
||||||
const char *reader_port = NULL;
|
|
||||||
struct app_ctx_s appbuf;
|
|
||||||
|
|
||||||
memset (&appbuf, 0, sizeof appbuf);
|
|
||||||
|
|
||||||
set_strusage (my_strusage);
|
|
||||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
|
||||||
log_set_prefix ("sc-copykeys", 1);
|
|
||||||
|
|
||||||
/* check that the libraries are suitable. Do it here because
|
|
||||||
the option parsing may need services of the library */
|
|
||||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
|
||||||
{
|
|
||||||
log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
|
|
||||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_libgcrypt_logging ();
|
|
||||||
gcry_control (GCRYCTL_DISABLE_SECMEM, 0); /* FIXME - we want to use it */
|
|
||||||
/* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
|
|
||||||
|
|
||||||
pargs.argc = &argc;
|
|
||||||
pargs.argv = &argv;
|
|
||||||
pargs.flags= 1; /* do not remove the args */
|
|
||||||
while (arg_parse (&pargs, opts) )
|
|
||||||
{
|
|
||||||
switch (pargs.r_opt)
|
|
||||||
{
|
|
||||||
case oVerbose: opt.verbose++; break;
|
|
||||||
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
|
|
||||||
case oDebugAll: opt.debug = ~0; break;
|
|
||||||
case oReaderPort: reader_port = pargs.r.ret_str; break;
|
|
||||||
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
|
|
||||||
default : pargs.err = 2; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (log_get_errorcount(0))
|
|
||||||
exit(2);
|
|
||||||
|
|
||||||
if (argc != 1)
|
|
||||||
usage (1);
|
|
||||||
|
|
||||||
slot = apdu_open_reader (reader_port);
|
|
||||||
if (slot == -1)
|
|
||||||
exit (1);
|
|
||||||
if (apdu_connect (slot))
|
|
||||||
exit (1);
|
|
||||||
|
|
||||||
/* FIXME: Use select_application. */
|
|
||||||
appbuf.slot = slot;
|
|
||||||
rc = app_select_openpgp (&appbuf);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
appbuf.initialized = 1;
|
|
||||||
log_info ("openpgp application selected\n");
|
|
||||||
|
|
||||||
copykeys (&appbuf, *argv);
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
send_status_info (CTRL ctrl, const char *keyword, ...)
|
|
||||||
{
|
|
||||||
/* DUMMY */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static char *
|
|
||||||
read_file (const char *fname, size_t *r_length)
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
struct stat st;
|
|
||||||
char *buf;
|
|
||||||
size_t buflen;
|
|
||||||
|
|
||||||
fp = fname? fopen (fname, "rb") : stdin;
|
|
||||||
if (!fp)
|
|
||||||
{
|
|
||||||
log_error ("can't open '%s': %s\n",
|
|
||||||
fname? fname: "[stdin]", strerror (errno));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fstat (fileno(fp), &st))
|
|
||||||
{
|
|
||||||
log_error ("can't stat '%s': %s\n",
|
|
||||||
fname? fname: "[stdin]", strerror (errno));
|
|
||||||
if (fname)
|
|
||||||
fclose (fp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
buflen = st.st_size;
|
|
||||||
buf = xmalloc (buflen+1);
|
|
||||||
if (fread (buf, buflen, 1, fp) != 1)
|
|
||||||
{
|
|
||||||
log_error ("error reading '%s': %s\n",
|
|
||||||
fname? fname: "[stdin]", strerror (errno));
|
|
||||||
if (fname)
|
|
||||||
fclose (fp);
|
|
||||||
xfree (buf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (fname)
|
|
||||||
fclose (fp);
|
|
||||||
|
|
||||||
*r_length = buflen;
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static gcry_sexp_t
|
|
||||||
read_key (const char *fname)
|
|
||||||
{
|
|
||||||
char *buf;
|
|
||||||
size_t buflen;
|
|
||||||
gcry_sexp_t private;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
buf = read_file (fname, &buflen);
|
|
||||||
if (!buf)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
rc = gcry_sexp_new (&private, buf, buflen, 1);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("gcry_sexp_new failed: %s\n", gpg_strerror (rc));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
xfree (buf);
|
|
||||||
|
|
||||||
return private;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static gcry_mpi_t *
|
|
||||||
sexp_to_kparms (gcry_sexp_t sexp, unsigned long *created)
|
|
||||||
{
|
|
||||||
gcry_sexp_t list, l2;
|
|
||||||
const char *name;
|
|
||||||
const char *s;
|
|
||||||
size_t n;
|
|
||||||
int i, idx;
|
|
||||||
const char *elems;
|
|
||||||
gcry_mpi_t *array;
|
|
||||||
|
|
||||||
*created = 0;
|
|
||||||
list = gcry_sexp_find_token (sexp, "private-key", 0 );
|
|
||||||
if(!list)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* quick hack to get the creation time. */
|
|
||||||
l2 = gcry_sexp_find_token (list, "created", 0);
|
|
||||||
if (l2 && (name = gcry_sexp_nth_data (l2, 1, &n)))
|
|
||||||
{
|
|
||||||
char *tmp = xmalloc (n+1);
|
|
||||||
memcpy (tmp, name, n);
|
|
||||||
tmp[n] = 0;
|
|
||||||
*created = strtoul (tmp, NULL, 10);
|
|
||||||
xfree (tmp);
|
|
||||||
}
|
|
||||||
gcry_sexp_release (l2);
|
|
||||||
l2 = gcry_sexp_cadr (list);
|
|
||||||
gcry_sexp_release (list);
|
|
||||||
list = l2;
|
|
||||||
name = gcry_sexp_nth_data (list, 0, &n);
|
|
||||||
if(!name || n != 3 || memcmp (name, "rsa", 3))
|
|
||||||
{
|
|
||||||
gcry_sexp_release (list);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parameter names used with RSA. */
|
|
||||||
elems = "nedpqu";
|
|
||||||
array = xcalloc (strlen(elems) + 1, sizeof *array);
|
|
||||||
for (idx=0, s=elems; *s; s++, idx++ )
|
|
||||||
{
|
|
||||||
l2 = gcry_sexp_find_token (list, s, 1);
|
|
||||||
if (!l2)
|
|
||||||
{
|
|
||||||
for (i=0; i<idx; i++)
|
|
||||||
gcry_mpi_release (array[i]);
|
|
||||||
xfree (array);
|
|
||||||
gcry_sexp_release (list);
|
|
||||||
return NULL; /* required parameter not found */
|
|
||||||
}
|
|
||||||
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
|
|
||||||
gcry_sexp_release (l2);
|
|
||||||
if (!array[idx])
|
|
||||||
{
|
|
||||||
for (i=0; i<idx; i++)
|
|
||||||
gcry_mpi_release (array[i]);
|
|
||||||
xfree (array);
|
|
||||||
gcry_sexp_release (list);
|
|
||||||
return NULL; /* required parameter is invalid */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gcry_sexp_release (list);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
|
|
||||||
static int
|
|
||||||
fpr_is_zero (const char *fpr)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i=0; i < 20 && !fpr[i]; i++)
|
|
||||||
;
|
|
||||||
return (i == 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
show_sha1_fpr (const unsigned char *fpr)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (fpr)
|
|
||||||
{
|
|
||||||
for (i=0; i < 20 ; i+=2, fpr += 2 )
|
|
||||||
{
|
|
||||||
if (i == 10 )
|
|
||||||
tty_printf (" ");
|
|
||||||
tty_printf (" %02X%02X", *fpr, fpr[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
tty_printf (" [none]");
|
|
||||||
tty_printf ("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Query the card, show a list of already stored keys and ask the user
|
|
||||||
where to store the key. Returns the key number or 0 for cancel
|
|
||||||
operation. */
|
|
||||||
static int
|
|
||||||
query_card (APP app)
|
|
||||||
{
|
|
||||||
int keyno = 0;
|
|
||||||
char *serialno, *disp_name, *pubkey_url;
|
|
||||||
unsigned char *fpr1, *fpr2, *fpr3;
|
|
||||||
|
|
||||||
|
|
||||||
if (app_openpgp_cardinfo (app,
|
|
||||||
&serialno,
|
|
||||||
&disp_name,
|
|
||||||
&pubkey_url,
|
|
||||||
&fpr1, &fpr2, &fpr3))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
char *answer;
|
|
||||||
|
|
||||||
tty_printf ("\n");
|
|
||||||
|
|
||||||
tty_printf ("Serial number ....: %s\n",
|
|
||||||
serialno? serialno : "[none]");
|
|
||||||
tty_printf ("Name of cardholder: %s\n",
|
|
||||||
disp_name && *disp_name? disp_name : "[not set]");
|
|
||||||
tty_printf ("URL of public key : %s\n",
|
|
||||||
pubkey_url && *pubkey_url? pubkey_url : "[not set]");
|
|
||||||
tty_printf ("Signature key ....:");
|
|
||||||
show_sha1_fpr (fpr1);
|
|
||||||
tty_printf ("Encryption key....:");
|
|
||||||
show_sha1_fpr (fpr2);
|
|
||||||
tty_printf ("Authentication key:");
|
|
||||||
show_sha1_fpr (fpr3);
|
|
||||||
|
|
||||||
tty_printf ("\n"
|
|
||||||
"1 - store as signature key and reset usage counter\n"
|
|
||||||
"2 - store as encryption key\n"
|
|
||||||
"3 - store as authentication key\n"
|
|
||||||
"Q - quit\n"
|
|
||||||
"\n");
|
|
||||||
|
|
||||||
answer = tty_get("Your selection? ");
|
|
||||||
tty_kill_prompt();
|
|
||||||
if (strlen (answer) != 1)
|
|
||||||
;
|
|
||||||
else if ( *answer == '1' )
|
|
||||||
{
|
|
||||||
if ( (fpr1 && !fpr_is_zero (fpr1)) )
|
|
||||||
{
|
|
||||||
tty_printf ("\n");
|
|
||||||
log_error ("WARNING: signature key does already exists!\n");
|
|
||||||
tty_printf ("\n");
|
|
||||||
if ( tty_get_answer_is_yes ("Replace existing key? ") )
|
|
||||||
{
|
|
||||||
keyno = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
keyno = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( *answer == '2' )
|
|
||||||
{
|
|
||||||
if ( (fpr2 && !fpr_is_zero (fpr2)) )
|
|
||||||
{
|
|
||||||
tty_printf ("\n");
|
|
||||||
log_error ("WARNING: encryption key does already exists!\n");
|
|
||||||
tty_printf ("\n");
|
|
||||||
if ( tty_get_answer_is_yes ("Replace existing key? ") )
|
|
||||||
{
|
|
||||||
keyno = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
keyno = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( *answer == '3' )
|
|
||||||
{
|
|
||||||
if ( (fpr3 && !fpr_is_zero (fpr3)) )
|
|
||||||
{
|
|
||||||
tty_printf ("\n");
|
|
||||||
log_error ("WARNING: authentication key does already exists!\n");
|
|
||||||
tty_printf ("\n");
|
|
||||||
if ( tty_get_answer_is_yes ("Replace existing key? ") )
|
|
||||||
{
|
|
||||||
keyno = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
keyno = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( *answer == 'q' || *answer == 'Q')
|
|
||||||
{
|
|
||||||
keyno = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree (serialno);
|
|
||||||
xfree (disp_name);
|
|
||||||
xfree (pubkey_url);
|
|
||||||
xfree (fpr1);
|
|
||||||
xfree (fpr2);
|
|
||||||
xfree (fpr3);
|
|
||||||
|
|
||||||
return keyno;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Callback function to ask for a PIN. */
|
|
||||||
static gpg_error_t
|
|
||||||
pincb (void *arg, const char *prompt, char **pinvalue)
|
|
||||||
{
|
|
||||||
char *pin = xstrdup ("12345678");
|
|
||||||
|
|
||||||
/* pin = simple_pwquery (NULL, NULL, prompt, */
|
|
||||||
/* "We need the admin's PIN to store the key on the card", */
|
|
||||||
/* 0, NULL); */
|
|
||||||
/* if (!pin) */
|
|
||||||
/* return gpg_error (GPG_ERR_CANCELED); */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*pinvalue = pin;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* This function expects a file (or NULL for stdin) with the secret
|
|
||||||
and public key parameters. This file should consist of an
|
|
||||||
S-expression as used by gpg-agent. Only the unprotected format is
|
|
||||||
supported. Example:
|
|
||||||
|
|
||||||
(private-key
|
|
||||||
(rsa
|
|
||||||
(n #00e0ce9..[some bytes not shown]..51#)
|
|
||||||
(e #010001#)
|
|
||||||
(d #046129F..[some bytes not shown]..81#)
|
|
||||||
(p #00e861b..[some bytes not shown]..f1#)
|
|
||||||
(q #00f7a7c..[some bytes not shown]..61#)
|
|
||||||
(u #304559a..[some bytes not shown]..9b#))
|
|
||||||
(uri http://foo.bar x-foo:whatever_you_want))
|
|
||||||
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
copykeys (APP app, const char *fname)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
gcry_sexp_t private;
|
|
||||||
gcry_mpi_t *mpis, rsa_n, rsa_e, rsa_p, rsa_q;
|
|
||||||
unsigned int nbits;
|
|
||||||
size_t n;
|
|
||||||
unsigned char *template, *tp;
|
|
||||||
unsigned char m[128], e[4];
|
|
||||||
size_t mlen, elen;
|
|
||||||
unsigned long creation_date;
|
|
||||||
time_t created_at;
|
|
||||||
int keyno;
|
|
||||||
|
|
||||||
if (!strcmp (fname, "-"))
|
|
||||||
fname = NULL;
|
|
||||||
|
|
||||||
private = read_key (fname);
|
|
||||||
if (!private)
|
|
||||||
exit (1);
|
|
||||||
|
|
||||||
mpis = sexp_to_kparms (private, &creation_date);
|
|
||||||
if (!creation_date)
|
|
||||||
{
|
|
||||||
log_info ("no creation date found - assuming current date\n");
|
|
||||||
created_at = time (NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
created_at = creation_date;
|
|
||||||
gcry_sexp_release (private);
|
|
||||||
if (!mpis)
|
|
||||||
{
|
|
||||||
log_error ("invalid structure of key file or not RSA\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
/* MPIS is now an array with the key parameters as defined by OpenPGP. */
|
|
||||||
rsa_n = mpis[0];
|
|
||||||
rsa_e = mpis[1];
|
|
||||||
gcry_mpi_release (mpis[2]);
|
|
||||||
rsa_p = mpis[3];
|
|
||||||
rsa_q = mpis[4];
|
|
||||||
gcry_mpi_release (mpis[5]);
|
|
||||||
xfree (mpis);
|
|
||||||
|
|
||||||
nbits = gcry_mpi_get_nbits (rsa_e);
|
|
||||||
if (nbits < 2 || nbits > 32)
|
|
||||||
{
|
|
||||||
log_error ("public exponent too large (more than 32 bits)\n");
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
nbits = gcry_mpi_get_nbits (rsa_p);
|
|
||||||
if (nbits != 512)
|
|
||||||
{
|
|
||||||
log_error ("length of first RSA prime is not 512\n");
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
nbits = gcry_mpi_get_nbits (rsa_q);
|
|
||||||
if (nbits != 512)
|
|
||||||
{
|
|
||||||
log_error ("length of second RSA prime is not 512\n");
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
nbits = gcry_mpi_get_nbits (rsa_n);
|
|
||||||
if (nbits != 1024)
|
|
||||||
{
|
|
||||||
log_error ("length of RSA modulus is not 1024\n");
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyno = query_card (app);
|
|
||||||
if (!keyno)
|
|
||||||
goto failure;
|
|
||||||
|
|
||||||
/* Build the private key template as described in section 4.3.3.6 of
|
|
||||||
the specs.
|
|
||||||
0xC0 <length> public exponent
|
|
||||||
0xC1 <length> prime p
|
|
||||||
0xC2 <length> prime q */
|
|
||||||
template = tp = xmalloc (1+2 + 1+1+4 + 1+1+64 + 1+1+64);
|
|
||||||
*tp++ = 0xC0;
|
|
||||||
*tp++ = 4;
|
|
||||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 4, &n, rsa_e);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
assert (n <= 4);
|
|
||||||
memcpy (e, tp, n);
|
|
||||||
elen = n;
|
|
||||||
if (n != 4)
|
|
||||||
{
|
|
||||||
memmove (tp+4-n, tp, 4-n);
|
|
||||||
memset (tp, 0, 4-n);
|
|
||||||
}
|
|
||||||
tp += 4;
|
|
||||||
|
|
||||||
*tp++ = 0xC1;
|
|
||||||
*tp++ = 64;
|
|
||||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_p);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
assert (n == 64);
|
|
||||||
tp += 64;
|
|
||||||
|
|
||||||
*tp++ = 0xC2;
|
|
||||||
*tp++ = 64;
|
|
||||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_q);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
assert (n == 64);
|
|
||||||
tp += 64;
|
|
||||||
assert (tp - template == 138);
|
|
||||||
|
|
||||||
/* (we need the modulus to calculate the fingerprint) */
|
|
||||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, m, 128, &n, rsa_n);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
assert (n == 128);
|
|
||||||
mlen = 128;
|
|
||||||
|
|
||||||
|
|
||||||
rc = app_openpgp_storekey (app, keyno,
|
|
||||||
template, tp - template,
|
|
||||||
created_at,
|
|
||||||
m, mlen,
|
|
||||||
e, elen,
|
|
||||||
pincb, NULL);
|
|
||||||
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("error storing key: %s\n", gpg_strerror (rc));
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
log_info ("key successfully stored\n");
|
|
||||||
{
|
|
||||||
unsigned char *mm, *ee;
|
|
||||||
size_t mmlen, eelen;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
rc = app_openpgp_readkey (app, keyno, &mm, &mmlen, &ee, &eelen);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("error reading key back: %s\n", gpg_strerror (rc));
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Strip leading zeroes. */
|
|
||||||
for (i=0; i < mmlen && !mm[i]; i++)
|
|
||||||
;
|
|
||||||
mmlen -= i;
|
|
||||||
memmove (mm, mm+i, mmlen);
|
|
||||||
for (i=0; i < eelen && !ee[i]; i++)
|
|
||||||
;
|
|
||||||
eelen -= i;
|
|
||||||
memmove (ee, ee+i, eelen);
|
|
||||||
|
|
||||||
if (eelen != elen || mmlen != mlen)
|
|
||||||
{
|
|
||||||
log_error ("key parameter length mismatch (n=%u/%u, e=%u/%u)\n",
|
|
||||||
(unsigned int)mlen, (unsigned int)mmlen,
|
|
||||||
(unsigned int)elen, (unsigned int)eelen);
|
|
||||||
xfree (mm);
|
|
||||||
xfree (ee);
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memcmp (m, mm, mlen))
|
|
||||||
{
|
|
||||||
log_error ("key parameter n mismatch\n");
|
|
||||||
log_printhex ("original n: ", m, mlen);
|
|
||||||
log_printhex (" copied n: ", mm, mlen);
|
|
||||||
xfree (mm);
|
|
||||||
xfree (ee);
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
if (memcmp (e, ee, elen))
|
|
||||||
{
|
|
||||||
log_error ("key parameter e mismatch\n");
|
|
||||||
log_printhex ("original e: ", e, elen);
|
|
||||||
log_printhex (" copied e: ", ee, elen);
|
|
||||||
xfree (mm);
|
|
||||||
xfree (ee);
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
xfree (mm);
|
|
||||||
xfree (ee);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
gcry_mpi_release (rsa_e);
|
|
||||||
gcry_mpi_release (rsa_p);
|
|
||||||
gcry_mpi_release (rsa_q);
|
|
||||||
gcry_mpi_release (rsa_n);
|
|
||||||
return;
|
|
||||||
|
|
||||||
failure:
|
|
||||||
gcry_mpi_release (rsa_e);
|
|
||||||
gcry_mpi_release (rsa_p);
|
|
||||||
gcry_mpi_release (rsa_q);
|
|
||||||
gcry_mpi_release (rsa_n);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user