Index: b/cipher/ecc-curves.c =================================================================== --- b/cipher/ecc-curves.c +++ b/cipher/ecc-curves.c @@ -147,7 +147,7 @@ }, #endif /*0*/ { - "NIST P-192", 192, 1, + "NIST P-192", 192, 0, MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD, "0xfffffffffffffffffffffffffffffffeffffffffffffffff", "0xfffffffffffffffffffffffffffffffefffffffffffffffc", Index: b/cipher/ecc.c =================================================================== --- b/cipher/ecc.c +++ b/cipher/ecc.c @@ -73,6 +73,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; @@ -1717,23 +1732,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; @@ -1757,7 +1910,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, "pabgnhq", "pabgnhqd", "sw", "rs", "pabgnhq", Index: b/cipher/pubkey-util.c =================================================================== --- b/cipher/pubkey-util.c +++ b/cipher/pubkey-util.c @@ -603,7 +603,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: b/src/fips.c =================================================================== --- b/src/fips.c +++ b/src/fips.c @@ -545,7 +545,7 @@ { GCRY_PK_RSA, GCRY_PK_DSA, - /* GCRY_PK_ECC is not enabled in fips mode. */ + GCRY_PK_ECC, 0 }; int idx; Index: b/tests/fipsdrv.c =================================================================== --- b/tests/fipsdrv.c +++ b/tests/fipsdrv.c @@ -1726,6 +1726,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 @@ -1998,6 +2025,198 @@ } + +static int +ecdsa_hash_from_string(const char *algo_string) +{ + if (strncmp(algo_string, "SHA-", 4)) + die("algorithms other than the SHA family are unsupported\n"); + + switch(atoi(algo_string + 4)) + { + case 1: + return GCRY_MD_SHA1; + case 224: + return GCRY_MD_SHA224; + case 256: + return GCRY_MD_SHA256; + case 384: + return GCRY_MD_SHA384; + case 512: + return GCRY_MD_SHA512; + default: + die("unsupported SHA function\n"); + } + return GCRY_MD_NONE; +} + +/* 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 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); +} + + + + +/* 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 char *algo_string) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig, s_tmp, s_tmp2; + char hash[128]; + gcry_mpi_t tmpmpi; + int algo; + + s_key = read_sexp_from_file (keyfile); + algo = ecdsa_hash_from_string (algo_string); + + 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)(value %m))", tmpmpi); + gcry_mpi_release (tmpmpi); + } + if (err) + die ("gcry_sexp_build failed for ECDSA data input: %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 char *algo_string, const char *sigfile) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig; + char hash[128]; + gcry_mpi_t tmpmpi; + int algo; + + s_key = read_sexp_from_file (keyfile); + algo = ecdsa_hash_from_string(algo_string); + + 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)(value %m))", 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 @@ -2014,7 +2233,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-{pqg-gen,gen,sign,verify}\n" + " rsa-{derive,gen,sign,verify}, 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" @@ -2023,6 +2242,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" " --signature NAME Take signature from file NAME\n" " --chunk N Read in chunks of N bytes (implies --binary)\n" @@ -2045,6 +2265,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; @@ -2160,6 +2381,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; @@ -2217,7 +2446,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) @@ -2507,6 +2737,43 @@ 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")) + { + 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 hash algorithm\n"); + + if (!data) + die ("no data available (do not use --chunk)\n"); + + run_ecdsa_sign (data, datalen, key_string, algo_string); + } + else if (!strcmp (mode_string, "ecdsa-verify")) + { + 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 hash algorithm\n"); + 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_string, signature_string); + } else usage (0);