1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-05-31 22:18:03 +02:00

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_key_format_t format;
} rsa; } rsa;
struct { struct {
const char *curve; const char *curve; /* Canonical name defined in openpgp-oid.c */
int algo; int algo;
unsigned int flags; unsigned int flags;
} ecc; } 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 /* Compute a digital signature on INDATA which is expected to be the
raw message digest. For this application the KEYIDSTR consists of raw message digest. For this application the KEYIDSTR consists of
the serialnumber and the fingerprint delimited by a slash. 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, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ) 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; int rc;
unsigned char data[19+64]; unsigned char data[19+64];
size_t datalen; size_t datalen;
@ -5354,6 +5355,60 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
return rc; 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 /* Compute a digital signature using the INTERNAL AUTHENTICATE command
on INDATA which is expected to be the raw message digest. For this on INDATA which is expected to be the raw message digest. For this
application the KEYIDSTR consists of the serialnumber and the 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 ) unsigned char **outdata, size_t *outdatalen )
{ {
int rc; int rc;
int challenge_generated = 0;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE); 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 if (app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
&& indatalen > 101) /* For a 2048 bit key. */ && indatalen > 101) /* For a 2048 bit key. */
return gpg_error (GPG_ERR_INV_VALUE); 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. */ /* Check whether an OpenPGP card of any version has been requested. */
if (!ascii_strcasecmp (keyidstr, "OPENPGP.3")) if (!ascii_strcasecmp (keyidstr, "OPENPGP.3"))
; ;
@ -5436,6 +5508,95 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
outdata, outdatalen); outdata, outdatalen);
if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
clear_chv_status (app, ctrl, 1); 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; return rc;
} }

View File

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

View File

@ -41,6 +41,7 @@
#endif #endif
#include "../common/asshelp.h" #include "../common/asshelp.h"
#include "../common/server-help.h" #include "../common/server-help.h"
#include "../common/ssh-utils.h"
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That
* length needs to small compared to the maximum Assuan line length. */ * 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[] = static const char hlp_pkauth[] =
"PKAUTH <hexified_id>"; "PKAUTH [--challenge-response] <hexified_id>";
static gpg_error_t static gpg_error_t
cmd_pkauth (assuan_context_t ctx, char *line) cmd_pkauth (assuan_context_t ctx, char *line)
{ {
@ -1085,11 +1086,17 @@ cmd_pkauth (assuan_context_t ctx, char *line)
char *keyidstr; char *keyidstr;
card_t card; card_t card;
const char *keygrip = NULL; const char *keygrip = NULL;
int challenge_response = 0;
if ((rc = open_card (ctrl))) if ((rc = open_card (ctrl)))
return rc; 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 the pin_cb which in turn uses the assuan line buffer and thus
overwriting the original line with the keyid */ overwriting the original line with the keyid */
keyidstr = xtrystrdup (line); keyidstr = xtrystrdup (line);
@ -1101,6 +1108,13 @@ cmd_pkauth (assuan_context_t ctx, char *line)
if (strlen (keyidstr) == 40) if (strlen (keyidstr) == 40)
keygrip = keyidstr; 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); card = card_get (ctrl, keygrip);
if (card) if (card)
{ {