mirror of
git://git.gnupg.org/gnupg.git
synced 2024-10-31 20:08:43 +01: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:
parent
756c0bd5d8
commit
44621120a2
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)))
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user