mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
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:
parent
c8454f792d
commit
2fa73e781f
@ -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>
|
2002-02-28 Werner Koch <wk@gnupg.org>
|
||||||
|
|
||||||
* pksign.c (agent_pksign): Detect whether a Smartcard is to be
|
* pksign.c (agent_pksign): Detect whether a Smartcard is to be
|
||||||
|
@ -151,14 +151,19 @@ int agent_marktrusted (const char *name, const char *fpr, int flag);
|
|||||||
|
|
||||||
|
|
||||||
/*-- divert-scd.c --*/
|
/*-- divert-scd.c --*/
|
||||||
int divert_pksign (GCRY_SEXP *s_sig, GCRY_SEXP s_hash,
|
int divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
|
||||||
const char *shadow_info);
|
const char *shadow_info, unsigned char **r_sig);
|
||||||
int divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher,
|
int divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher,
|
||||||
const char *shadow_info);
|
const char *shadow_info);
|
||||||
|
|
||||||
/*-- call-scd.c --*/
|
/*-- call-scd.c --*/
|
||||||
int agent_card_learn (void);
|
int agent_card_learn (void);
|
||||||
int agent_card_serialno (char **r_serialno);
|
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*/
|
#endif /*AGENT_H*/
|
||||||
|
184
agent/call-scd.c
184
agent/call-scd.c
@ -46,6 +46,80 @@ struct learn_parm_s {
|
|||||||
char *buffer;
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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
|
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;
|
int rc;
|
||||||
char *kid;
|
char *kid;
|
||||||
|
size_t siglen;
|
||||||
|
char *sigval;
|
||||||
|
unsigned char *data;
|
||||||
|
size_t ndata;
|
||||||
|
|
||||||
rc = ask_for_card (shadow_info, &kid);
|
rc = ask_for_card (shadow_info, &kid);
|
||||||
if (rc)
|
if (rc)
|
||||||
return 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);
|
xfree (kid);
|
||||||
return GNUPG_Not_Implemented;
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ do_encode_md (const unsigned char *digest, size_t digestlen, int algo,
|
|||||||
asnlen = DIM(asn);
|
asnlen = DIM(asn);
|
||||||
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
|
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;
|
return GNUPG_Internal_Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,28 +106,37 @@ agent_pksign (CTRL ctrl, FILE *outfp)
|
|||||||
goto leave;
|
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)
|
if (!s_skey)
|
||||||
{ /* divert operation to the smartcard */
|
{ /* 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)
|
if (rc)
|
||||||
{
|
{
|
||||||
log_error ("smartcard signing failed: %s\n", gnupg_strerror (rc));
|
log_error ("smartcard signing failed: %s\n", gnupg_strerror (rc));
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
len = gcry_sexp_canon_len (sigbuf, 0, NULL, NULL);
|
||||||
|
assert (len);
|
||||||
|
buf = sigbuf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* no smartcard, but a private key */
|
{ /* 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)
|
if (DBG_CRYPTO)
|
||||||
{
|
{
|
||||||
log_debug ("skey: ");
|
log_debug ("skey: ");
|
||||||
@ -142,19 +151,19 @@ agent_pksign (CTRL ctrl, FILE *outfp)
|
|||||||
rc = map_gcry_err (rc);
|
rc = map_gcry_err (rc);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (DBG_CRYPTO)
|
if (DBG_CRYPTO)
|
||||||
{
|
{
|
||||||
log_debug ("result: ");
|
log_debug ("result: ");
|
||||||
gcry_sexp_dump (s_sig);
|
gcry_sexp_dump (s_sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
|
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||||
assert (len);
|
assert (len);
|
||||||
buf = xmalloc (len);
|
buf = xmalloc (len);
|
||||||
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len);
|
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len);
|
||||||
assert (len);
|
assert (len);
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME: we must make sure that no buffering takes place or we are
|
/* 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
|
in full control of the buffer memory (easy to do) - should go
|
||||||
|
Loading…
x
Reference in New Issue
Block a user