summaryrefslogtreecommitdiff
path: root/cipher/ecc.c
diff options
context:
space:
mode:
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2013-10-15 23:56:44 +0400
committerWerner Koch <wk@gnupg.org>2013-10-16 16:31:07 +0200
commit83902f1f1dbc8263a0c3f61be59cd2eb95293c97 (patch)
tree44104dabba10787887e717ddf86d98a83a9dda02 /cipher/ecc.c
parent187b2bb541b985255aee262d181434a7cb4ae2e7 (diff)
downloadlibgcrypt-83902f1f1dbc8263a0c3f61be59cd2eb95293c97.tar.gz
ecc: Add support for GOST R 34.10-2001/-2012 signatures
* src/cipher.h: define PUBKEY_FLAG_GOST * cipher/ecc-curves.c: Add GOST2001-test and GOST2012-test curves defined in standards. Typical applications would use either those curves, or curves defined in RFC 4357 (will be added later). * cipher/ecc.c (sign_gost, verify_gost): New. (ecc_sign, ecc_verify): use sign_gost/verify_gost if PUBKEY_FLAG_GOST is set. (ecc_names): add "gost" for gost signatures. * cipher/pubkey-util.c (_gcry_pk_util_parse_flaglist, _gcry_pk_util_preparse_sigval): set PUBKEY_FLAG_GOST if gost flag is present in s-exp. * tests/benchmark.c (ecc_bench): also benchmark GOST signatures. * tests/basic.c (check_pubkey): add two public keys from GOST R 34.10-2012 standard. (check_pubkey_sign_ecdsa): add two data sets to check gost signatures. * tests/curves.c: correct N_CURVES as we now have 2 more curves. Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> Removed some comments from the new curve definitions in ecc-curves.c to avoid line wrapping. Eventually we will develop a precompiler to avoid parsing those hex strings. -wk
Diffstat (limited to 'cipher/ecc.c')
-rw-r--r--cipher/ecc.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/cipher/ecc.c b/cipher/ecc.c
index 1323d00a..8b61ae4d 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -71,6 +71,7 @@ static const char *ecc_names[] =
"ecdsa",
"ecdh",
"eddsa",
+ "gost",
NULL,
};
@@ -575,6 +576,203 @@ verify_ecdsa (gcry_mpi_t input, ECC_public_key *pkey,
return err;
}
+/* Compute an GOST R 34.10-01/-12 signature.
+ * Return the signature struct (r,s) from the message hash. The caller
+ * must have allocated R and S.
+ */
+static gpg_err_code_t
+sign_gost (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s)
+{
+ gpg_err_code_t err = 0;
+ gcry_mpi_t k, dr, sum, ke, x, e;
+ mpi_point_struct I;
+ gcry_mpi_t hash;
+ const void *abuf;
+ unsigned int abits, qbits;
+ mpi_ec_t ctx;
+
+ if (DBG_CIPHER)
+ log_mpidump ("gost sign hash ", input );
+
+ qbits = mpi_get_nbits (skey->E.n);
+
+ /* Convert the INPUT into an MPI if needed. */
+ if (mpi_is_opaque (input))
+ {
+ abuf = gcry_mpi_get_opaque (input, &abits);
+ err = gpg_err_code (gcry_mpi_scan (&hash, GCRYMPI_FMT_USG,
+ abuf, (abits+7)/8, NULL));
+ if (err)
+ return err;
+ if (abits > qbits)
+ gcry_mpi_rshift (hash, hash, abits - qbits);
+ }
+ else
+ hash = input;
+
+
+ k = NULL;
+ dr = mpi_alloc (0);
+ sum = mpi_alloc (0);
+ ke = mpi_alloc (0);
+ e = mpi_alloc (0);
+ x = mpi_alloc (0);
+ point_init (&I);
+
+ ctx = _gcry_mpi_ec_p_internal_new (skey->E.model, skey->E.dialect,
+ skey->E.p, skey->E.a, skey->E.b);
+
+ mpi_mod (e, input, skey->E.n); /* e = hash mod n */
+
+ if (!mpi_cmp_ui (e, 0))
+ mpi_set_ui (e, 1);
+
+ /* Two loops to avoid R or S are zero. This is more of a joke than
+ a real demand because the probability of them being zero is less
+ than any hardware failure. Some specs however require it. */
+ do
+ {
+ do
+ {
+ mpi_free (k);
+ k = _gcry_dsa_gen_k (skey->E.n, GCRY_STRONG_RANDOM);
+
+ _gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx);
+ if (_gcry_mpi_ec_get_affine (x, NULL, &I, ctx))
+ {
+ if (DBG_CIPHER)
+ log_debug ("ecc sign: Failed to get affine coordinates\n");
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ mpi_mod (r, x, skey->E.n); /* r = x mod n */
+ }
+ while (!mpi_cmp_ui (r, 0));
+ mpi_mulm (dr, skey->d, r, skey->E.n); /* dr = d*r mod n */
+ mpi_mulm (ke, k, e, skey->E.n); /* ke = k*e mod n */
+ mpi_addm (s, ke, dr, skey->E.n); /* sum = (k*e+ d*r) mod n */
+ }
+ while (!mpi_cmp_ui (s, 0));
+
+ if (DBG_CIPHER)
+ {
+ log_mpidump ("gost sign result r ", r);
+ log_mpidump ("gost sign result s ", s);
+ }
+
+ leave:
+ _gcry_mpi_ec_free (ctx);
+ point_free (&I);
+ mpi_free (x);
+ mpi_free (e);
+ mpi_free (ke);
+ mpi_free (sum);
+ mpi_free (dr);
+ mpi_free (k);
+
+ if (hash != input)
+ mpi_free (hash);
+
+ return err;
+}
+
+/* Verify a GOST R 34.10-01/-12 signature.
+ * Check if R and S verifies INPUT.
+ */
+static gpg_err_code_t
+verify_gost (gcry_mpi_t input, ECC_public_key *pkey,
+ gcry_mpi_t r, gcry_mpi_t s)
+{
+ gpg_err_code_t err = 0;
+ gcry_mpi_t e, x, z1, z2, v, rv, zero;
+ mpi_point_struct Q, Q1, Q2;
+ mpi_ec_t ctx;
+
+ if( !(mpi_cmp_ui (r, 0) > 0 && mpi_cmp (r, pkey->E.n) < 0) )
+ return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < r < n failed. */
+ if( !(mpi_cmp_ui (s, 0) > 0 && mpi_cmp (s, pkey->E.n) < 0) )
+ return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */
+
+ x = mpi_alloc (0);
+ e = mpi_alloc (0);
+ z1 = mpi_alloc (0);
+ z2 = mpi_alloc (0);
+ v = mpi_alloc (0);
+ rv = mpi_alloc (0);
+ zero = mpi_alloc (0);
+
+ point_init (&Q);
+ point_init (&Q1);
+ point_init (&Q2);
+
+ ctx = _gcry_mpi_ec_p_internal_new (pkey->E.model, pkey->E.dialect,
+ pkey->E.p, pkey->E.a, pkey->E.b);
+
+ mpi_mod (e, input, pkey->E.n); /* e = hash mod n */
+ if (!mpi_cmp_ui (e, 0))
+ mpi_set_ui (e, 1);
+ mpi_invm (v, e, pkey->E.n); /* v = e^(-1) (mod n) */
+ mpi_mulm (z1, s, v, pkey->E.n); /* z1 = s*v (mod n) */
+ mpi_mulm (rv, r, v, pkey->E.n); /* rv = s*v (mod n) */
+ mpi_subm (z2, zero, rv, pkey->E.n); /* z2 = -r*v (mod n) */
+
+ _gcry_mpi_ec_mul_point (&Q1, z1, &pkey->E.G, ctx);
+/* log_mpidump ("Q1.x", Q1.x); */
+/* log_mpidump ("Q1.y", Q1.y); */
+/* log_mpidump ("Q1.z", Q1.z); */
+ _gcry_mpi_ec_mul_point (&Q2, z2, &pkey->Q, ctx);
+/* log_mpidump ("Q2.x", Q2.x); */
+/* log_mpidump ("Q2.y", Q2.y); */
+/* log_mpidump ("Q2.z", Q2.z); */
+ _gcry_mpi_ec_add_points (&Q, &Q1, &Q2, ctx);
+/* log_mpidump (" Q.x", Q.x); */
+/* log_mpidump (" Q.y", Q.y); */
+/* log_mpidump (" Q.z", Q.z); */
+
+ if (!mpi_cmp_ui (Q.z, 0))
+ {
+ if (DBG_CIPHER)
+ log_debug ("ecc verify: Rejected\n");
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ if (_gcry_mpi_ec_get_affine (x, NULL, &Q, ctx))
+ {
+ if (DBG_CIPHER)
+ log_debug ("ecc verify: Failed to get affine coordinates\n");
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ mpi_mod (x, x, pkey->E.n); /* x = x mod E_n */
+ if (mpi_cmp (x, r)) /* x != r */
+ {
+ if (DBG_CIPHER)
+ {
+ log_mpidump (" x", x);
+ log_mpidump (" r", r);
+ log_mpidump (" s", s);
+ log_debug ("ecc verify: Not verified\n");
+ }
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ if (DBG_CIPHER)
+ log_debug ("ecc verify: Accepted\n");
+
+ leave:
+ _gcry_mpi_ec_free (ctx);
+ point_free (&Q2);
+ point_free (&Q1);
+ point_free (&Q);
+ mpi_free (zero);
+ mpi_free (rv);
+ mpi_free (v);
+ mpi_free (z2);
+ mpi_free (z1);
+ mpi_free (x);
+ mpi_free (e);
+ return err;
+}
static void
@@ -1623,6 +1821,13 @@ ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
rc = gcry_sexp_build (r_sig, NULL,
"(sig-val(eddsa(r%M)(s%M)))", sig_r, sig_s);
}
+ else if ((ctx.flags & PUBKEY_FLAG_GOST))
+ {
+ rc = sign_gost (data, &sk, sig_r, sig_s);
+ if (!rc)
+ rc = gcry_sexp_build (r_sig, NULL,
+ "(sig-val(gost(r%M)(s%M)))", sig_r, sig_s);
+ }
else
{
rc = sign_ecdsa (data, &sk, sig_r, sig_s, ctx.flags, ctx.hash_algo);
@@ -1773,6 +1978,15 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
{
rc = verify_eddsa (data, &pk, sig_r, sig_s, ctx.hash_algo, mpi_q);
}
+ else if ((sigflags & PUBKEY_FLAG_GOST))
+ {
+ point_init (&pk.Q);
+ rc = _gcry_ecc_os2ec (&pk.Q, mpi_q);
+ if (rc)
+ goto leave;
+
+ rc = verify_gost (data, &pk, sig_r, sig_s);
+ }
else
{
point_init (&pk.Q);