mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
Key generation and signing using the OpenPGP card does rudimentary work.
This commit is contained in:
parent
ed0d33f1d0
commit
f5db59fc21
50 changed files with 1535 additions and 449 deletions
|
@ -1,3 +1,34 @@
|
|||
2003-06-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* app-openpgp.c (find_tlv): Fixed length header parsing.
|
||||
|
||||
* app.c (app_genkey): New.
|
||||
* command.c (cmd_genkey): New.
|
||||
|
||||
2003-06-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (percent_plus_unescape): New.
|
||||
(cmd_setattr): New.
|
||||
|
||||
2003-06-24 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (send_status_info): New.
|
||||
|
||||
* app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg
|
||||
and setup the function pointers in APP on success. Changed callers.
|
||||
* app.c: New.
|
||||
* app-common.h: New.
|
||||
* scdaemon.h (APP): New type to handle applications.
|
||||
(server_control_s): Add an APP context field.
|
||||
|
||||
* command.c (cmd_serialno): Handle applications.
|
||||
(cmd_pksign): Ditto.
|
||||
(cmd_pkdecrypt): Ditto.
|
||||
(reset_notify): Ditto.
|
||||
(cmd_learn): For now return error for application contexts.
|
||||
(cmd_readcert): Ditto.
|
||||
(cmd_readkey): Ditto.
|
||||
|
||||
2003-06-04 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make.
|
||||
|
|
|
@ -33,6 +33,7 @@ scdaemon_SOURCES = \
|
|||
card-p15.c card-dinsig.c \
|
||||
apdu.c apdu.h \
|
||||
iso7816.c iso7816.h \
|
||||
app.c app-common.h \
|
||||
app-openpgp.c
|
||||
|
||||
scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
|
||||
|
|
90
scd/app-common.h
Normal file
90
scd/app-common.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/* app-common.h - Common declarations for all card applications
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef GNUPG_SCD_APP_COMMON_H
|
||||
#define GNUPG_SCD_APP_COMMON_H
|
||||
|
||||
struct app_ctx_s {
|
||||
int initialized; /* The application has been initialied and the
|
||||
function pointers may be used. Note that for
|
||||
unsupported operations the particular
|
||||
function pointer is set to NULL */
|
||||
int slot; /* Used reader. */
|
||||
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
|
||||
size_t serialnolen; /* Length in octets of serialnumber. */
|
||||
int did_chv1;
|
||||
int did_chv2;
|
||||
int did_chv3;
|
||||
struct {
|
||||
int (*learn_status) (APP app, CTRL ctrl);
|
||||
int (*setattr) (APP app, const char *name,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const unsigned char *value, size_t valuelen);
|
||||
int (*sign) (APP app,
|
||||
const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
void **outdata, size_t *outdatalen );
|
||||
int (*decipher) (APP app, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
void **outdata, size_t *outdatalen);
|
||||
int (*genkey) (APP app, CTRL ctrl,
|
||||
const char *keynostr, unsigned int flags,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg);
|
||||
} fnc;
|
||||
|
||||
|
||||
};
|
||||
|
||||
/*-- app.c --*/
|
||||
APP select_application (void);
|
||||
int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
|
||||
int app_write_learn_status (APP app, CTRL ctrl);
|
||||
int app_setattr (APP app, const char *name,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const unsigned char *value, size_t valuelen);
|
||||
int app_sign (APP app, const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
void **outdata, size_t *outdatalen );
|
||||
int app_decipher (APP app, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
void **outdata, size_t *outdatalen );
|
||||
int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg);
|
||||
|
||||
/*-- app-openpgp.c --*/
|
||||
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
|
||||
|
||||
|
||||
#endif /*GNUPG_SCD_APP_COMMON_H*/
|
||||
|
||||
|
||||
|
|
@ -26,8 +26,11 @@
|
|||
#include <dlfcn.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "app-common.h"
|
||||
#include "iso7816.h"
|
||||
|
||||
|
||||
|
||||
static struct {
|
||||
int tag;
|
||||
int constructed;
|
||||
|
@ -80,6 +83,12 @@ find_tlv (const unsigned char *buffer, size_t length,
|
|||
buffer = s;
|
||||
if (n < 2)
|
||||
return NULL; /* buffer definitely too short for tag and length. */
|
||||
if (!*s || *s == 0xff)
|
||||
{ /* Skip optional filler between TLV objects. */
|
||||
s++;
|
||||
n--;
|
||||
continue;
|
||||
}
|
||||
composite = !!(*s & 0x20);
|
||||
if ((*s & 0x1f) == 0x1f)
|
||||
{ /* more tag bytes to follow */
|
||||
|
@ -95,13 +104,26 @@ find_tlv (const unsigned char *buffer, size_t length,
|
|||
this_tag = s[0];
|
||||
len = s[1];
|
||||
s += 2; n -= 2;
|
||||
if (len == 255)
|
||||
{
|
||||
if (len < 0x80)
|
||||
;
|
||||
else if (len == 0x81)
|
||||
{ /* One byte length follows. */
|
||||
if (!n)
|
||||
return NULL; /* we expected 1 more bytes with the length. */
|
||||
len = s[0];
|
||||
s++; n--;
|
||||
}
|
||||
else if (len == 0x82)
|
||||
{ /* Two byte length follows. */
|
||||
if (n < 2)
|
||||
return NULL; /* we expected 2 more bytes with the length. */
|
||||
len = (s[0] << 8) | s[1];
|
||||
s += 2; n -= 2;
|
||||
}
|
||||
else
|
||||
return NULL; /* APDU limit is 65535, thus it does not make
|
||||
sense to assume longer length fields. */
|
||||
|
||||
if (composite && nestlevel < 100)
|
||||
{ /* Dive into this composite DO after checking for too deep
|
||||
nesting. */
|
||||
|
@ -128,7 +150,64 @@ find_tlv (const unsigned char *buffer, size_t length,
|
|||
}
|
||||
|
||||
|
||||
/* Get the DO identified by TAG from the card in SLOT and return a
|
||||
buffer with its content in RESULT and NBYTES. The return value is
|
||||
NULL if not found or a pointer which must be used to release the
|
||||
buffer holding value. */
|
||||
static void *
|
||||
get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
|
||||
{
|
||||
int rc, i;
|
||||
unsigned char *buffer;
|
||||
size_t buflen;
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
|
||||
*result = NULL;
|
||||
*nbytes = 0;
|
||||
for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
|
||||
;
|
||||
|
||||
value = NULL;
|
||||
rc = -1;
|
||||
if (data_objects[i].tag && data_objects[i].get_from)
|
||||
{
|
||||
rc = iso7816_get_data (slot, data_objects[i].get_from,
|
||||
&buffer, &buflen);
|
||||
if (!rc)
|
||||
{
|
||||
value = find_tlv (buffer, buflen, tag, &valuelen, 0);
|
||||
if (!value)
|
||||
; /* not found */
|
||||
else if (valuelen > buflen - (value - buffer))
|
||||
{
|
||||
log_error ("warning: constructed DO too short\n");
|
||||
value = NULL;
|
||||
xfree (buffer); buffer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!value) /* Not in a constructed DO, try simple. */
|
||||
{
|
||||
rc = iso7816_get_data (slot, tag, &buffer, &buflen);
|
||||
if (!rc)
|
||||
{
|
||||
value = buffer;
|
||||
valuelen = buflen;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
{
|
||||
*nbytes = valuelen;
|
||||
*result = value;
|
||||
return buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0 /* not used */
|
||||
static void
|
||||
dump_one_do (int slot, int tag)
|
||||
{
|
||||
|
@ -191,6 +270,7 @@ dump_one_do (int slot, int tag)
|
|||
xfree (buffer);
|
||||
}
|
||||
}
|
||||
#endif /*not used*/
|
||||
|
||||
|
||||
static void
|
||||
|
@ -257,15 +337,15 @@ dump_all_do (int slot)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Note, that FPR must be at least 20 bytes. */
|
||||
static int
|
||||
store_fpr (int slot, int keynumber, u32 timestamp,
|
||||
const unsigned char *m, size_t mlen,
|
||||
const unsigned char *e, size_t elen)
|
||||
const unsigned char *e, size_t elen,
|
||||
unsigned char *fpr)
|
||||
{
|
||||
unsigned int n;
|
||||
unsigned char *buffer, *p;
|
||||
unsigned char fpr[20];
|
||||
int rc;
|
||||
|
||||
n = 6 + 2 + mlen + 2 + elen;
|
||||
|
@ -299,45 +379,189 @@ store_fpr (int slot, int keynumber, u32 timestamp,
|
|||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
send_fpr_if_not_null (CTRL ctrl, const char *keyword,
|
||||
int number, const unsigned char *fpr)
|
||||
{
|
||||
int i;
|
||||
char buf[41];
|
||||
char numbuf[25];
|
||||
|
||||
for (i=0; i < 20 && !fpr[i]; i++)
|
||||
;
|
||||
if (i==20)
|
||||
return; /* All zero. */
|
||||
for (i=0; i< 20; i++)
|
||||
sprintf (buf+2*i, "%02X", fpr[i]);
|
||||
if (number == -1)
|
||||
*numbuf = 0; /* Don't print the key number */
|
||||
else
|
||||
sprintf (numbuf, "%d", number);
|
||||
send_status_info (ctrl, keyword,
|
||||
numbuf, (size_t)strlen(numbuf),
|
||||
buf, (size_t)strlen (buf), NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
send_key_data (CTRL ctrl, const char *name,
|
||||
const unsigned char *a, size_t alen)
|
||||
{
|
||||
char *p, *buf = xmalloc (alen*2+1);
|
||||
|
||||
for (p=buf; alen; a++, alen--, p += 2)
|
||||
sprintf (p, "%02X", *a);
|
||||
|
||||
send_status_info (ctrl, "KEY-DATA",
|
||||
name, (size_t)strlen(name),
|
||||
buf, (size_t)strlen (buf),
|
||||
NULL, 0);
|
||||
xfree (buf);
|
||||
}
|
||||
|
||||
|
||||
/* Generate a new key on the card and store the fingerprint in the
|
||||
corresponding DO. A KEYNUMBER of 0 creates the digital signature
|
||||
key, 1 the encryption key and 2 the authentication key. If the key
|
||||
already exists an error is returned unless FORCE has been set to
|
||||
true. Note, that the function does not return the public key; this
|
||||
has to be done using openpgp_readkey(). */
|
||||
int
|
||||
openpgp_genkey (int slot, int keynumber, int force)
|
||||
|
||||
static int
|
||||
do_learn_status (APP app, CTRL ctrl)
|
||||
{
|
||||
void *relptr;
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
int i;
|
||||
|
||||
relptr = get_one_do (app->slot, 0x005B, &value, &valuelen);
|
||||
if (relptr)
|
||||
{
|
||||
send_status_info (ctrl, "DISP-NAME", value, valuelen, NULL, 0);
|
||||
xfree (relptr);
|
||||
}
|
||||
relptr = get_one_do (app->slot, 0x5FF0, &value, &valuelen);
|
||||
if (relptr)
|
||||
{
|
||||
send_status_info (ctrl, "PUBKEY-URL", value, valuelen, NULL, 0);
|
||||
xfree (relptr);
|
||||
}
|
||||
|
||||
relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen);
|
||||
if (relptr && valuelen >= 60)
|
||||
{
|
||||
for (i=0; i < 3; i++)
|
||||
send_fpr_if_not_null (ctrl, "KEY-FPR", i+1, value+i*20);
|
||||
}
|
||||
xfree (relptr);
|
||||
relptr = get_one_do (app->slot, 0x00C6, &value, &valuelen);
|
||||
if (relptr && valuelen >= 60)
|
||||
{
|
||||
for (i=0; i < 3; i++)
|
||||
send_fpr_if_not_null (ctrl, "CA-FPR", i+1, value+i*20);
|
||||
}
|
||||
xfree (relptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Handle the SETATTR operation. All arguments are already basically
|
||||
checked. */
|
||||
static int
|
||||
do_setattr (APP app, const char *name,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const unsigned char *value, size_t valuelen)
|
||||
{
|
||||
gpg_error_t rc;
|
||||
|
||||
log_debug ("app_openpgp#setattr `%s' value of length %u\n",
|
||||
name, (unsigned int)valuelen); /* fixme: name should be
|
||||
sanitized. */
|
||||
|
||||
if (!app->did_chv3)
|
||||
{
|
||||
char *pinvalue;
|
||||
|
||||
/* rc = pincb (pincb_arg, "Please enter the card's admin PIN (CHV3)", */
|
||||
/* &pinvalue); */
|
||||
pinvalue = xstrdup ("12345678");
|
||||
rc = 0;
|
||||
if (rc)
|
||||
{
|
||||
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
|
||||
xfree (pinvalue);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("verify CHV3 failed\n");
|
||||
rc = gpg_error (GPG_ERR_GENERAL);
|
||||
return rc;
|
||||
}
|
||||
app->did_chv3 = 1;
|
||||
}
|
||||
|
||||
log_debug ("setting `%s' to `%.*s'\n", name, (int)valuelen, value);
|
||||
if (!strcmp (name, "DISP-NAME"))
|
||||
{
|
||||
rc = iso7816_put_data (app->slot, 0x005B, value, valuelen);
|
||||
if (rc)
|
||||
{
|
||||
/* FIXME: If this fails we should *once* try again after
|
||||
doing a verify command, so that in case of a problem with
|
||||
tracking the verify operation we have a fallback. */
|
||||
/* FIXME: change this when iso7816 returns correct error
|
||||
codes. */
|
||||
log_error ("failed to set `Name'\n");
|
||||
rc = gpg_error (GPG_ERR_GENERAL);
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = gpg_error (GPG_ERR_INV_NAME);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Handle the GENKEY command. */
|
||||
static int
|
||||
do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg)
|
||||
{
|
||||
int rc;
|
||||
int i;
|
||||
char numbuf[30];
|
||||
unsigned char fprbuf[20];
|
||||
const unsigned char *fpr;
|
||||
const unsigned char *keydata, *m, *e;
|
||||
unsigned char *buffer;
|
||||
size_t buflen, keydatalen, n, mlen, elen;
|
||||
time_t created_at;
|
||||
|
||||
if (keynumber < 0 || keynumber > 2)
|
||||
return -1; /* invalid value */
|
||||
int keyno = atoi (keynostr);
|
||||
int force = (flags & 1);
|
||||
|
||||
rc = iso7816_get_data (slot, 0x006E, &buffer, &buflen);
|
||||
if (keyno < 1 || keyno > 3)
|
||||
return gpg_error (GPG_ERR_INV_ID);
|
||||
keyno--;
|
||||
|
||||
rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("error reading application data\n");
|
||||
return -1;
|
||||
return gpg_error (GPG_ERR_GENERAL);
|
||||
}
|
||||
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
|
||||
if (!fpr || n != 60)
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_GENERAL);
|
||||
log_error ("error reading fingerprint DO\n");
|
||||
goto leave;
|
||||
}
|
||||
fpr += 20*keynumber;
|
||||
fpr += 20*keyno;
|
||||
for (i=0; i < 20 && !fpr[i]; i++)
|
||||
;
|
||||
if (i!=20 && !force)
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_EEXIST);
|
||||
log_error ("key already exists\n");
|
||||
goto leave;
|
||||
}
|
||||
|
@ -346,8 +570,7 @@ openpgp_genkey (int slot, int keynumber, int force)
|
|||
else
|
||||
log_info ("generating new key\n");
|
||||
|
||||
|
||||
rc = iso7816_verify (slot, 0x83, "12345678", 8);
|
||||
rc = iso7816_verify (app->slot, 0x83, "12345678", 8);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("verify CHV3 failed: rc=%04X\n", rc);
|
||||
|
@ -355,13 +578,14 @@ openpgp_genkey (int slot, int keynumber, int force)
|
|||
}
|
||||
|
||||
xfree (buffer); buffer = NULL;
|
||||
rc = iso7816_generate_keypair (slot,
|
||||
keynumber == 0? "\xB6" :
|
||||
keynumber == 1? "\xB8" : "\xA4",
|
||||
rc = iso7816_generate_keypair (app->slot,
|
||||
keyno == 0? "\xB6" :
|
||||
keyno == 1? "\xB8" : "\xA4",
|
||||
2,
|
||||
&buffer, &buflen);
|
||||
if (rc)
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_CARD);
|
||||
log_error ("generating key failed\n");
|
||||
goto leave;
|
||||
}
|
||||
|
@ -372,7 +596,6 @@ openpgp_genkey (int slot, int keynumber, int force)
|
|||
goto leave;
|
||||
}
|
||||
|
||||
|
||||
m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
|
||||
if (!m)
|
||||
{
|
||||
|
@ -380,6 +603,8 @@ openpgp_genkey (int slot, int keynumber, int force)
|
|||
goto leave;
|
||||
}
|
||||
log_printhex ("RSA n:", m, mlen);
|
||||
send_key_data (ctrl, "n", m, mlen);
|
||||
|
||||
e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
|
||||
if (!e)
|
||||
{
|
||||
|
@ -387,8 +612,18 @@ openpgp_genkey (int slot, int keynumber, int force)
|
|||
goto leave;
|
||||
}
|
||||
log_printhex ("RSA e:", e, elen);
|
||||
send_key_data (ctrl, "e", e, elen);
|
||||
|
||||
created_at = gnupg_get_time ();
|
||||
rc = store_fpr (slot, keynumber, (u32)created_at, m, mlen, e, elen);
|
||||
sprintf (numbuf, "%lu", (unsigned long)created_at);
|
||||
send_status_info (ctrl, "KEY-CREATED-AT",
|
||||
numbuf, (size_t)strlen(numbuf), NULL, 0);
|
||||
|
||||
rc = store_fpr (app->slot, keyno, (u32)created_at,
|
||||
m, mlen, e, elen, fprbuf);
|
||||
if (rc)
|
||||
goto leave;
|
||||
send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
|
||||
|
||||
|
||||
leave:
|
||||
|
@ -397,12 +632,75 @@ openpgp_genkey (int slot, int keynumber, int force)
|
|||
}
|
||||
|
||||
|
||||
/* Comopute a digital signature on INDATA which is expected to be the
|
||||
raw message digest. */
|
||||
static int
|
||||
do_sign (APP app, const char *keyidstr, int hashalgo,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
void **outdata, size_t *outdatalen )
|
||||
{
|
||||
static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
|
||||
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
|
||||
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
|
||||
static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
|
||||
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
|
||||
0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
|
||||
int rc;
|
||||
unsigned char data[35];
|
||||
|
||||
/* We ignore KEYIDSTR, because the OpenPGP application has only one
|
||||
signing key and no way to specify a different one. */
|
||||
|
||||
if (indatalen != 20)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (hashalgo == GCRY_MD_SHA1)
|
||||
memcpy (data, sha1_prefix, 15);
|
||||
else if (hashalgo == GCRY_MD_RMD160)
|
||||
memcpy (data, rmd160_prefix, 15);
|
||||
else
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||
memcpy (data+15, indata, indatalen);
|
||||
|
||||
|
||||
if (!app->did_chv1)
|
||||
{
|
||||
char *pinvalue;
|
||||
|
||||
/* rc = pincb (pincb_arg, "signature PIN", &pinvalue); */
|
||||
pinvalue = xstrdup ("123456");
|
||||
rc = 0;
|
||||
if (rc)
|
||||
{
|
||||
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
|
||||
xfree (pinvalue);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("verify CHV1 failed\n");
|
||||
rc = gpg_error (GPG_ERR_GENERAL);
|
||||
return rc;
|
||||
}
|
||||
app->did_chv1 = 1;
|
||||
}
|
||||
|
||||
rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Select the OpenPGP application on the card in SLOT. This function
|
||||
must be used to before any other OpenPGP application functions. */
|
||||
must be used before any other OpenPGP application functions. */
|
||||
int
|
||||
app_select_openpgp (int slot)
|
||||
app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
|
||||
{
|
||||
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
|
||||
int slot = app->slot;
|
||||
int rc;
|
||||
unsigned char *buffer;
|
||||
size_t buflen;
|
||||
|
@ -416,27 +714,31 @@ app_select_openpgp (int slot)
|
|||
if (rc)
|
||||
goto leave;
|
||||
if (opt.verbose)
|
||||
log_info ("got AID: ");
|
||||
log_printhex ("", buffer, buflen);
|
||||
xfree (buffer);
|
||||
{
|
||||
log_info ("got AID: ");
|
||||
log_printhex ("", buffer, buflen);
|
||||
}
|
||||
|
||||
if (sn)
|
||||
{
|
||||
*sn = buffer;
|
||||
*snlen = buflen;
|
||||
}
|
||||
else
|
||||
xfree (buffer);
|
||||
|
||||
dump_all_do (slot);
|
||||
|
||||
/* rc = iso7816_verify (slot, 0x83, "12345678", 8); */
|
||||
/* if (rc) */
|
||||
/* log_error ("verify CHV3 failed: rc=%04X\n", rc); */
|
||||
|
||||
|
||||
/* rc = iso7816_put_data (slot, 0x005B, "Joe Hacker", 10); */
|
||||
/* if (rc) */
|
||||
/* log_error ("failed to set `Name': rc=%04X\n", rc); */
|
||||
/* else */
|
||||
/* dump_one_do (slot, 0x005B); */
|
||||
|
||||
/* fixme: associate the internal state with the slot */
|
||||
}
|
||||
app->fnc.learn_status = do_learn_status;
|
||||
app->fnc.setattr = do_setattr;
|
||||
app->fnc.genkey = do_genkey;
|
||||
app->fnc.sign = do_sign;
|
||||
}
|
||||
|
||||
leave:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
208
scd/app.c
Normal file
208
scd/app.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
/* app.c - Application selection.
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "app-common.h"
|
||||
#include "apdu.h"
|
||||
|
||||
/* The select the best fitting application and return a context.
|
||||
Returns NULL if no application was found or no card is present. */
|
||||
APP
|
||||
select_application (void)
|
||||
{
|
||||
int reader_port = 32768; /* First USB reader. */
|
||||
int slot;
|
||||
int rc;
|
||||
APP app;
|
||||
|
||||
slot = apdu_open_reader (reader_port);
|
||||
if (slot == -1)
|
||||
{
|
||||
log_error ("card reader not available\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
app = xtrycalloc (1, sizeof *app);
|
||||
if (!app)
|
||||
{
|
||||
rc = out_of_core ();
|
||||
log_info ("error allocating context: %s\n", gpg_strerror (rc));
|
||||
/*apdu_close_reader (slot);*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
app->slot = slot;
|
||||
rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
|
||||
if (rc)
|
||||
{
|
||||
/* apdu_close_reader (slot); */
|
||||
log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
|
||||
xfree (app);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
app->initialized = 1;
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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. Caller must
|
||||
free SERIAL unless the function returns an error. */
|
||||
int
|
||||
app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
|
||||
{
|
||||
unsigned char *buf, *p;
|
||||
int i;
|
||||
|
||||
if (!app || !serial || !stamp)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
*serial = NULL;
|
||||
*stamp = 0; /* not available */
|
||||
|
||||
buf = xtrymalloc (app->serialnolen * 2 + 1);
|
||||
if (!buf)
|
||||
return gpg_error_from_errno (errno);
|
||||
for (p=buf, i=0; i < app->serialnolen; p +=2, i++)
|
||||
sprintf (p, "%02X", app->serialno[i]);
|
||||
*p = 0;
|
||||
*serial = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Write out the application specifig status lines for the LEARN
|
||||
command. */
|
||||
int
|
||||
app_write_learn_status (APP app, CTRL ctrl)
|
||||
{
|
||||
if (!app)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.learn_status)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
return app->fnc.learn_status (app, ctrl);
|
||||
}
|
||||
|
||||
|
||||
/* Perform a SETATTR operation. */
|
||||
int
|
||||
app_setattr (APP app, const char *name,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const unsigned char *value, size_t valuelen)
|
||||
{
|
||||
if (!app || !name || !*name || !value)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.setattr)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
return app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen);
|
||||
}
|
||||
|
||||
/* 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
|
||||
app_sign (APP app, const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
void **outdata, size_t *outdatalen )
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.sign)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.sign (app, keyidstr, hashalgo,
|
||||
pincb, pincb_arg,
|
||||
indata, indatalen,
|
||||
outdata, outdatalen);
|
||||
if (opt.verbose)
|
||||
log_info ("operation sign result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Decrypt the data in INDATA 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
|
||||
app_decipher (APP app, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
void **outdata, size_t *outdatalen )
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.decipher)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.decipher (app, keyidstr,
|
||||
pincb, pincb_arg,
|
||||
indata, indatalen,
|
||||
outdata, outdatalen);
|
||||
if (opt.verbose)
|
||||
log_info ("operation decipher result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Perform a SETATTR operation. */
|
||||
int
|
||||
app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !keynostr || !*keynostr || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.genkey)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg);
|
||||
if (opt.verbose)
|
||||
log_info ("operation genkey result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ map_sc_err (int rc)
|
|||
int
|
||||
card_help_get_keygrip (KsbaCert cert, unsigned char *array)
|
||||
{
|
||||
GCRY_SEXP s_pkey;
|
||||
gcry_sexp_t s_pkey;
|
||||
int rc;
|
||||
KsbaSexp p;
|
||||
size_t n;
|
||||
|
@ -558,3 +558,4 @@ card_decipher (CARD card, const char *keyidstr,
|
|||
log_info ("card operation decipher result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
290
scd/command.c
290
scd/command.c
|
@ -30,6 +30,7 @@
|
|||
#include <assuan.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "app-common.h"
|
||||
|
||||
/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */
|
||||
#define MAXLEN_PIN 100
|
||||
|
@ -69,6 +70,12 @@ reset_notify (ASSUAN_CONTEXT ctx)
|
|||
xfree (ctrl->in_data.value);
|
||||
ctrl->in_data.value = NULL;
|
||||
}
|
||||
if (ctrl->app_ctx)
|
||||
{
|
||||
/* FIXME: close the application. */
|
||||
xfree (ctrl->app_ctx);
|
||||
ctrl->app_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -85,8 +92,14 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
|
|||
static AssuanError
|
||||
open_card (CTRL ctrl)
|
||||
{
|
||||
if (!ctrl->card_ctx)
|
||||
{
|
||||
if (ctrl->app_ctx)
|
||||
return 0; /* Already initialized for one specific application. */
|
||||
if (ctrl->card_ctx)
|
||||
return 0; /* Already initialized using a card context. */
|
||||
|
||||
ctrl->app_ctx = select_application ();
|
||||
if (!ctrl->app_ctx)
|
||||
{ /* No application found - fall back to old mode. */
|
||||
int rc = card_open (&ctrl->card_ctx);
|
||||
if (rc)
|
||||
return map_to_assuan_status (rc);
|
||||
|
@ -95,6 +108,41 @@ open_card (CTRL ctrl)
|
|||
}
|
||||
|
||||
|
||||
/* Do the percent and plus/space unescaping in place and return tghe
|
||||
length of the valid buffer. */
|
||||
static size_t
|
||||
percent_plus_unescape (unsigned char *string)
|
||||
{
|
||||
unsigned char *p = string;
|
||||
size_t n = 0;
|
||||
|
||||
while (*string)
|
||||
{
|
||||
if (*string == '%' && string[1] && string[2])
|
||||
{
|
||||
string++;
|
||||
*p++ = xtoi_2 (string);
|
||||
n++;
|
||||
string+= 2;
|
||||
}
|
||||
else if (*string == '+')
|
||||
{
|
||||
*p++ = ' ';
|
||||
n++;
|
||||
string++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = *string++;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* SERIALNO
|
||||
|
||||
Return the serial number of the card using a status reponse. This
|
||||
|
@ -106,7 +154,7 @@ open_card (CTRL ctrl)
|
|||
|
||||
Background: We want to keep the client clear of handling card
|
||||
changes between operations; i.e. the client can assume that all
|
||||
operations are doneon the same card unless he call this function.
|
||||
operations are done on the same card unless he call this function.
|
||||
*/
|
||||
static int
|
||||
cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
|
||||
|
@ -120,7 +168,10 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
|
|||
if ((rc = open_card (ctrl)))
|
||||
return rc;
|
||||
|
||||
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
|
||||
if (ctrl->app_ctx)
|
||||
rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
|
||||
else
|
||||
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
|
||||
if (rc)
|
||||
return map_to_assuan_status (rc);
|
||||
rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
|
||||
|
@ -149,6 +200,16 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
|
|||
error message. The response of this command is a list of status
|
||||
lines formatted as this:
|
||||
|
||||
S APPTYPE <apptype>
|
||||
|
||||
This returns the type of the application, currently the strings:
|
||||
|
||||
P15 = PKCS-15 structure used
|
||||
DINSIG = DIN SIG
|
||||
OPENPGP = OpenPGP card
|
||||
|
||||
are implemented. These strings are aliases for the AID
|
||||
|
||||
S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>
|
||||
|
||||
If there is no certificate yet stored on the card a single "X" is
|
||||
|
@ -157,13 +218,34 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
|
|||
|
||||
S CERTINFO <certtype> <hexstring_with_id>
|
||||
|
||||
Where CERTINFO is a number indicating the type of certificate:
|
||||
Where CERTTYPE is a number indicating the type of certificate:
|
||||
0 := Unknown
|
||||
100 := Regular X.509 cert
|
||||
101 := Trusted X.509 cert
|
||||
102 := Useful X.509 cert
|
||||
|
||||
For certain cards, more information will be returned:
|
||||
|
||||
S KEY-FPR <no> <hexstring>
|
||||
|
||||
For OpenPGP cards this returns the stored fingerprints of the
|
||||
keys. This can be used check whether a key is available on the
|
||||
card. NO may be 1, 2 or 3.
|
||||
|
||||
S CA-FPR <no> <hexstring>
|
||||
|
||||
Similar to above, these are the fingerprints of keys assumed to be
|
||||
ultimately trusted.
|
||||
|
||||
S DISP-NAME <name_of_card_holder>
|
||||
|
||||
The name of the card holder as stored on the card; percent
|
||||
aescaping takes place, spaces are encoded as '+'
|
||||
|
||||
S PUBKEY-URL <url>
|
||||
|
||||
The URL to be used for locating the entire public key.
|
||||
|
||||
*/
|
||||
static int
|
||||
cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
||||
|
@ -183,8 +265,11 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
|||
char *serial_and_stamp;
|
||||
char *serial;
|
||||
time_t stamp;
|
||||
|
||||
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
|
||||
|
||||
if (ctrl->app_ctx)
|
||||
rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
|
||||
else
|
||||
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
|
||||
if (rc)
|
||||
return map_to_assuan_status (rc);
|
||||
rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
|
||||
|
@ -221,6 +306,8 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
|||
}
|
||||
|
||||
/* Return information about the certificates. */
|
||||
if (ctrl->app_ctx)
|
||||
rc = -1; /* This information is not yet available for applications. */
|
||||
for (idx=0; !rc; idx++)
|
||||
{
|
||||
char *certid;
|
||||
|
@ -248,6 +335,8 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
|||
|
||||
|
||||
/* Return information about the keys. */
|
||||
if (ctrl->app_ctx)
|
||||
rc = -1; /* This information is not yet available for applications. */
|
||||
for (idx=0; !rc; idx++)
|
||||
{
|
||||
unsigned char keygrip[20];
|
||||
|
@ -294,6 +383,9 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
|||
if (rc == -1)
|
||||
rc = 0;
|
||||
|
||||
if (!rc && ctrl->app_ctx)
|
||||
rc = app_write_learn_status (ctrl->app_ctx, ctrl);
|
||||
|
||||
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
@ -314,6 +406,9 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
|
|||
if ((rc = open_card (ctrl)))
|
||||
return rc;
|
||||
|
||||
if (ctrl->app_ctx)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
|
||||
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
|
||||
if (rc)
|
||||
{
|
||||
|
@ -348,6 +443,9 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
|
|||
if ((rc = open_card (ctrl)))
|
||||
return rc;
|
||||
|
||||
if (ctrl->app_ctx)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
|
||||
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
|
||||
if (rc)
|
||||
{
|
||||
|
@ -480,11 +578,19 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
|
|||
keyidstr = strdup (line);
|
||||
if (!keyidstr)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
rc = card_sign (ctrl->card_ctx,
|
||||
keyidstr, GCRY_MD_SHA1,
|
||||
pin_cb, ctx,
|
||||
ctrl->in_data.value, ctrl->in_data.valuelen,
|
||||
&outdata, &outdatalen);
|
||||
|
||||
if (ctrl->app_ctx)
|
||||
rc = app_sign (ctrl->app_ctx,
|
||||
keyidstr, GCRY_MD_SHA1,
|
||||
pin_cb, ctx,
|
||||
ctrl->in_data.value, ctrl->in_data.valuelen,
|
||||
&outdata, &outdatalen);
|
||||
else
|
||||
rc = card_sign (ctrl->card_ctx,
|
||||
keyidstr, GCRY_MD_SHA1,
|
||||
pin_cb, ctx,
|
||||
ctrl->in_data.value, ctrl->in_data.valuelen,
|
||||
&outdata, &outdatalen);
|
||||
free (keyidstr);
|
||||
if (rc)
|
||||
{
|
||||
|
@ -519,11 +625,18 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
|
|||
keyidstr = strdup (line);
|
||||
if (!keyidstr)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
rc = card_decipher (ctrl->card_ctx,
|
||||
keyidstr,
|
||||
pin_cb, ctx,
|
||||
ctrl->in_data.value, ctrl->in_data.valuelen,
|
||||
&outdata, &outdatalen);
|
||||
if (ctrl->app_ctx)
|
||||
rc = app_decipher (ctrl->app_ctx,
|
||||
keyidstr,
|
||||
pin_cb, ctx,
|
||||
ctrl->in_data.value, ctrl->in_data.valuelen,
|
||||
&outdata, &outdatalen);
|
||||
else
|
||||
rc = card_decipher (ctrl->card_ctx,
|
||||
keyidstr,
|
||||
pin_cb, ctx,
|
||||
ctrl->in_data.value, ctrl->in_data.valuelen,
|
||||
&outdata, &outdatalen);
|
||||
free (keyidstr);
|
||||
if (rc)
|
||||
{
|
||||
|
@ -541,6 +654,99 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
|
|||
}
|
||||
|
||||
|
||||
/* SETATTR <name> <value>
|
||||
|
||||
This command is used to store data on a a smartcard. The allowed
|
||||
names and values are depend on the currently selected smartcard
|
||||
application. NAME and VALUE must be percent and '+' escaped.
|
||||
|
||||
However, the curent implementation assumes that Name is not escaped;
|
||||
this works as long as noone uses arbitrary escaping.
|
||||
|
||||
A PIN will be requested for most NAMEs. See the corresponding
|
||||
setattr function of the actually used application (app-*.c) for
|
||||
details. */
|
||||
static int
|
||||
cmd_setattr (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
int rc;
|
||||
char *keyword;
|
||||
int keywordlen;
|
||||
size_t nbytes;
|
||||
|
||||
if ((rc = open_card (ctrl)))
|
||||
return rc;
|
||||
|
||||
keyword = line;
|
||||
for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
|
||||
;
|
||||
if (*line)
|
||||
*line++ = 0;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
nbytes = percent_plus_unescape (line);
|
||||
|
||||
rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes);
|
||||
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
/* GENKEY [--force] <no>
|
||||
|
||||
Generate a key on-card identified by NO, which is application
|
||||
specific. Return values are application specific. For OpenPGP
|
||||
cards 2 status lines are returned:
|
||||
|
||||
S KEY-FPR <hexstring>
|
||||
S KEY-CREATED-AT <seconds_since_epoch>
|
||||
S KEY-DATA [p|n] <hexdata>
|
||||
|
||||
|
||||
--force is required to overwriet an already existing key. The
|
||||
KEY-CREATED-AT is required for further processing because it is
|
||||
part of the hashed key material for the fingerprint.
|
||||
|
||||
The public part of the key can also later be retrieved using the
|
||||
READKEY command.
|
||||
|
||||
*/
|
||||
static int
|
||||
cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
int rc;
|
||||
char *keyno;
|
||||
int force = has_option (line, "--force");
|
||||
|
||||
/* Skip over options. */
|
||||
while ( *line == '-' && line[1] == '-' )
|
||||
{
|
||||
while (!spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
if (!*line)
|
||||
return set_error (Parameter_Error, "no key number given");
|
||||
keyno = line;
|
||||
while (!spacep (line))
|
||||
line++;
|
||||
*line = 0;
|
||||
|
||||
if ((rc = open_card (ctrl)))
|
||||
return rc;
|
||||
|
||||
if (!ctrl->app_ctx)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
|
||||
rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx);
|
||||
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Tell the assuan library about our commands */
|
||||
|
@ -560,6 +766,8 @@ register_commands (ASSUAN_CONTEXT ctx)
|
|||
{ "PKDECRYPT", cmd_pkdecrypt },
|
||||
{ "INPUT", NULL },
|
||||
{ "OUTPUT", NULL },
|
||||
{ "SETATTR", cmd_setattr },
|
||||
{ "GENKEY", cmd_genkey },
|
||||
{ NULL }
|
||||
};
|
||||
int i, rc;
|
||||
|
@ -646,3 +854,51 @@ scd_command_handler (int listen_fd)
|
|||
|
||||
assuan_deinit_server (ctx);
|
||||
}
|
||||
|
||||
|
||||
/* Send a line with status information via assuan and escape all given
|
||||
buffers. The variable elements are pairs of (char *, size_t),
|
||||
terminated with a (NULL, 0). */
|
||||
void
|
||||
send_status_info (CTRL ctrl, const char *keyword, ...)
|
||||
{
|
||||
va_list arg_ptr;
|
||||
const unsigned char *value;
|
||||
size_t valuelen;
|
||||
char buf[950], *p;
|
||||
size_t n;
|
||||
ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
|
||||
|
||||
va_start (arg_ptr, keyword);
|
||||
|
||||
p = buf;
|
||||
n = 0;
|
||||
while ( (value = va_arg (arg_ptr, const unsigned char *)) )
|
||||
{
|
||||
valuelen = va_arg (arg_ptr, size_t);
|
||||
if (!valuelen)
|
||||
continue; /* empty buffer */
|
||||
if (n)
|
||||
{
|
||||
*p++ = ' ';
|
||||
n++;
|
||||
}
|
||||
for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
|
||||
{
|
||||
if (*value < ' ' || *value == '+')
|
||||
{
|
||||
sprintf (p, "%%%02X", *value);
|
||||
p += 3;
|
||||
}
|
||||
else if (*value == ' ')
|
||||
*p++ = '+';
|
||||
else
|
||||
*p++ = *value;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
assuan_write_status (ctx, keyword, buf);
|
||||
|
||||
va_end (arg_ptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,12 @@
|
|||
#include <string.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include <gcrypt.h>
|
||||
#include "scdaemon.h"
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include "apdu.h" /* for open_reader */
|
||||
#include "atr.h"
|
||||
#include "app-common.h"
|
||||
|
||||
#define _(a) (a)
|
||||
|
||||
|
@ -104,6 +105,9 @@ main (int argc, char **argv )
|
|||
ARGPARSE_ARGS pargs;
|
||||
int slot, rc;
|
||||
int reader_port = 32768; /* First USB reader. */
|
||||
struct app_ctx_s appbuf;
|
||||
|
||||
memset (&appbuf, 0, sizeof appbuf);
|
||||
|
||||
set_strusage (my_strusage);
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
|
@ -147,7 +151,8 @@ main (int argc, char **argv )
|
|||
if (rc)
|
||||
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
|
||||
|
||||
rc = app_select_openpgp (slot);
|
||||
appbuf.slot = slot;
|
||||
rc = app_select_openpgp (&appbuf, NULL, NULL);
|
||||
if (rc)
|
||||
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
|
||||
else
|
||||
|
@ -159,3 +164,8 @@ main (int argc, char **argv )
|
|||
|
||||
|
||||
|
||||
void
|
||||
send_status_info (CTRL ctrl, const char *keyword, ...)
|
||||
{
|
||||
/* DUMMY */
|
||||
}
|
||||
|
|
|
@ -34,12 +34,12 @@
|
|||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "scdaemon.h"
|
||||
#include <ksba.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include <assuan.h> /* malloc hooks */
|
||||
#include "scdaemon.h"
|
||||
|
||||
#include "i18n.h"
|
||||
#include "sysutils.h"
|
||||
|
|
|
@ -76,18 +76,22 @@ struct {
|
|||
|
||||
struct server_local_s;
|
||||
struct card_ctx_s;
|
||||
struct app_ctx_s;
|
||||
|
||||
struct server_control_s {
|
||||
struct server_local_s *server_local;
|
||||
struct card_ctx_s *card_ctx;
|
||||
struct app_ctx_s *app_ctx;
|
||||
struct {
|
||||
unsigned char *value;
|
||||
int valuelen;
|
||||
} in_data; /* helper to store the value we are going to sign */
|
||||
|
||||
};
|
||||
|
||||
typedef struct server_control_s *CTRL;
|
||||
typedef struct card_ctx_s *CARD;
|
||||
typedef struct app_ctx_s *APP;
|
||||
|
||||
/*-- scdaemon.c --*/
|
||||
void scd_exit (int rc);
|
||||
|
@ -95,6 +99,7 @@ void scd_init_default_ctrl (CTRL ctrl);
|
|||
|
||||
/*-- command.c --*/
|
||||
void scd_command_handler (int);
|
||||
void send_status_info (CTRL ctrl, const char *keyword, ...);
|
||||
|
||||
/*-- card.c --*/
|
||||
int card_open (CARD *rcard);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue