scd:nks: Support the Telesec ESIGN application.

* scd/app-nks.c (find_fid_by_keyref): Disable the cache for now.
(readcert_from_ef): Considere an all zero certificate as not found.
(do_sign): Support ECC and the ESIGN application.
--

This allows me to create qualified signatures using my Telesec card.
There is of course more work to do but this is the first step.

Note: The design of the FID cache needs to be reconsidered.  Until
that the lookup here has been disabled.  The do_sign code should be
revamped to be similar to what we do in app-p15.

GnuPG-bug-id: 5219, 4938
This commit is contained in:
Werner Koch 2022-05-29 15:55:26 +02:00
parent 7aabd94b81
commit 07eaf006c2
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
1 changed files with 81 additions and 26 deletions

View File

@ -509,10 +509,14 @@ find_fid_by_keyref (app_t app, const char *keyref, int *r_idx, int *r_algo)
{
struct fid_cache_s *ci;
for (ci = app->app_local->fid_cache; ci; ci = ci->next)
/* FIXME: Our cache structure needs to be revised. It doesn't
* take the app_id into account and we don't have a way to
* directly access the FID item if there are several of them
* with different app_ids. We disable the cache for now. */
for (ci = app->app_local->fid_cache ; ci; ci = ci->next)
if (ci->fid && ci->got_keygrip && !strcmp (ci->keygripstr, keyref))
break;
if (ci) /* Cached */
if (ci && 0 ) /* Cached (disabled) */
{
for (idx=0; filelist[idx].fid; idx++)
if (filelist[idx].fid == ci->fid)
@ -960,7 +964,7 @@ readcert_from_ef (app_t app, int fid, unsigned char **cert, size_t *certlen)
return err;
}
if (!buflen || *buffer == 0xff)
if (!buflen || *buffer == 0xff || all_zero_p (buffer, buflen))
{
log_info ("nks: no certificate contained in FID 0x%04X\n", fid);
err = gpg_error (GPG_ERR_NOT_FOUND);
@ -1740,28 +1744,32 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
unsigned char data[83]; /* Must be large enough for a SHA-1 digest
+ the largest OID prefix. */
size_t datalen;
int algo;
unsigned int digestlen; /* Length of the hash. */
unsigned char oidbuf[64];
size_t oidbuflen;
(void)ctrl;
switch (indatalen)
{
case 20: // plain SHA-1 or RMD160 digest
case 28: // plain SHA-224 digest
case 32: // plain SHA-256 digest
case 48: // plain SHA-384 digest
case 64: // plain SHA-512 digest
case 35: // ASN.1 encoded SHA-1 or RMD160 digest
case 47: // ASN.1 encoded SHA-224 digest
case 51: // ASN.1 encoded SHA-256 digest
case 67: // ASN.1 encoded SHA-384 digest
case 83: // ASN.1 encoded SHA-512 digest
break;
case 20: /* plain SHA-1 or RMD160 digest */
case 28: /* plain SHA-224 digest */
case 32: /* plain SHA-256 digest */
case 48: /* plain SHA-384 digest */
case 64: /* plain SHA-512 digest */
case 35: /* ASN.1 encoded SHA-1 or RMD160 digest */
case 47: /* ASN.1 encoded SHA-224 digest */
case 51: /* ASN.1 encoded SHA-256 digest */
case 67: /* ASN.1 encoded SHA-384 digest */
case 83: /* ASN.1 encoded SHA-512 digest */
break;
default:
log_debug ("invalid length of input data: %zu\n", indatalen);
log_info ("nks: invalid length of input data: %zu\n", indatalen);
return gpg_error (GPG_ERR_INV_VALUE);
}
err = find_fid_by_keyref (app, keyidstr, &idx, NULL);
err = find_fid_by_keyref (app, keyidstr, &idx, &algo);
if (err)
return err;
@ -1780,12 +1788,57 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
kid = filelist[idx].kid;
/* Prepare the DER object from INDATA. */
if (app->appversion > 2 && (indatalen == 35
|| indatalen == 47
|| indatalen == 51
|| indatalen == 67
|| indatalen == 83))
digestlen = gcry_md_get_algo_dlen (hashalgo);
/* Prepare the input object from INDATA. */
if (algo == GCRY_PK_ECC)
{
if (digestlen != 32 && digestlen != 48 && digestlen != 64)
{
log_error ("nks: ECC signing not possible: dlen=%u\n", digestlen);
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
if (indatalen == digestlen)
{
/* Already prepared. */
datalen = indatalen;
log_assert (datalen <= sizeof data);
memcpy (data, indata, datalen);
}
else if (indatalen > digestlen)
{
/* Assume a PKCS#1 prefix and remove it. */
oidbuflen = sizeof oidbuf;
err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen);
if (err)
{
log_error ("nks: no OID for hash algo %d\n", hashalgo);
return gpg_error (GPG_ERR_INTERNAL);
}
if (indatalen != oidbuflen + digestlen
|| memcmp (indata, oidbuf, oidbuflen))
{
log_error ("nks: input data too long for ECC: len=%zu\n",
indatalen);
return gpg_error (GPG_ERR_INV_VALUE);
}
datalen = indatalen - oidbuflen;
log_assert (datalen <= sizeof data);
memcpy (data, (const char*)indata + oidbuflen, datalen);
}
else
{
log_error ("nks: input data too short for ECC: len=%zu\n",
indatalen);
return gpg_error (GPG_ERR_INV_VALUE);
}
}
else if (app->appversion > 2 && (indatalen == 35
|| indatalen == 47
|| indatalen == 51
|| indatalen == 67
|| indatalen == 83))
{
/* Verify that the caller has sent a proper ASN.1 encoded hash
for RMD160 or SHA-{1,224,256,384,512}. */
@ -1821,7 +1874,8 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
memcpy (data, indata, indatalen);
datalen = 35;
}
/* Concatenate prefix and digest. */
/* Concatenate prefix and digest.
* Note that the X macro creates an "else if". Ugly - I know. */
#define X(algo,prefix,plaindigestlen) \
if ((hashalgo == (algo)) && (indatalen == (plaindigestlen))) \
{ \
@ -1841,7 +1895,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
#undef X
/* Send an MSE for PSO:Computer_Signature. */
if (app->appversion > 2)
if (app->appversion > 2 && app->app_local->active_nks_app != NKS_APP_ESIGN)
{
unsigned char mse[6];
@ -1855,8 +1909,9 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
mse, sizeof mse);
}
/* We use the Global PIN 1 */
if (app->appversion == 15)
if (app->app_local->active_nks_app == NKS_APP_ESIGN)
pwid = 0x81;
else if (app->appversion == 15)
pwid = 0x03;
else
pwid = 0x00;