summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVitezslav Cizek <vcizek@suse.com>2015-10-27 14:29:11 +0100
committerWerner Koch <wk@gnupg.org>2016-03-18 15:26:28 +0100
commita242e3d9185e6e2dc13902ea9331131755bbba01 (patch)
tree11679a351ba6f54edf67fc395bf1d3d5ff80f7c5
parente40939b2141306238cc30a340b867b60fa4dc2a3 (diff)
downloadlibgcrypt-a242e3d9185e6e2dc13902ea9331131755bbba01.tar.gz
ecc: ECDSA adjustments for FIPS 186-4
* cipher/ecc-curves.c: Unmark curve P-192 for FIPS. * cipher/ecc.c: Add ECDSA self test. * cipher/pubkey-util.c (_gcry_pk_util_init_encoding_ctx): Use SHA-2 in FIPS mode. * tests/fipsdrv.c: Add support for ECDSA signatures. -- Enable ECC in FIPS mode. According to NIST SP 800-131A, curve P-192 and SHA-1 are disallowed for key pair generation and signature generation after 2013. Thanks to Jan Matejek for the patch. Signed-off-by: Vitezslav Cizek <vcizek@suse.com> Minor source code re-formatting by -wk.
-rw-r--r--cipher/ecc-curves.c2
-rw-r--r--cipher/ecc.c171
-rw-r--r--cipher/pubkey-util.c9
-rw-r--r--src/fips.c2
-rw-r--r--tests/fipsdrv.c262
5 files changed, 436 insertions, 10 deletions
diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c
index a74501db..91f29cc7 100644
--- a/cipher/ecc-curves.c
+++ b/cipher/ecc-curves.c
@@ -160,7 +160,7 @@ static const ecc_domain_parms_t domain_parms[] =
},
#endif /*0*/
{
- "NIST P-192", 192, 1,
+ "NIST P-192", 192, 0,
MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD,
"0xfffffffffffffffffffffffffffffffeffffffffffffffff",
"0xfffffffffffffffffffffffffffffffefffffffffffffffc",
diff --git a/cipher/ecc.c b/cipher/ecc.c
index f65203f5..5b035306 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -73,6 +73,25 @@ static const char *ecc_names[] =
};
+/* Sample NIST P-256 key from RFC 6979 A.2.5 */
+static const char sample_public_key_secp256[] =
+ "(public-key"
+ " (ecc"
+ " (curve secp256r1)"
+ " (q #04"
+ /**/ "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6"
+ /**/ "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
+
+static const char sample_secret_key_secp256[] =
+ "(private-key"
+ " (ecc"
+ " (curve secp256r1)"
+ " (d #C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721#)"
+ " (q #04"
+ /**/ "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6"
+ /**/ "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
+
+
/* Registered progress function and its callback value. */
static void (*progress_cb) (void *, const char*, int, int, int);
static void *progress_cb_data;
@@ -1956,23 +1975,165 @@ _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
Self-test section.
*/
+static const char *
+selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey)
+{
+ /* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */
+ static const char sample_data[] =
+ "(data (flags rfc6979)"
+ " (hash sha256 #af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e98915"
+ /**/ "62113d8a62add1bf#))";
+ static const char sample_data_bad[] =
+ "(data (flags rfc6979)"
+ " (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e98915"
+ /**/ "62113d8a62add1bf#))";
+ static const char signature_r[] =
+ "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716";
+ static const char signature_s[] =
+ "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8";
+
+ const char *errtxt = NULL;
+ gcry_error_t err;
+ gcry_sexp_t data = NULL;
+ gcry_sexp_t data_bad = NULL;
+ gcry_sexp_t sig = NULL;
+ gcry_sexp_t l1 = NULL;
+ gcry_sexp_t l2 = NULL;
+ gcry_mpi_t r = NULL;
+ gcry_mpi_t s = NULL;
+ gcry_mpi_t calculated_r = NULL;
+ gcry_mpi_t calculated_s = NULL;
+ int cmp;
+
+ err = sexp_sscan (&data, NULL, sample_data, strlen (sample_data));
+ if (!err)
+ err = sexp_sscan (&data_bad, NULL,
+ sample_data_bad, strlen (sample_data_bad));
+ if (!err)
+ err = _gcry_mpi_scan (&r, GCRYMPI_FMT_HEX, signature_r, 0, NULL);
+ if (!err)
+ err = _gcry_mpi_scan (&s, GCRYMPI_FMT_HEX, signature_s, 0, NULL);
+
+ if (err)
+ {
+ errtxt = "converting data failed";
+ goto leave;
+ }
+
+ err = _gcry_pk_sign (&sig, data, skey);
+ if (err)
+ {
+ errtxt = "signing failed";
+ goto leave;
+ }
+
+ /* check against known signature */
+ errtxt = "signature validity failed";
+ l1 = _gcry_sexp_find_token (sig, "sig-val", 0);
+ if (!l1)
+ goto leave;
+ l2 = _gcry_sexp_find_token (l1, "ecdsa", 0);
+ if (!l2)
+ goto leave;
+
+ sexp_release (l1);
+ l1 = l2;
+
+ l2 = _gcry_sexp_find_token (l1, "r", 0);
+ if (!l2)
+ goto leave;
+ calculated_r = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ if (!calculated_r)
+ goto leave;
+
+ l2 = _gcry_sexp_find_token (l1, "s", 0);
+ if (!l2)
+ goto leave;
+ calculated_s = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ if (!calculated_s)
+ goto leave;
+
+ errtxt = "known sig check failed";
+
+ cmp = _gcry_mpi_cmp (r, calculated_r);
+ if (cmp)
+ goto leave;
+ cmp = _gcry_mpi_cmp (s, calculated_s);
+ if (cmp)
+ goto leave;
+
+ errtxt = NULL;
+
+ /* verify generated signature */
+ err = _gcry_pk_verify (sig, data, pkey);
+ if (err)
+ {
+ errtxt = "verify failed";
+ goto leave;
+ }
+ err = _gcry_pk_verify (sig, data_bad, pkey);
+ if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
+ {
+ errtxt = "bad signature not detected";
+ goto leave;
+ }
+
+
+ leave:
+ sexp_release (sig);
+ sexp_release (data_bad);
+ sexp_release (data);
+ sexp_release (l1);
+ sexp_release (l2);
+ mpi_release (r);
+ mpi_release (s);
+ mpi_release (calculated_r);
+ mpi_release (calculated_s);
+ return errtxt;
+}
+
static gpg_err_code_t
selftests_ecdsa (selftest_report_func_t report)
{
const char *what;
const char *errtxt;
+ gcry_error_t err;
+ gcry_sexp_t skey = NULL;
+ gcry_sexp_t pkey = NULL;
+
+ what = "convert";
+ err = sexp_sscan (&skey, NULL, sample_secret_key_secp256,
+ strlen (sample_secret_key_secp256));
+ if (!err)
+ err = sexp_sscan (&pkey, NULL, sample_public_key_secp256,
+ strlen (sample_public_key_secp256));
+ if (err)
+ {
+ errtxt = _gcry_strerror (err);
+ goto failed;
+ }
+
+ what = "key consistency";
+ err = ecc_check_secret_key(skey);
+ if (err)
+ {
+ errtxt = _gcry_strerror (err);
+ goto failed;
+ }
- what = "low-level";
- errtxt = NULL; /*selftest ();*/
+ what = "sign";
+ errtxt = selftest_sign (pkey, skey);
if (errtxt)
goto failed;
- /* FIXME: need more tests. */
-
+ sexp_release(pkey);
+ sexp_release(skey);
return 0; /* Succeeded. */
failed:
+ sexp_release(pkey);
+ sexp_release(skey);
if (report)
report ("pubkey", GCRY_PK_ECC, what, errtxt);
return GPG_ERR_SELFTEST_FAILED;
@@ -1996,7 +2157,7 @@ run_selftests (int algo, int extended, selftest_report_func_t report)
gcry_pk_spec_t _gcry_pubkey_spec_ecc =
{
- GCRY_PK_ECC, { 0, 0 },
+ GCRY_PK_ECC, { 0, 1 },
(GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR),
"ECC", ecc_names,
"pabgnhq", "pabgnhqd", "sw", "rs", "pabgnhq",
diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c
index d0d6003a..76d39232 100644
--- a/cipher/pubkey-util.c
+++ b/cipher/pubkey-util.c
@@ -617,7 +617,14 @@ _gcry_pk_util_init_encoding_ctx (struct pk_encoding_ctx *ctx,
ctx->nbits = nbits;
ctx->encoding = PUBKEY_ENC_UNKNOWN;
ctx->flags = 0;
- ctx->hash_algo = GCRY_MD_SHA1;
+ if (fips_mode ())
+ {
+ ctx->hash_algo = GCRY_MD_SHA256;
+ }
+ else
+ {
+ ctx->hash_algo = GCRY_MD_SHA1;
+ }
ctx->label = NULL;
ctx->labellen = 0;
ctx->saltlen = 20;
diff --git a/src/fips.c b/src/fips.c
index edcbeacb..3311ba2c 100644
--- a/src/fips.c
+++ b/src/fips.c
@@ -549,7 +549,7 @@ run_pubkey_selftests (int extended)
{
GCRY_PK_RSA,
GCRY_PK_DSA,
- /* GCRY_PK_ECC is not enabled in fips mode. */
+ GCRY_PK_ECC,
0
};
int idx;
diff --git a/tests/fipsdrv.c b/tests/fipsdrv.c
index d7574201..b5962cf8 100644
--- a/tests/fipsdrv.c
+++ b/tests/fipsdrv.c
@@ -1721,6 +1721,33 @@ dsa_gen_with_seed (int keysize, const void *seed, size_t seedlen)
}
+/* Generate an ECDSA key on the specified curve and return the complete
+ S-expression. */
+static gcry_sexp_t
+ecdsa_gen_key (const char *curve)
+{
+ gpg_error_t err;
+ gcry_sexp_t keyspec, key;
+
+ err = gcry_sexp_build (&keyspec, NULL,
+ "(genkey"
+ " (ecc"
+ " (use-fips186)"
+ " (curve %s)))",
+ curve);
+ if (err)
+ die ("gcry_sexp_build failed for ECDSA key generation: %s\n",
+ gpg_strerror (err));
+ err = gcry_pk_genkey (&key, keyspec);
+ if (err)
+ die ("gcry_pk_genkey failed for ECDSA: %s\n", gpg_strerror (err));
+
+ gcry_sexp_release (keyspec);
+
+ return key;
+}
+
+
/* Print the domain parameter as well as the derive information. KEY
is the complete key as returned by dsa_gen. We print to stdout
with one parameter per line in hex format using this order: p, q,
@@ -1813,6 +1840,46 @@ print_dsa_domain_parameters (gcry_sexp_t key)
}
+/* Print public key Q (in octet-string format) and private key d.
+ KEY is the complete key as returned by ecdsa_gen_key.
+ with one parameter per line in hex format using this order: d, Q. */
+static void
+print_ecdsa_dq (gcry_sexp_t key)
+{
+ gcry_sexp_t l1, l2;
+ gcry_mpi_t mpi;
+ int idx;
+
+ l1 = gcry_sexp_find_token (key, "private-key", 0);
+ if (!l1)
+ die ("private key not found in genkey result\n");
+
+ l2 = gcry_sexp_find_token (l1, "ecc", 0);
+ if (!l2)
+ die ("returned private key not formed as expected\n");
+ gcry_sexp_release (l1);
+ l1 = l2;
+
+ /* Extract the parameters from the S-expression and print them to stdout. */
+ for (idx=0; "dq"[idx]; idx++)
+ {
+ l2 = gcry_sexp_find_token (l1, "dq"+idx, 1);
+ if (!l2)
+ die ("no %c parameter in returned public key\n", "dq"[idx]);
+ mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ if (!mpi)
+ die ("no value for %c parameter in returned private key\n","dq"[idx]);
+ gcry_sexp_release (l2);
+ if (standalone_mode)
+ printf ("%c = ", "dQ"[idx]);
+ print_mpi_line (mpi, 1);
+ gcry_mpi_release (mpi);
+ }
+
+ gcry_sexp_release (l1);
+}
+
+
/* Generate DSA domain parameters for a modulus size of KEYSIZE. The
result is printed to stdout with one parameter per line in hex
format and in this order: p, q, g, seed, counter, h. If SEED is
@@ -1992,6 +2059,138 @@ run_dsa_verify (const void *data, size_t datalen,
}
+
+/* Sign DATA of length DATALEN using the key taken from the S-expression
+ encoded KEYFILE. */
+static void
+run_ecdsa_sign (const void *data, size_t datalen,
+ const char *keyfile, const int algo)
+
+{
+ gpg_error_t err;
+ gcry_sexp_t s_data, s_key, s_sig, s_tmp;
+ char hash[128];
+ gcry_mpi_t tmpmpi;
+
+ s_key = read_sexp_from_file (keyfile);
+
+ gcry_md_hash_buffer (algo, hash, data, datalen);
+ err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash,
+ gcry_md_get_algo_dlen(algo), NULL);
+ if (!err)
+ {
+ err = gcry_sexp_build (&s_data, NULL,
+ "(data (flags raw)(hash %s %M))",
+ gcry_md_algo_name(algo), tmpmpi);
+ gcry_mpi_release (tmpmpi);
+ }
+ if (err)
+ die ("gcry_sexp_build failed for ECDSA data input: %s\n",
+ gpg_strerror (err));
+
+ err = gcry_pk_sign (&s_sig, s_data, s_key);
+ if (err)
+ {
+ die ("gcry_pk_signed failed: %s\n", gpg_strerror (err));
+ }
+ gcry_sexp_release (s_data);
+ gcry_sexp_release (s_key);
+
+ /* Now return the actual signature. */
+ s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
+ if (!s_tmp)
+ die ("no sig-val element in returned S-expression\n");
+
+ gcry_sexp_release (s_sig);
+ s_sig = s_tmp;
+ s_tmp = gcry_sexp_find_token (s_sig, "ecdsa", 0);
+ if (!s_tmp)
+ die ("no ecdsa element in returned S-expression\n");
+
+ gcry_sexp_release (s_sig);
+ s_sig = s_tmp;
+
+ s_tmp = gcry_sexp_find_token (s_sig, "r", 0);
+ tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG);
+ if (!tmpmpi)
+ die ("no r parameter in returned S-expression\n");
+ print_mpi_line (tmpmpi, 1);
+ gcry_mpi_release (tmpmpi);
+ gcry_sexp_release (s_tmp);
+
+ s_tmp = gcry_sexp_find_token (s_sig, "s", 0);
+ tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG);
+ if (!tmpmpi)
+ die ("no s parameter in returned S-expression\n");
+ print_mpi_line (tmpmpi, 1);
+ gcry_mpi_release (tmpmpi);
+ gcry_sexp_release (s_tmp);
+
+ gcry_sexp_release (s_sig);
+}
+
+
+
+/* Verify DATA of length DATALEN using the public key taken from the
+ S-expression in KEYFILE against the S-expression formatted
+ signature in SIGFILE. */
+static void
+run_ecdsa_verify (const void *data, size_t datalen,
+ const char *keyfile, const int algo, const char *sigfile)
+
+{
+ gpg_error_t err;
+ gcry_sexp_t s_data, s_key, s_sig;
+ char hash[128];
+ gcry_mpi_t tmpmpi;
+
+ s_key = read_sexp_from_file (keyfile);
+
+ gcry_md_hash_buffer (algo, hash, data, datalen);
+ /* Note that we can't simply use %b with HASH to build the
+ S-expression, because that might yield a negative value. */
+ err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash,
+ gcry_md_get_algo_dlen(algo), NULL);
+ if (!err)
+ {
+ err = gcry_sexp_build (&s_data, NULL,
+ "(data (flags raw)(hash %s %M))",
+ gcry_md_algo_name(algo), tmpmpi);
+ gcry_mpi_release (tmpmpi);
+ }
+ if (err)
+ die ("gcry_sexp_build failed for DSA data input: %s\n",
+ gpg_strerror (err));
+
+ s_sig = read_sexp_from_file (sigfile);
+
+ err = gcry_pk_verify (s_sig, s_data, s_key);
+ if (!err)
+ puts ("GOOD signature");
+ else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE)
+ puts ("BAD signature");
+ else
+ printf ("ERROR (%s)\n", gpg_strerror (err));
+
+ gcry_sexp_release (s_sig);
+ gcry_sexp_release (s_key);
+ gcry_sexp_release (s_data);
+}
+
+
+/* Generate an ECDSA key with specified domain parameters
+ and print the d and Q values, in the standard octet-string format. */
+static void
+run_ecdsa_gen_key (const char *curve)
+{
+ gcry_sexp_t key;
+
+ key = ecdsa_gen_key (curve);
+ print_ecdsa_dq (key);
+
+ gcry_sexp_release (key);
+}
+
static void
@@ -2008,7 +2207,8 @@ usage (int show_help)
"Run a crypto operation using hex encoded input and output.\n"
"MODE:\n"
" encrypt, decrypt, digest, random, hmac-sha,\n"
- " rsa-{derive,gen,sign,verify}, dsa-{pqg-gen,gen,sign,verify}\n"
+ " rsa-{derive,gen,sign,verify},\n"
+ " dsa-{pqg-gen,gen,sign,verify}, ecdsa-{gen-key,sign,verify}\n"
"OPTIONS:\n"
" --verbose Print additional information\n"
" --binary Input and output is in binary form\n"
@@ -2017,6 +2217,7 @@ usage (int show_help)
" --iv IV Use the hex encoded IV\n"
" --dt DT Use the hex encoded DT for the RNG\n"
" --algo NAME Use algorithm NAME\n"
+ " --curve NAME Select ECC curve spec NAME\n"
" --keysize N Use a keysize of N bits\n"
" --signature NAME Take signature from file NAME\n"
" --chunk N Read in chunks of N bytes (implies --binary)\n"
@@ -2039,6 +2240,7 @@ main (int argc, char **argv)
int progress = 0;
int use_pkcs1 = 0;
const char *mode_string;
+ const char *curve_string = NULL;
const char *key_string = NULL;
const char *iv_string = NULL;
const char *dt_string = NULL;
@@ -2154,6 +2356,14 @@ main (int argc, char **argv)
binary_input = binary_output = 1;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--curve"))
+ {
+ argc--; argv++;
+ if (!argc)
+ usage (0);
+ curve_string = *argv;
+ argc--; argv++;
+ }
else if (!strcmp (*argv, "--pkcs1"))
{
use_pkcs1 = 1;
@@ -2211,7 +2421,8 @@ main (int argc, char **argv)
&& !mct_server
&& strcmp (mode_string, "random")
&& strcmp (mode_string, "rsa-gen")
- && strcmp (mode_string, "dsa-gen") )
+ && strcmp (mode_string, "dsa-gen")
+ && strcmp (mode_string, "ecdsa-gen-key") )
{
data = read_file (input, !binary_input, &datalen);
if (!data)
@@ -2501,6 +2712,53 @@ main (int argc, char **argv)
run_dsa_verify (data, datalen, key_string, signature_string);
}
+ else if (!strcmp (mode_string, "ecdsa-gen-key"))
+ {
+ if (!curve_string)
+ die ("option --curve containing name of the specified curve is required in this mode\n");
+ run_ecdsa_gen_key (curve_string);
+ }
+ else if (!strcmp (mode_string, "ecdsa-sign"))
+ {
+ int algo;
+
+ if (!key_string)
+ die ("option --key is required in this mode\n");
+ if (access (key_string, R_OK))
+ die ("option --key needs to specify an existing keyfile\n");
+ if (!algo_string)
+ die ("use --algo to specify the digest algorithm\n");
+ algo = gcry_md_map_name (algo_string);
+ if (!algo)
+ die ("digest algorithm `%s' is not supported\n", algo_string);
+
+ if (!data)
+ die ("no data available (do not use --chunk)\n");
+
+ run_ecdsa_sign (data, datalen, key_string, algo);
+ }
+ else if (!strcmp (mode_string, "ecdsa-verify"))
+ {
+ int algo;
+
+ if (!key_string)
+ die ("option --key is required in this mode\n");
+ if (access (key_string, R_OK))
+ die ("option --key needs to specify an existing keyfile\n");
+ if (!algo_string)
+ die ("use --algo to specify the digest algorithm\n");
+ algo = gcry_md_map_name (algo_string);
+ if (!algo)
+ die ("digest algorithm `%s' is not supported\n", algo_string);
+ if (!data)
+ die ("no data available (do not use --chunk)\n");
+ if (!signature_string)
+ die ("option --signature is required in this mode\n");
+ if (access (signature_string, R_OK))
+ die ("option --signature needs to specify an existing file\n");
+
+ run_ecdsa_verify (data, datalen, key_string, algo, signature_string);
+ }
else
usage (0);