summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorNIIBE Yutaka <gniibe@fsij.org>2022-03-03 09:45:49 +0100
committerNIIBE Yutaka <gniibe@fsij.org>2022-03-03 09:45:49 +0100
commit44621120a269ba67408fe1ea067af8cbd1cbb35e (patch)
treee3933c6c5f69a9cceda3f924911f0a0627d8faad /scd
parentg10/encrypt: use iobuf_copy instead of manual iobuf_read/iobuf_write (diff)
downloadgnupg2-44621120a269ba67408fe1ea067af8cbd1cbb35e.tar.xz
gnupg2-44621120a269ba67408fe1ea067af8cbd1cbb35e.zip
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>
Diffstat (limited to 'scd')
-rw-r--r--scd/app-openpgp.c207
-rw-r--r--scd/app.c2
-rw-r--r--scd/command.c18
3 files changed, 201 insertions, 26 deletions
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 76a15d00e..05e1f3977 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -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;
}
diff --git a/scd/app.c b/scd/app.c
index 5fcf8fb87..2de4f129c 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -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)))
diff --git a/scd/command.c b/scd/command.c
index dfd1ee538..392b678c4 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -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)
{