Signing using a PKCS15 smartcard does work. How to create such a card

is of course a different thing.  Note, that you need to create the
shadowed-private-key file manually.
This commit is contained in:
Werner Koch 2002-03-05 17:14:45 +00:00
parent c8454f792d
commit 2fa73e781f
5 changed files with 354 additions and 29 deletions

View File

@ -1,3 +1,15 @@
2002-03-05 Werner Koch <wk@gnupg.org>
* call-scd.c (inq_needpin): New.
(agent_card_pksign): Add getpin_cb args.
2002-03-04 Werner Koch <wk@gnupg.org>
* pksign.c (agent_pksign): Changed how the diversion is done.
* divert-scd.c (divert_pksign): Change interface and implemented it.
(encode_md_for_card): New.
* call-scd.c (agent_card_pksign): New.
2002-02-28 Werner Koch <wk@gnupg.org>
* pksign.c (agent_pksign): Detect whether a Smartcard is to be

View File

@ -151,14 +151,19 @@ int agent_marktrusted (const char *name, const char *fpr, int flag);
/*-- divert-scd.c --*/
int divert_pksign (GCRY_SEXP *s_sig, GCRY_SEXP s_hash,
const char *shadow_info);
int divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
const char *shadow_info, unsigned char **r_sig);
int divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher,
const char *shadow_info);
/*-- call-scd.c --*/
int agent_card_learn (void);
int agent_card_serialno (char **r_serialno);
int agent_card_pksign (const char *keyid,
int (*getpin_cb)(void *, const char *, char*, size_t),
void *getpin_cb_arg,
const unsigned char *indata, size_t indatalen,
char **r_buf, size_t *r_buflen);
#endif /*AGENT_H*/

View File

@ -46,6 +46,80 @@ struct learn_parm_s {
char *buffer;
};
struct inq_needpin_s {
ASSUAN_CONTEXT ctx;
int (*getpin_cb)(void *, const char *, char*, size_t);
void *getpin_cb_arg;
};
struct membuf {
size_t len;
size_t size;
char *buf;
int out_of_core;
};
/* A simple implemnation of a dynamic buffer. Use init_membuf() to
create a buffer, put_membuf to append bytes and get_membuf to
release and return the buffer. Allocation errors are detected but
only returned at the final get_membuf(), this helps not to clutter
the code with out of core checks. */
static void
init_membuf (struct membuf *mb, int initiallen)
{
mb->len = 0;
mb->size = initiallen;
mb->out_of_core = 0;
mb->buf = xtrymalloc (initiallen);
if (!mb->buf)
mb->out_of_core = 1;
}
static void
put_membuf (struct membuf *mb, const void *buf, size_t len)
{
if (mb->out_of_core)
return;
if (mb->len + len >= mb->size)
{
char *p;
mb->size += len + 1024;
p = xtryrealloc (mb->buf, mb->size);
if (!p)
{
mb->out_of_core = 1;
return;
}
mb->buf = p;
}
memcpy (mb->buf + mb->len, buf, len);
mb->len += len;
}
static void *
get_membuf (struct membuf *mb, size_t *len)
{
char *p;
if (mb->out_of_core)
{
xfree (mb->buf);
mb->buf = NULL;
return NULL;
}
p = mb->buf;
*len = mb->len;
mb->buf = NULL;
mb->out_of_core = 1; /* don't allow a reuse */
return p;
}
@ -216,6 +290,116 @@ agent_card_serialno (char **r_serialno)
return 0;
}
static AssuanError
membuf_data_cb (void *opaque, const void *buffer, size_t length)
{
struct membuf *data = opaque;
put_membuf (data, buffer, length);
return 0;
}
/* Handle the NEEDPIN inquiry. */
static AssuanError
inq_needpin (void *opaque, const char *line)
{
struct inq_needpin_s *parm = opaque;
char *pin;
size_t pinlen;
int rc;
if (!(!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7])))
{
log_error ("unsupported inquiry `%s'\n", line);
return ASSUAN_Inquire_Unknown;
}
line += 7;
pinlen = 90;
pin = gcry_malloc_secure (pinlen);
if (!pin)
return ASSUAN_Out_Of_Core;
rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen);
if (rc)
rc = ASSUAN_Canceled;
if (!rc)
rc = assuan_send_data (parm->ctx, pin, pinlen);
xfree (pin);
return rc;
}
/* Create a signature using the current card */
int
agent_card_pksign (const char *keyid,
int (*getpin_cb)(void *, const char *, char*, size_t),
void *getpin_cb_arg,
const unsigned char *indata, size_t indatalen,
char **r_buf, size_t *r_buflen)
{
int rc, i;
char *p, line[ASSUAN_LINELENGTH];
struct membuf data;
struct inq_needpin_s inqparm;
size_t len;
unsigned char *sigbuf;
size_t sigbuflen;
*r_buf = NULL;
rc = start_scd ();
if (rc)
return rc;
if (indatalen*2 + 50 > DIM(line))
return seterr (General_Error);
sprintf (line, "SETDATA ");
p = line + strlen (line);
for (i=0; i < indatalen ; i++, p += 2 )
sprintf (p, "%02X", indata[i]);
rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
init_membuf (&data, 1024);
inqparm.ctx = scd_ctx;
inqparm.getpin_cb = getpin_cb;
inqparm.getpin_cb_arg = getpin_cb_arg;
snprintf (line, DIM(line)-1, "PKSIGN %s", keyid);
line[DIM(line)-1] = 0;
rc = assuan_transact (scd_ctx, line,
membuf_data_cb, &data,
inq_needpin, &inqparm,
NULL, NULL);
if (rc)
{
xfree (get_membuf (&data, &len));
return map_assuan_err (rc);
}
sigbuf = get_membuf (&data, &sigbuflen);
/* create an S-expression from it which is formatted like this:
"(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" */
*r_buflen = 21 + 11 + sigbuflen + 4;
*r_buf = xtrymalloc (*r_buflen);
if (!*r_buf)
{
xfree (*r_buf);
return GNUPG_Out_Of_Core;
}
p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" );
sprintf (p, "%u:", (unsigned int)sigbuflen);
p += strlen (p);
memcpy (p, sigbuf, sigbuflen);
p += sigbuflen;
strcpy (p, ")))");
xfree (sigbuf);
assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL));
return 0;
}

View File

@ -126,20 +126,135 @@ ask_for_card (const unsigned char *shadow_info, char **r_kid)
}
/* fixme: this should be moved to libgcrypt and only be used if the
smartcard does not support pkcs-1 itself */
static int
encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
unsigned int nbits, unsigned char **r_val, size_t *r_len)
{
int nframe = (nbits+7) / 8;
byte *frame;
int i, n;
byte asn[100];
size_t asnlen;
asnlen = DIM(asn);
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
{
log_error ("no object identifier for algo %d\n", algo);
return GNUPG_Internal_Error;
}
if (digestlen + asnlen + 4 > nframe )
{
log_error ("can't encode a %d bit MD into a %d bits frame\n",
(int)(digestlen*8), (int)nbits);
return GNUPG_Internal_Error;
}
/* We encode the MD in this way:
*
* 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
*
* PAD consists of FF bytes.
*/
frame = xtrymalloc (nframe);
if (!frame)
return GNUPG_Out_Of_Core;
n = 0;
frame[n++] = 0;
frame[n++] = 1; /* block type */
i = nframe - digestlen - asnlen -3 ;
assert ( i > 1 );
memset ( frame+n, 0xff, i ); n += i;
frame[n++] = 0;
memcpy ( frame+n, asn, asnlen ); n += asnlen;
memcpy ( frame+n, digest, digestlen ); n += digestlen;
assert ( n == nframe );
if (DBG_CRYPTO)
log_printhex ("encoded hash:", frame, nframe);
*r_val = frame;
*r_len = nframe;
return 0;
}
/* Callback used to ask for the PIN which should be set into BUF. The
buf has been allocated by the caller and is of size MAXBUF which
includes the terminating null. The function should return an UTF-8
string with the passphrase, the buffer may optioanlly be padded
with arbitrary characters */
static int
getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
{
struct pin_entry_info_s *pi;
int rc;
int tries = 0;
const char *errtext;
assert (!opaque);
if (maxbuf < 2)
return GNUPG_Invalid_Value;
/* FIXME: keep PI and TRIES in OPAQUE */
pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
pi->max_length = maxbuf-1;
pi->min_digits = 0; /* we want a real passphrase */
pi->max_digits = 8;
pi->max_tries = 3;
errtext = NULL;
do
{
rc = agent_askpin (info, errtext, pi);
if (!rc)
{
strncpy (buf, pi->pin, maxbuf-1);
buf[maxbuf-1] = 0;
xfree (pi);
return 0;
}
errtext = pi->min_digits? trans ("Bad PIN") : trans ("Bad Passphrase");
}
while ((rc == GNUPG_Bad_Passphrase || rc == GNUPG_Bad_PIN)
&& tries++ < 3);
xfree (pi);
return rc;
}
int
divert_pksign (GCRY_SEXP *s_sig, GCRY_SEXP s_hash, const char *shadow_info)
divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
const char *shadow_info, unsigned char **r_sig)
{
int rc;
char *kid;
size_t siglen;
char *sigval;
unsigned char *data;
size_t ndata;
rc = ask_for_card (shadow_info, &kid);
if (rc)
return rc;
rc = encode_md_for_card (digest, digestlen, algo, 1024 /* fixme*/,
&data, &ndata);
if (rc)
return rc;
rc = agent_card_pksign (kid, getpin_cb, NULL,
data, ndata, &sigval, &siglen);
if (!rc)
*r_sig = sigval;
xfree (data);
xfree (kid);
return GNUPG_Not_Implemented;
return rc;
}

View File

@ -44,7 +44,7 @@ do_encode_md (const unsigned char *digest, size_t digestlen, int algo,
asnlen = DIM(asn);
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
{
log_error ("No object identifier for algo %d\n", algo);
log_error ("no object identifier for algo %d\n", algo);
return GNUPG_Internal_Error;
}
@ -106,28 +106,37 @@ agent_pksign (CTRL ctrl, FILE *outfp)
goto leave;
}
/* put the hash into a sexp FIXME: this belongs into libgcrypt/divert-scd.c*/
rc = do_encode_md (ctrl->digest.value,
ctrl->digest.valuelen,
ctrl->digest.algo,
gcry_pk_get_nbits (s_skey),
&frame);
if (rc)
goto leave;
if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
BUG ();
if (!s_skey)
{ /* divert operation to the smartcard */
rc = divert_pksign (&s_sig, s_hash, shadow_info);
unsigned char *sigbuf;
rc = divert_pksign (ctrl->digest.value,
ctrl->digest.valuelen,
ctrl->digest.algo,
shadow_info, &sigbuf);
if (rc)
{
log_error ("smartcard signing failed: %s\n", gnupg_strerror (rc));
goto leave;
}
len = gcry_sexp_canon_len (sigbuf, 0, NULL, NULL);
assert (len);
buf = sigbuf;
}
else
{ /* no smartcard, but a private key */
/* put the hash into a sexp */
rc = do_encode_md (ctrl->digest.value,
ctrl->digest.valuelen,
ctrl->digest.algo,
gcry_pk_get_nbits (s_skey),
&frame);
if (rc)
goto leave;
if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
BUG ();
if (DBG_CRYPTO)
{
log_debug ("skey: ");
@ -142,19 +151,19 @@ agent_pksign (CTRL ctrl, FILE *outfp)
rc = map_gcry_err (rc);
goto leave;
}
}
if (DBG_CRYPTO)
{
log_debug ("result: ");
gcry_sexp_dump (s_sig);
}
if (DBG_CRYPTO)
{
log_debug ("result: ");
gcry_sexp_dump (s_sig);
}
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xmalloc (len);
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len);
assert (len);
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xmalloc (len);
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len);
assert (len);
}
/* FIXME: we must make sure that no buffering takes place or we are
in full control of the buffer memory (easy to do) - should go