File libgcrypt-fips_ecdsa.patch of Package libgcrypt.665
Index: libgcrypt-1.6.1/cipher/ecc-curves.c
===================================================================
--- libgcrypt-1.6.1.orig/cipher/ecc-curves.c 2014-01-29 10:48:38.000000000 +0100
+++ libgcrypt-1.6.1/cipher/ecc-curves.c 2015-03-16 15:32:31.284501952 +0100
@@ -114,7 +114,7 @@
"0x6666666666666666666666666666666666666666666666666666666666666658"
},
{
- "NIST P-192", 192, 1,
+ "NIST P-192", 192, 0,
MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD,
"0xfffffffffffffffffffffffffffffffeffffffffffffffff",
"0xfffffffffffffffffffffffffffffffefffffffffffffffc",
Index: libgcrypt-1.6.1/cipher/pubkey-util.c
===================================================================
--- libgcrypt-1.6.1.orig/cipher/pubkey-util.c 2013-12-16 18:44:32.000000000 +0100
+++ libgcrypt-1.6.1/cipher/pubkey-util.c 2015-03-16 15:32:31.285501958 +0100
@@ -593,7 +593,11 @@
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;
Index: libgcrypt-1.6.1/tests/fipsdrv.c
===================================================================
--- libgcrypt-1.6.1.orig/tests/fipsdrv.c 2015-03-16 15:32:31.238501672 +0100
+++ libgcrypt-1.6.1/tests/fipsdrv.c 2015-03-16 16:47:43.169441673 +0100
@@ -1764,6 +1764,33 @@
return key;
}
+/* 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
@@ -1900,6 +1927,46 @@
}
+/* 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 pq 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, seed, counter. If SEED is
@@ -1972,6 +2039,20 @@
}
+/* 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 int
dsa_hash_from_key(gcry_sexp_t s_key)
{
@@ -2161,6 +2242,120 @@
}
+
+/* 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, s_tmp2;
+ 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);
+}
static void
@@ -2177,7 +2372,7 @@
"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-{pq-gen,g-gen,gen,sign,verify}\n"
+ " rsa-{derive,gen,sign,verify}, dsa-{pq-gen,g-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"
@@ -2186,6 +2381,7 @@
" --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"
" --qize N Use a DSA q parameter size of N bits\n"
" --signature NAME Take signature from file NAME\n"
@@ -2209,6 +2405,7 @@
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;
@@ -2333,6 +2530,14 @@
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;
@@ -2390,7 +2595,8 @@
&& !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)
@@ -2706,6 +2912,53 @@
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);
Index: libgcrypt-1.6.1/src/fips.c
===================================================================
--- libgcrypt-1.6.1.orig/src/fips.c 2015-03-16 15:32:31.231501629 +0100
+++ libgcrypt-1.6.1/src/fips.c 2015-03-16 15:32:31.285501958 +0100
@@ -557,7 +557,7 @@
{
GCRY_PK_RSA,
GCRY_PK_DSA,
- /* GCRY_PK_ECC is not enabled in fips mode. */
+ GCRY_PK_ECC,
0
};
int idx;
Index: libgcrypt-1.6.1/cipher/ecc.c
===================================================================
--- libgcrypt-1.6.1.orig/cipher/ecc.c 2014-01-29 10:48:38.000000000 +0100
+++ libgcrypt-1.6.1/cipher/ecc.c 2015-03-16 15:32:31.286501964 +0100
@@ -76,6 +76,21 @@
};
+/* Sample NIST P-256 key from RFC 6979 A.2.5 */
+static const char sample_public_key_secp256[] =
+"(public-key"
+" (ecc"
+" (curve secp256r1)"
+" (q #0460FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB67903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
+
+static const char sample_secret_key_secp256[] =
+"(private-key"
+" (ecc"
+" (curve secp256r1)"
+" (d #C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721#)"
+" (q #0460FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB67903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
+
+
/* Registered progress function and its callback value. */
static void (*progress_cb) (void *, const char*, int, int, int);
static void *progress_cb_data;
@@ -1702,23 +1717,161 @@
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 #af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf#))";
+ static const char sample_data_bad[] =
+ "(data (flags rfc6979)"
+ " (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf#))";
+ 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;
@@ -1742,7 +1895,7 @@
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,
"pabgnq", "pabgnqd", "sw", "rs", "pabgnq",