scd: Add --challenge-response option to PK_AUTH for OpenPGP card.

* scd/app-openpgp.c (rmd160_prefix, sha1_prefix, sha224_prefix)
(sha256_prefix, sha384_prefix, sha512_prefix): Move the scope up.
(gen_challenge): New.
(do_auth): Support challenge-response check if it signs correctly.
* scd/app.c (app_auth): Remove the check INDATA and INDATALEN.
* scd/command.c (cmd_pkauth): Support --challenge-response option.

--

GnuPG-bug-id: 5862
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2022-03-03 17:45:49 +09:00
parent 756c0bd5d8
commit 44621120a2
3 changed files with 201 additions and 26 deletions

View File

@ -256,7 +256,7 @@ struct app_local_s {
rsa_key_format_t format;
} rsa;
struct {
const char *curve;
const char *curve; /* Canonical name defined in openpgp-oid.c */
int algo;
unsigned int flags;
} ecc;
@ -5156,6 +5156,29 @@ check_keyidstr (app_t app, const char *keyidstr, int keyno, int *r_use_auth)
}
static const 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 };
static const unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
static const unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */
{ 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
0x1C };
static const unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */
{ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20 };
static const unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */
{ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
0x00, 0x04, 0x30 };
static const unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */
{ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40 };
/* Compute a digital signature on INDATA which is expected to be the
raw message digest. For this application the KEYIDSTR consists of
the serialnumber and the fingerprint delimited by a slash.
@ -5175,28 +5198,6 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen )
{
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 };
static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */
{ 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
0x1C };
static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */
{ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20 };
static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */
{ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
0x00, 0x04, 0x30 };
static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */
{ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40 };
int rc;
unsigned char data[19+64];
size_t datalen;
@ -5354,6 +5355,60 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
return rc;
}
/* Generate data to be signed for PKAUTH with --challenge-response. */
static gpg_error_t
gen_challenge (app_t app, const void **r_data, size_t *r_datalen)
{
void *data;
size_t datalen;
int header_size;
const unsigned char *hash_prefix = NULL;
if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC)
{
unsigned int n;
openpgp_curve_to_oid (app->app_local->keyattr[2].ecc.curve, &n, NULL);
/* No hash algo header, and appropriate length of random octets,
determined by field size of the curve. */
datalen = (n+7)/8;
header_size = 0;
}
else
{
/* Hash algo header, and random octets of hash size, the hash
algo is determined by size of key. */
if (app->app_local->keyattr[2].rsa.n_bits <= 2048)
{
datalen = 32;
hash_prefix = sha256_prefix;
}
else if (app->app_local->keyattr[2].rsa.n_bits <= 3072)
{
datalen = 48;
hash_prefix = sha384_prefix;
}
else
{
datalen = 64;
hash_prefix = sha512_prefix;
}
header_size = 19;
}
data = xtrymalloc (datalen+header_size);
if (!data)
return gpg_error_from_syserror ();
if (hash_prefix)
memcpy (data, hash_prefix, header_size);
gcry_create_nonce ((char *)data+header_size, datalen);
*r_data = data;
*r_datalen = datalen+header_size;
return 0;
}
/* Compute a digital signature using the INTERNAL AUTHENTICATE command
on INDATA which is expected to be the raw message digest. For this
application the KEYIDSTR consists of the serialnumber and the
@ -5372,9 +5427,24 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
unsigned char **outdata, size_t *outdatalen )
{
int rc;
int challenge_generated = 0;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
if (indatalen == 0)
{
rc = get_public_key (app, 2);
if (rc)
return rc;
rc = gen_challenge (app, &indata, &indatalen);
if (rc)
return rc;
challenge_generated = 1;
goto indata_ready;
}
if (app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
&& indatalen > 101) /* For a 2048 bit key. */
return gpg_error (GPG_ERR_INV_VALUE);
@ -5396,6 +5466,8 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
}
}
indata_ready:
/* Check whether an OpenPGP card of any version has been requested. */
if (!ascii_strcasecmp (keyidstr, "OPENPGP.3"))
;
@ -5436,6 +5508,95 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
outdata, outdatalen);
if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
clear_chv_status (app, ctrl, 1);
/* Verify the result, when CHALLENGE_GENERATED */
if (challenge_generated)
{
gcry_sexp_t s_pkey, s_sig, s_hash;
const char *fmt;
if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC)
{
if (!strcmp (app->app_local->keyattr[2].ecc.curve, "Ed25519"))
fmt = "(data(flags eddsa)(hash-algo sha512)(value %b))";
else
fmt = "(data(value %b))";
}
else
{
void *old_indata = (void *)indata;
unsigned char *new_indata;
size_t new_indatalen;
/* For RSA, it's PKCS#1 padding. */
new_indatalen = app->app_local->keyattr[2].rsa.n_bits / 8;
new_indata = xtrymalloc (new_indatalen);
if (!new_indata)
{
rc = gpg_error_from_syserror ();
xfree (old_indata);
return rc;
}
memset (new_indata, 0xff, new_indatalen);
new_indata[0] = 0x00;
new_indata[1] = 0x01;
new_indata[new_indatalen - indatalen -1] = 0x00;
memcpy (new_indata + new_indatalen - indatalen,
indata, indatalen);
xfree (old_indata);
indata = new_indata;
indatalen = new_indatalen;
fmt = "%b"; /* Old style data format. */
}
rc = gcry_sexp_build (&s_hash, NULL, fmt, (int)indatalen, indata);
if (rc)
{
xfree ((void *)indata);
return rc;
}
if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC)
{
if (!strcmp (app->app_local->keyattr[2].ecc.curve, "Ed25519")
|| !strcmp (app->app_local->keyattr[2].ecc.curve, "Ed448"))
fmt = "(sig-val(eddsa(r %b)(s %b)))";
else
fmt = "(sig-val(ecdsa(r %b)(s %b)))";
rc = gcry_sexp_build (&s_sig, NULL, fmt,
(int)*outdatalen/2, *outdata,
(int)*outdatalen/2, *outdata+*outdatalen/2);
}
else
{
fmt = "(sig-val(rsa(s %b)))";
rc = gcry_sexp_build (&s_sig, NULL, fmt,
(int)*outdatalen, *outdata);
}
if (rc)
{
gcry_sexp_release (s_hash);
xfree ((void *)indata);
return rc;
}
rc = gcry_sexp_new (&s_pkey, app->app_local->pk[2].key,
app->app_local->pk[2].keylen, 0);
if (rc)
{
gcry_sexp_release (s_hash);
gcry_sexp_release (s_sig);
xfree ((void *)indata);
return rc;
}
rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
gcry_sexp_release (s_hash);
gcry_sexp_release (s_sig);
gcry_sexp_release (s_pkey);
xfree ((void *)indata);
}
}
return rc;
}

View File

@ -2053,7 +2053,7 @@ app_auth (card_t card, ctrl_t ctrl, const char *keyidstr,
{
gpg_error_t err;
if (!indata || !indatalen || !outdata || !outdatalen || !pincb)
if (!outdata || !outdatalen || !pincb)
return gpg_error (GPG_ERR_INV_VALUE);
if ((err = maybe_switch_app (ctrl, card, keyidstr)))

View File

@ -41,6 +41,7 @@
#endif
#include "../common/asshelp.h"
#include "../common/server-help.h"
#include "../common/ssh-utils.h"
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That
* length needs to small compared to the maximum Assuan line length. */
@ -1074,7 +1075,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
static const char hlp_pkauth[] =
"PKAUTH <hexified_id>";
"PKAUTH [--challenge-response] <hexified_id>";
static gpg_error_t
cmd_pkauth (assuan_context_t ctx, char *line)
{
@ -1085,11 +1086,17 @@ cmd_pkauth (assuan_context_t ctx, char *line)
char *keyidstr;
card_t card;
const char *keygrip = NULL;
int challenge_response = 0;
if ((rc = open_card (ctrl)))
return rc;
/* We have to use a copy of the key ID because the function may use
if (has_option (line, "--challenge-response"))
challenge_response = 1;
line = skip_options (line);
/* We have to use a copy of the key ID because the function may use
the pin_cb which in turn uses the assuan line buffer and thus
overwriting the original line with the keyid */
keyidstr = xtrystrdup (line);
@ -1101,6 +1108,13 @@ cmd_pkauth (assuan_context_t ctx, char *line)
if (strlen (keyidstr) == 40)
keygrip = keyidstr;
if (challenge_response)
{
xfree (ctrl->in_data.value);
ctrl->in_data.value = NULL;
ctrl->in_data.valuelen = 0;
}
card = card_get (ctrl, keygrip);
if (card)
{