From: Dmitry Baryshkov dbaryshkov@gmail.com
Add GOST Digital Signature Algorithms support according to GOST R 34.10-2001/-2012. English translations of these standards are provided as RFC 5832 and RFC 7091.
Signed-off-by: Dmitry Baryshkov dbaryshkov@gmail.com --- Makefile.in | 4 +- ecc-gostdsa-sign.c | 101 +++++++++++++++++++++ ecc-gostdsa-verify.c | 130 +++++++++++++++++++++++++++ ecc-hash.c | 11 +++ ecc-internal.h | 7 ++ gostdsa-sign.c | 74 +++++++++++++++ gostdsa-verify.c | 78 ++++++++++++++++ gostdsa.h | 100 +++++++++++++++++++++ testsuite/.gitignore | 3 + testsuite/.test-rules.make | 9 ++ testsuite/Makefile.in | 4 +- testsuite/gostdsa-keygen-test.c | 155 ++++++++++++++++++++++++++++++++ testsuite/gostdsa-sign-test.c | 88 ++++++++++++++++++ testsuite/gostdsa-verify-test.c | 111 +++++++++++++++++++++++ 14 files changed, 873 insertions(+), 2 deletions(-) create mode 100644 ecc-gostdsa-sign.c create mode 100644 ecc-gostdsa-verify.c create mode 100644 gostdsa-sign.c create mode 100644 gostdsa-verify.c create mode 100644 gostdsa.h create mode 100644 testsuite/gostdsa-keygen-test.c create mode 100644 testsuite/gostdsa-sign-test.c create mode 100644 testsuite/gostdsa-verify-test.c
diff --git a/Makefile.in b/Makefile.in index eb1c6c335c39..f876e5e82197 100644 --- a/Makefile.in +++ b/Makefile.in @@ -189,6 +189,8 @@ hogweed_SOURCES = sexp.c sexp-format.c \ ecc-point.c ecc-scalar.c ecc-point-mul.c ecc-point-mul-g.c \ ecc-ecdsa-sign.c ecdsa-sign.c \ ecc-ecdsa-verify.c ecdsa-verify.c ecdsa-keygen.c \ + ecc-gostdsa-sign.c gostdsa-sign.c \ + ecc-gostdsa-verify.c gostdsa-verify.c \ curve25519-mul-g.c curve25519-mul.c curve25519-eh-to-x.c \ curve448-mul-g.c curve448-mul.c curve448-eh-to-x.c \ eddsa-compress.c eddsa-decompress.c eddsa-expand.c \ @@ -205,7 +207,7 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \ cbc.h ccm.h cfb.h chacha.h chacha-poly1305.h ctr.h \ curve25519.h curve448.h des.h dsa.h dsa-compat.h eax.h \ ecc-curve.h ecc.h ecdsa.h eddsa.h \ - gcm.h gost28147.h gosthash94.h hmac.h \ + gcm.h gost28147.h gostdsa.h gosthash94.h hmac.h \ knuth-lfib.h hkdf.h \ macros.h \ cmac.h siv-cmac.h \ diff --git a/ecc-gostdsa-sign.c b/ecc-gostdsa-sign.c new file mode 100644 index 000000000000..00eeef81f659 --- /dev/null +++ b/ecc-gostdsa-sign.c @@ -0,0 +1,101 @@ +/* ecc-gostdsa-sign.c + + Copyright (C) 2015 Dmitry Eremin-Solenikov + Copyright (C) 2013, 2014 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> + +#include "gostdsa.h" +#include "ecc-internal.h" + +/* Low-level GOST DSA signing */ + +mp_size_t +ecc_gostdsa_sign_itch (const struct ecc_curve *ecc) +{ + /* Needs 3*ecc->p.size + scratch for ecc->mul_g. Currently same for + ecc_mul_g and ecc_mul_g_eh. */ + return ECC_GOSTDSA_SIGN_ITCH (ecc->p.size); +} + +/* NOTE: Caller should check if r or s is zero. */ +void +ecc_gostdsa_sign (const struct ecc_curve *ecc, + const mp_limb_t *zp, + const mp_limb_t *kp, + size_t length, const uint8_t *digest, + mp_limb_t *rp, mp_limb_t *sp, + mp_limb_t *scratch) +{ +#define P scratch +#define hp (scratch + 4*ecc->p.size) +#define tp (scratch + 2*ecc->p.size) +#define t2p scratch + /* Procedure, according to GOST 34.10. q denotes the group + order. + + 1. k <-- uniformly random, 0 < k < q + + 2. C <-- (c_x, c_y) = k g + + 3. r <-- c_x mod q + + 4. s <-- (r*z + k*h) mod q. + */ + + ecc->mul_g (ecc, P, kp, P + 3*ecc->p.size); + /* x coordinate only, modulo q */ + ecc->h_to_a (ecc, 2, rp, P, P + 3*ecc->p.size); + + /* Process hash digest */ + gost_hash (&ecc->q, hp, length, digest); + if (mpn_zero_p (hp, ecc->p.size)) + mpn_add_1 (hp, hp, ecc->p.size, 1); + + ecc_modq_mul (ecc, tp, rp, zp); + ecc_modq_mul (ecc, t2p, kp, hp); + ecc_modq_add (ecc, sp, tp, t2p); + + /* Also reduce mod ecc->q. It should already be < 2*ecc->q, + * so one subtraction should suffice. */ + + *scratch = mpn_sub_n (tp, sp, ecc->q.m, ecc->p.size); + cnd_copy (*scratch == 0, sp, tp, ecc->p.size); + +#undef P +#undef hp +#undef tp +#undef t2p +} diff --git a/ecc-gostdsa-verify.c b/ecc-gostdsa-verify.c new file mode 100644 index 000000000000..4358132b2bf6 --- /dev/null +++ b/ecc-gostdsa-verify.c @@ -0,0 +1,130 @@ +/* ecc-gostdsa-verify.c + + Copyright (C) 2015 Dmitry Eremin-Solenikov + Copyright (C) 2013, 2014 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> + +#include "gostdsa.h" +#include "ecc-internal.h" + +/* Low-level GOST DSA verify */ + +static int +ecdsa_in_range (const struct ecc_curve *ecc, const mp_limb_t *xp) +{ + return !mpn_zero_p (xp, ecc->p.size) + && mpn_cmp (xp, ecc->q.m, ecc->p.size) < 0; +} + +mp_size_t +ecc_gostdsa_verify_itch (const struct ecc_curve *ecc) +{ + /* Largest storage need is for the ecc->mul call. */ + return 5*ecc->p.size + ecc->mul_itch; +} + +/* FIXME: Use faster primitives, not requiring side-channel silence. */ +int +ecc_gostdsa_verify (const struct ecc_curve *ecc, + const mp_limb_t *pp, /* Public key */ + size_t length, const uint8_t *digest, + const mp_limb_t *rp, const mp_limb_t *sp, + mp_limb_t *scratch) +{ + /* Procedure, according to GOST R 34.10. q denotes the group + order. + + 1. Check 0 < r, s < q. + + 2. v <-- h^{-1} (mod q) + + 3. z1 <-- s * v (mod q) + + 4. z2 <-- -r * v (mod q) + + 5. R = u1 G + u2 Y + + 6. Signature is valid if R_x = r (mod q). + */ + +#define hp (scratch) +#define vp (scratch + ecc->p.size) +#define z1 (scratch + 3*ecc->p.size) +#define z2 (scratch + 4*ecc->p.size) + +#define P1 (scratch + 4*ecc->p.size) +#define P2 (scratch) + + + if (! (ecdsa_in_range (ecc, rp) + && ecdsa_in_range (ecc, sp))) + return 0; + + gost_hash (&ecc->q, hp, length, digest); + + if (mpn_zero_p (hp, ecc->p.size)) + mpn_add_1 (hp, hp, ecc->p.size, 1); + + /* Compute v */ + ecc->q.invert (&ecc->q, vp, hp, vp + 2*ecc->p.size); + + /* z1 = s / h, P1 = z1 * G */ + ecc_modq_mul (ecc, z1, sp, vp); + + /* z2 = - r / h, P2 = z2 * Y */ + ecc_modq_mul (ecc, z2, rp, vp); + mpn_sub_n (z2, ecc->q.m, z2, ecc->p.size); + + /* Total storage: 5*ecc->p.size + ecc->mul_itch */ + ecc->mul (ecc, P2, z2, pp, z2 + ecc->p.size); + + /* Total storage: 7*ecc->p.size + ecc->mul_g_itch (ecc->p.size) */ + ecc->mul_g (ecc, P1, z1, P1 + 3*ecc->p.size); + + /* Total storage: 6*ecc->p.size + ecc->add_hhh_itch */ + ecc->add_hhh (ecc, P1, P1, P2, P1 + 3*ecc->p.size); + + /* x coordinate only, modulo q */ + ecc->h_to_a (ecc, 2, P2, P1, P1 + 3*ecc->p.size); + + return (mpn_cmp (rp, P2, ecc->p.size) == 0); +#undef P2 +#undef P1 +#undef z2 +#undef z1 +#undef hp +#undef vp +} diff --git a/ecc-hash.c b/ecc-hash.c index 4e830a514ac4..07877110263f 100644 --- a/ecc-hash.c +++ b/ecc-hash.c @@ -62,3 +62,14 @@ ecc_hash (const struct ecc_modulo *m, /* We got a few extra bits, at the low end. Discard them. */ mpn_rshift (hp, hp, m->size + 1, 8*length - m->bit_size); } + +void +gost_hash (const struct ecc_modulo *m, + mp_limb_t *hp, + size_t length, const uint8_t *digest) +{ + if (length > ((size_t) m->bit_size + 7) / 8) + length = (m->bit_size + 7) / 8; + + mpn_set_base256_le (hp, m->size + 1, digest, length); +} diff --git a/ecc-internal.h b/ecc-internal.h index cef1366545e0..0022e0ab6cc2 100644 --- a/ecc-internal.h +++ b/ecc-internal.h @@ -53,6 +53,7 @@ #define ecc_mod _nettle_ecc_mod #define ecc_mod_inv _nettle_ecc_mod_inv #define ecc_hash _nettle_ecc_hash +#define gost_hash _nettle_gost_hash #define ecc_a_to_j _nettle_ecc_a_to_j #define ecc_j_to_a _nettle_ecc_j_to_a #define ecc_eh_to_a _nettle_ecc_eh_to_a @@ -288,6 +289,11 @@ ecc_hash (const struct ecc_modulo *m, mp_limb_t *hp, size_t length, const uint8_t *digest);
+void +gost_hash (const struct ecc_modulo *m, + mp_limb_t *hp, + size_t length, const uint8_t *digest); + /* Converts a point P in affine coordinates into a point R in jacobian coordinates. */ void @@ -456,6 +462,7 @@ curve448_eh_to_x (mp_limb_t *xp, const mp_limb_t *p, #endif #define ECC_MUL_M_ITCH(size) (11*(size)) #define ECC_ECDSA_SIGN_ITCH(size) (12*(size)) +#define ECC_GOSTDSA_SIGN_ITCH(size) (12*(size)) #define ECC_MOD_RANDOM_ITCH(size) (size) #define ECC_HASH_ITCH(size) (1+(size))
diff --git a/gostdsa-sign.c b/gostdsa-sign.c new file mode 100644 index 000000000000..892c0742c898 --- /dev/null +++ b/gostdsa-sign.c @@ -0,0 +1,74 @@ +/* gostdsa-sign.c + + Copyright (C) 2015 Dmitry Eremin-Solenikov + Copyright (C) 2013 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> + +#include "gostdsa.h" +#include "ecc-internal.h" +#include "nettle-internal.h" + +void +gostdsa_sign (const struct ecc_scalar *key, + void *random_ctx, nettle_random_func *random, + size_t digest_length, + const uint8_t *digest, + struct dsa_signature *signature) +{ + /* At most 936 bytes. */ + TMP_DECL(k, mp_limb_t, ECC_MAX_SIZE + ECC_GOSTDSA_SIGN_ITCH (ECC_MAX_SIZE)); + mp_limb_t size = key->ecc->p.size; + mp_limb_t *rp = mpz_limbs_write (signature->r, size); + mp_limb_t *sp = mpz_limbs_write (signature->s, size); + + TMP_ALLOC (k, size + ECC_GOSTDSA_SIGN_ITCH (size)); + + /* Timing reveals the number of rounds through this loop, but the + timing is still independent of the secret k finally used. */ + do + { + do + { + ecc_mod_random (&key->ecc->q, k, random_ctx, random, k + size); + } + while (mpn_zero_p(k, size)); + ecc_gostdsa_sign (key->ecc, key->p, k, digest_length, digest, + rp, sp, k + size); + mpz_limbs_finish (signature->r, size); + mpz_limbs_finish (signature->s, size); + } + while (mpz_sgn (signature->r) == 0 || mpz_sgn (signature->s) == 0); +} diff --git a/gostdsa-verify.c b/gostdsa-verify.c new file mode 100644 index 000000000000..7dc1bec1ef62 --- /dev/null +++ b/gostdsa-verify.c @@ -0,0 +1,78 @@ +/* gostdsa-verify.c + + Copyright (C) 2015 Dmitry Eremin-Solenikov + Copyright (C) 2013 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> + +#include "gostdsa.h" + +#include "gmp-glue.h" + +int +gostdsa_verify (const struct ecc_point *pub, + size_t length, const uint8_t *digest, + const struct dsa_signature *signature) +{ + mp_limb_t size = ecc_size (pub->ecc); + mp_size_t itch = 2*size + ecc_gostdsa_verify_itch (pub->ecc); + /* For ECC_MUL_A_WBITS == 0, at most 1512 bytes. With + ECC_MUL_A_WBITS == 4, currently needs 67 * ecc->size, at most + 4824 bytes. Don't use stack allocation for this. */ + mp_limb_t *scratch; + int res; + +#define rp scratch +#define sp (scratch + size) +#define scratch_out (scratch + 2*size) + + if (mpz_sgn (signature->r) <= 0 || mpz_size (signature->r) > size + || mpz_sgn (signature->s) <= 0 || mpz_size (signature->s) > size) + return 0; + + scratch = gmp_alloc_limbs (itch); + + mpz_limbs_copy (rp, signature->r, size); + mpz_limbs_copy (sp, signature->s, size); + + res = ecc_gostdsa_verify (pub->ecc, pub->p, length, digest, rp, sp, scratch_out); + + gmp_free_limbs (scratch, itch); + + return res; +#undef rp +#undef sp +#undef scratch_out +} diff --git a/gostdsa.h b/gostdsa.h new file mode 100644 index 000000000000..c92dfd1e1dd6 --- /dev/null +++ b/gostdsa.h @@ -0,0 +1,100 @@ +/* gostdsa.h + + Copyright (C) 2015 Dmity Eremin-Solenikov + Copyright (C) 2013 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_GOSTDSA_H_INCLUDED +#define NETTLE_GOSTDSA_H_INCLUDED + +#include "ecc.h" +#include "dsa.h" +#include "ecdsa.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Name mangling */ +#define gostdsa_sign nettle_gostdsa_sign +#define gostdsa_verify nettle_gostdsa_verify +#define ecc_gostdsa_sign nettle_ecc_gostdsa_sign +#define ecc_gostdsa_sign_itch nettle_ecc_gostdsa_sign_itch +#define ecc_gostdsa_verify nettle_ecc_gostdsa_verify +#define ecc_gostdsa_verify_itch nettle_ecc_gostdsa_verify_itch + +/* Just use ECDSA function for key generation */ +#define gostdsa_generate_keypair ecdsa_generate_keypair + +/* High level GOST DSA functions. + * + * A public key is represented as a struct ecc_point, and a private + * key as a struct ecc_scalar. FIXME: Introduce some aliases? */ +void +gostdsa_sign (const struct ecc_scalar *key, + void *random_ctx, nettle_random_func *random, + size_t digest_length, + const uint8_t *digest, + struct dsa_signature *signature); + +int +gostdsa_verify (const struct ecc_point *pub, + size_t length, const uint8_t *digest, + const struct dsa_signature *signature); + +/* Low-level GOSTDSA functions. */ +mp_size_t +ecc_gostdsa_sign_itch (const struct ecc_curve *ecc); + +void +ecc_gostdsa_sign (const struct ecc_curve *ecc, + const mp_limb_t *zp, + /* Random nonce, must be invertible mod ecc group + order. */ + const mp_limb_t *kp, + size_t length, const uint8_t *digest, + mp_limb_t *rp, mp_limb_t *sp, + mp_limb_t *scratch); + +mp_size_t +ecc_gostdsa_verify_itch (const struct ecc_curve *ecc); + +int +ecc_gostdsa_verify (const struct ecc_curve *ecc, + const mp_limb_t *pp, /* Public key */ + size_t length, const uint8_t *digest, + const mp_limb_t *rp, const mp_limb_t *sp, + mp_limb_t *scratch); + + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_GOSTDSA_H_INCLUDED */ diff --git a/testsuite/.gitignore b/testsuite/.gitignore index 1e2a69a60c13..be3a48707580 100644 --- a/testsuite/.gitignore +++ b/testsuite/.gitignore @@ -43,6 +43,9 @@ /eddsa-sign-test /eddsa-verify-test /gcm-test +/gostdsa-keygen-test +/gostdsa-sign-test +/gostdsa-verify-test /gosthash94-test /hkdf-test /hmac-test diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make index 6dbef7e24a27..9fd11fd6d126 100644 --- a/testsuite/.test-rules.make +++ b/testsuite/.test-rules.make @@ -289,6 +289,15 @@ ed25519-test$(EXEEXT): ed25519-test.$(OBJEXT) ed448-test$(EXEEXT): ed448-test.$(OBJEXT) $(LINK) ed448-test.$(OBJEXT) $(TEST_OBJS) -o ed448-test$(EXEEXT)
+gostdsa-sign-test$(EXEEXT): gostdsa-sign-test.$(OBJEXT) + $(LINK) gostdsa-sign-test.$(OBJEXT) $(TEST_OBJS) -o gostdsa-sign-test$(EXEEXT) + +gostdsa-verify-test$(EXEEXT): gostdsa-verify-test.$(OBJEXT) + $(LINK) gostdsa-verify-test.$(OBJEXT) $(TEST_OBJS) -o gostdsa-verify-test$(EXEEXT) + +gostdsa-keygen-test$(EXEEXT): gostdsa-keygen-test.$(OBJEXT) + $(LINK) gostdsa-keygen-test.$(OBJEXT) $(TEST_OBJS) -o gostdsa-keygen-test$(EXEEXT) + sha1-huge-test$(EXEEXT): sha1-huge-test.$(OBJEXT) $(LINK) sha1-huge-test.$(OBJEXT) $(TEST_OBJS) -o sha1-huge-test$(EXEEXT)
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index dea6c28d2f20..73a61685dfc1 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -53,7 +53,9 @@ TS_HOGWEED_SOURCES = sexp-test.c sexp-format-test.c \ ecdsa-sign-test.c ecdsa-verify-test.c \ ecdsa-keygen-test.c ecdh-test.c \ eddsa-compress-test.c eddsa-sign-test.c \ - eddsa-verify-test.c ed25519-test.c ed448-test.c + eddsa-verify-test.c ed25519-test.c ed448-test.c \ + gostdsa-sign-test.c gostdsa-verify-test.c \ + gostdsa-keygen-test.c
TS_SOURCES = $(TS_NETTLE_SOURCES) $(TS_HOGWEED_SOURCES) CXX_SOURCES = cxx-test.cxx diff --git a/testsuite/gostdsa-keygen-test.c b/testsuite/gostdsa-keygen-test.c new file mode 100644 index 000000000000..ebeabc860bca --- /dev/null +++ b/testsuite/gostdsa-keygen-test.c @@ -0,0 +1,155 @@ +#include "testutils.h" +#include "gostdsa.h" +#include "knuth-lfib.h" + +/* Check if y^2 = x^3 - 3x + b */ +static int +ecc_valid_p (struct ecc_point *pub) +{ + mpz_t t, x, y; + mpz_t lhs, rhs; + int res; + mp_size_t size; + + size = pub->ecc->p.size; + + /* First check range */ + if (mpn_cmp (pub->p, pub->ecc->p.m, size) >= 0 + || mpn_cmp (pub->p + size, pub->ecc->p.m, size) >= 0) + return 0; + + mpz_init (lhs); + mpz_init (rhs); + + mpz_roinit_n (x, pub->p, size); + mpz_roinit_n (y, pub->p + size, size); + + mpz_mul (lhs, y, y); + + if (pub->ecc->p.bit_size == 255) + { + /* Check that + 121666 (1 + x^2 - y^2) = 121665 x^2 y^2 */ + mpz_t x2; + mpz_init (x2); + mpz_mul (x2, x, x); /* x^2 */ + mpz_mul (rhs, x2, lhs); /* x^2 y^2 */ + mpz_sub (lhs, x2, lhs); /* x^2 - y^2 */ + mpz_add_ui (lhs, lhs, 1); /* 1 + x^2 - y^2 */ + mpz_mul_ui (lhs, lhs, 121666); + mpz_mul_ui (rhs, rhs, 121665); + + mpz_clear (x2); + } + else if (pub->ecc->p.bit_size == 448) + { + /* Check that + x^2 + y^2 = 1 - 39081 x^2 y^2 */ + mpz_t x2, d; + mpz_init (x2); + mpz_init_set_ui (d, 39081); + mpz_mul (x2, x, x); /* x^2 */ + mpz_mul (d, d, x2); /* 39081 x^2 */ + mpz_set_ui (rhs, 1); + mpz_submul (rhs, d, lhs); /* 1 - 39081 x^2 y^2 */ + mpz_add (lhs, x2, lhs); /* x^2 + y^2 */ + + mpz_clear (d); + mpz_clear (x2); + } + else + { + /* Check y^2 = x^3 - 3 x + b */ + mpz_mul (rhs, x, x); + mpz_sub_ui (rhs, rhs, 3); + mpz_mul (rhs, rhs, x); + mpz_add (rhs, rhs, mpz_roinit_n (t, pub->ecc->b, size)); + } + res = mpz_congruent_p (lhs, rhs, mpz_roinit_n (t, pub->ecc->p.m, size)); + + mpz_clear (lhs); + mpz_clear (rhs); + + return res; +} + +void +test_main (void) +{ + unsigned i; + struct knuth_lfib_ctx rctx; + struct dsa_signature signature; + + struct tstring *digest; + + knuth_lfib_init (&rctx, 4711); + dsa_signature_init (&signature); + + digest = SHEX (/* sha256("abc") */ + "BA7816BF 8F01CFEA 414140DE 5DAE2223" + "B00361A3 96177A9C B410FF61 F20015AD"); + + for (i = 0; ecc_curves[i]; i++) + { + const struct ecc_curve *ecc = ecc_curves[i]; + struct ecc_point pub; + struct ecc_scalar key; + + if (ecc->p.bit_size == 255 || ecc->p.bit_size == 448) + /* Exclude curve25519 and curve448, not supported with GOSTDSA. */ + continue; + + if (verbose) + fprintf (stderr, "Curve %d\n", ecc->p.bit_size); + + ecc_point_init (&pub, ecc); + ecc_scalar_init (&key, ecc); + + ecdsa_generate_keypair (&pub, &key, + &rctx, + (nettle_random_func *) knuth_lfib_random); + + if (verbose) + { + fprintf (stderr, "Public key:\nx = "); + write_mpn (stderr, 16, pub.p, ecc->p.size); + fprintf (stderr, "\ny = "); + write_mpn (stderr, 16, pub.p + ecc->p.size, ecc->p.size); + fprintf (stderr, "\nPrivate key: "); + write_mpn (stderr, 16, key.p, ecc->p.size); + fprintf (stderr, "\n"); + } + if (!ecc_valid_p (&pub)) + die ("gostdsa_generate_keypair produced an invalid point.\n"); + + gostdsa_sign (&key, + &rctx, (nettle_random_func *) knuth_lfib_random, + digest->length, digest->data, + &signature); + + if (!gostdsa_verify (&pub, digest->length, digest->data, + &signature)) + die ("gostdsa_verify failed.\n"); + + digest->data[3] ^= 17; + if (gostdsa_verify (&pub, digest->length, digest->data, + &signature)) + die ("gostdsa_verify returned success with invalid digest.\n"); + digest->data[3] ^= 17; + + mpz_combit (signature.r, 117); + if (gostdsa_verify (&pub, digest->length, digest->data, + &signature)) + die ("gostdsa_verify returned success with invalid signature.r.\n"); + + mpz_combit (signature.r, 117); + mpz_combit (signature.s, 93); + if (gostdsa_verify (&pub, digest->length, digest->data, + &signature)) + die ("gostdsa_verify returned success with invalid signature.s.\n"); + + ecc_point_clear (&pub); + ecc_scalar_clear (&key); + } + dsa_signature_clear (&signature); +} diff --git a/testsuite/gostdsa-sign-test.c b/testsuite/gostdsa-sign-test.c new file mode 100644 index 000000000000..0e2e0420a313 --- /dev/null +++ b/testsuite/gostdsa-sign-test.c @@ -0,0 +1,88 @@ +#include "testutils.h" +#include "gostdsa.h" + +static void +test_gostdsa (const struct ecc_curve *ecc, + /* Private key */ + const char *sz, + /* Random nonce */ + const char *sk, + /* Hash */ + const struct tstring *h, + /* Expected signature */ + const char *r, const char *s) +{ + struct dsa_signature ref; + mpz_t z; + mpz_t k; + mp_limb_t *rp = xalloc_limbs (ecc->p.size); + mp_limb_t *sp = xalloc_limbs (ecc->p.size); + mp_limb_t *scratch = xalloc_limbs (ecc_gostdsa_sign_itch (ecc)); + + dsa_signature_init (&ref); + + mpz_init_set_str (z, sz, 16); + mpz_init_set_str (k, sk, 16); + + ecc_gostdsa_sign (ecc, mpz_limbs_read_n (z, ecc->p.size), + mpz_limbs_read_n (k, ecc->p.size), + h->length, h->data, rp, sp, scratch); + + mpz_set_str (ref.r, r, 16); + mpz_set_str (ref.s, s, 16); + + if (mpz_limbs_cmp (ref.r, rp, ecc->p.size) != 0 + || mpz_limbs_cmp (ref.s, sp, ecc->p.size) != 0) + { + fprintf (stderr, "_gostdsa_sign failed, bit_size = %u\n", ecc->p.bit_size); + fprintf (stderr, "r = "); + write_mpn (stderr, 16, rp, ecc->p.size); + fprintf (stderr, "\ns = "); + write_mpn (stderr, 16, sp, ecc->p.size); + fprintf (stderr, "\nref.r = "); + mpz_out_str (stderr, 16, ref.r); + fprintf (stderr, "\nref.s = "); + mpz_out_str (stderr, 16, ref.s); + fprintf (stderr, "\n"); + abort(); + } + + free (rp); + free (sp); + free (scratch); + + dsa_signature_clear (&ref); + mpz_clear (k); + mpz_clear (z); +} + +void +test_main (void) +{ + test_gostdsa (nettle_get_gost_gc256b(), + "BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924", /* z */ + + "5782C53F110C596F9155D35EBD25A06A89C50391850A8FEFE33B0E270318857C", /* k */ + + SHEX("1C067E20EA6CB183F22EFB0F3C6FD2A4E6A02821CB7A1B17FACD5E1F7AA76F70"), /* h */ + + "E9323A5E88DD87FB7C724383BFFE7CECD4B9FFA2AC33BEEF73A5A1F743404F6B", /* r */ + + "5E5B9B805B01147A8492C4A162643AC615DC777B9174108F3DC276A41F987AF3"); /* s */ + + test_gostdsa (nettle_get_gost_gc512a(), + "3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435" + "757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B", /* z */ + + "72ABB44536656BF1618CE10BF7EADD40582304A51EE4E2A25A0A32CB0E773ABB" + "23B7D8FDD8FA5EEE91B4AE452F2272C86E1E2221215D405F51B5D5015616E1F6", /* k */ + + SHEX("EDC257BED45FDDE4F1457B7F5B19017A8F204184366689D938532CDBAA5CB29A" + "1D369DA57F8B983BE272219BD2C9A4FC57ECF7A77F34EE2E8AA553976A4766C0"), /* h */ + + "891AA75C2A6F3B4DE27E3903F61CBB0F3F85A4E3C62F39A6E4E84A7477679C6E" + "45008DC2774CA2FF64C12C0606FF918CAE3A50115440E9BF2971B627A882A1E8", /* r */ + + "31065479996DDBDEE180AFE22CA3CDC44B45CE4C6C83909D1D3B702922A32441" + "A9E11DCFBEA3D847C06B1A8A38EB1671D6C82FA21B79C99BE2EA809B10DAA5DF"); /* s */ +} diff --git a/testsuite/gostdsa-verify-test.c b/testsuite/gostdsa-verify-test.c new file mode 100644 index 000000000000..7279f5f46c5b --- /dev/null +++ b/testsuite/gostdsa-verify-test.c @@ -0,0 +1,111 @@ +#include "testutils.h" +#include "gostdsa.h" + +static void +test_gostdsa (const struct ecc_curve *ecc, + /* Public key */ + const char *xs, const char *ys, + /* Hash */ + struct tstring *h, + /* Valid signature */ + const char *r, const char *s) +{ + struct ecc_point pub; + struct dsa_signature signature; + mpz_t x, y; + + ecc_point_init (&pub, ecc); + dsa_signature_init (&signature); + + mpz_init_set_str (x, xs, 16); + mpz_init_set_str (y, ys, 16); + + if (!ecc_point_set (&pub, x, y)) + die ("ecc_point_set failed.\n"); + + mpz_set_str (signature.r, r, 16); + mpz_set_str (signature.s, s, 16); + + if (!gostdsa_verify (&pub, h->length, h->data, &signature)) + { + fprintf (stderr, "gostdsa_verify failed with valid signature.\n"); + fail: + fprintf (stderr, "bit_size = %u\nx = ", ecc->p.bit_size); + mpz_out_str (stderr, 16, x); + fprintf (stderr, "\ny = "); + mpz_out_str (stderr, 16, y); + fprintf (stderr, "\ndigest "); + print_hex (h->length, h->data); + fprintf (stderr, "r = "); + mpz_out_str (stderr, 16, signature.r); + fprintf (stderr, "\ns = "); + mpz_out_str (stderr, 16, signature.s); + fprintf (stderr, "\n"); + abort(); + } + + mpz_combit (signature.r, ecc->p.bit_size / 3); + if (gostdsa_verify (&pub, h->length, h->data, &signature)) + { + fprintf (stderr, "gostdsa_verify unexpectedly succeeded with invalid signature.\n"); + goto fail; + } + mpz_combit (signature.r, ecc->p.bit_size / 3); + + mpz_combit (signature.s, 4*ecc->p.bit_size / 5); + if (gostdsa_verify (&pub, h->length, h->data, &signature)) + { + fprintf (stderr, "gostdsa_verify unexpectedly succeeded with invalid signature.\n"); + goto fail; + } + mpz_combit (signature.s, 4*ecc->p.bit_size / 5); + + h->data[2*h->length / 3] ^= 0x40; + if (gostdsa_verify (&pub, h->length, h->data, &signature)) + { + fprintf (stderr, "gostdsa_verify unexpectedly succeeded with invalid signature.\n"); + goto fail; + } + h->data[2*h->length / 3] ^= 0x40; + if (!gostdsa_verify (&pub, h->length, h->data, &signature)) + { + fprintf (stderr, "gostdsa_verify failed, internal testsuite error.\n"); + goto fail; + } + + ecc_point_clear (&pub); + dsa_signature_clear (&signature); + mpz_clear (x); + mpz_clear (y); +} + +void +test_main (void) +{ + test_gostdsa (nettle_get_gost_gc256b(), + "971566CEDA436EE7678F7E07E84EBB7217406C0B4747AA8FD2AB1453C3D0DFBA", /* x */ + + "AD58736965949F8E59830F8DE20FC6C0D177F6AB599874F1E2E24FF71F9CE643", /* y */ + + SHEX("1C067E20EA6CB183F22EFB0F3C6FD2A4E6A02821CB7A1B17FACD5E1F7AA76F70"), /* h */ + + "E9323A5E88DD87FB7C724383BFFE7CECD4B9FFA2AC33BEEF73A5A1F743404F6B", /* r */ + + "5E5B9B805B01147A8492C4A162643AC615DC777B9174108F3DC276A41F987AF3"); /* s */ + + test_gostdsa (nettle_get_gost_gc512a(), + "03A36340A95BB5F93D131961B5B1C1B3213DF7FF3B5A30376407E2A65C441BC6" + "D1B34662317083243F007B15A8512B526606D3B172B606DCE86DBD6F82DA3D40", /* x */ + + "DEAD76318012FED79507809C89CC44848743640EAC9A3C847DA9082E050760A1" + "0679F4B707ABC1872640AD20D7441F66C7A8B3BFF1B8E11B4A076F0A86749F73", /* y */ + + SHEX("EDC257BED45FDDE4F1457B7F5B19017A8F204184366689D938532CDBAA5CB29A" + "1D369DA57F8B983BE272219BD2C9A4FC57ECF7A77F34EE2E8AA553976A4766C0"), /* h */ + + "891AA75C2A6F3B4DE27E3903F61CBB0F3F85A4E3C62F39A6E4E84A7477679C6E" + "45008DC2774CA2FF64C12C0606FF918CAE3A50115440E9BF2971B627A882A1E8", /* r */ + + "31065479996DDBDEE180AFE22CA3CDC44B45CE4C6C83909D1D3B702922A32441" + "A9E11DCFBEA3D847C06B1A8A38EB1671D6C82FA21B79C99BE2EA809B10DAA5DF"); /* s */ +}
From: Dmitry Baryshkov dbaryshkov@gmail.com
Signed-off-by: Dmitry Baryshkov dbaryshkov@gmail.com --- nettle.texinfo | 65 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-)
diff --git a/nettle.texinfo b/nettle.texinfo index 65b36e315f81..38c84410c103 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -115,6 +115,7 @@ Public-key algorithms
* Side-channel silence:: * ECDSA:: +* GOSTDSA:: * Curve 25519 and Curve 448::
@end detailmenu @@ -4916,6 +4917,7 @@ curve'' is used as a shorthand for the bitsize of the curve's prime @menu * Side-channel silence:: * ECDSA:: +* GOSTDSA:: * Curve 25519 and Curve 448:: @end menu
@@ -4950,7 +4952,7 @@ accesses depend only on the size of the input data and its location in memory, not on the actual data bits. This implies a performance penalty in several of the building blocks.
-@node ECDSA, Curve 25519 and Curve 448, Side-channel silence, Elliptic curves +@node ECDSA, GOSTDSA, Side-channel silence, Elliptic curves @comment node-name, next, previous, up @subsubsection ECDSA
@@ -5054,6 +5056,67 @@ random octets and store them at @code{dst}. For advice, see @xref{Randomness}. @end deftypefun
+@node GOSTDSA, Curve 25519 and Curve 448, ECDSA, Elliptic curves +@comment node-name, next, previous, up +@subsubsection GOSTDSA + +GOSTDSA (GOST R 34.10-2001, GOST R 34.10-2012) is a variant of the DSA +(@pxref{DSA}) and ECDSA (@pxref{ECDSA}) digital signature schemes, which works +over an elliptic curve group. Original documents are written in Russian. +English translations are provided in @cite{RFC 5832} and @cite{RFC 7091}. +While technically nothing stops one from using GOSTDSA over any curve, it +is defined only over several 256 and 512-bit curves. Like DSA and ECDSA, +creating a signature requires a unique random nonce (repeating the nonce +with two different messages reveals the private key, and any leak or bias +in the generation of the nonce also leaks information about the key). + +GOST R 34.10-2001 was defined to use GOST R 34.11-94 hash function +(GOSTHASH94 and GOSTHASH94CP, @cite{RFC 5831}). GOST R 34.10-2012 is +defined to use GOST R 34.11-2012 hash function (Streebog, @cite{RFC +6986}) of corresponding size (256 or 512) depending on curve size. + +Nettle defines GOSTDSA in @file{<nettle/gostdsa.h>}. GOSTDSA reuses ECDSA +data types (@code{struct ecc_point}, @code{struct ecc_scalar}) to +represent public and private keys. Also to generate a new GOSTDSA key +pair one has to use @code{ecdsa_generate_keypair()} function. + +To create and verify GOSTDSA signatures, the following functions are used. + +@deftypefun void gostdsa_sign (const struct ecc_scalar *@var{key}, void *@var{random_ctx}, nettle_random_func *@var{random}, size_t @var{digest_length}, const uint8_t *@var{digest}, struct dsa_signature *@var{signature}) +Uses the private key @var{key} to create a signature on @var{digest}. +@var{random_ctx} and @var{random} is a randomness generator. +@code{random(random_ctx, length, dst)} should generate @code{length} +random octets and store them at @code{dst}. The signature is stored in +@var{signature}, in the same was as for plain DSA. +@end deftypefun + +@deftypefun int gostdsa_verify (const struct ecc_point *@var{pub}, size_t @var{length}, const uint8_t *@var{digest}, const struct dsa_signature *@var{signature}) +Uses the public key @var{pub} to verify that @var{signature} is a valid +signature for the message digest @var{digest} (of @var{length} octets). +Returns 1 if the signature is valid, otherwise 0. +@end deftypefun + +For historical reason several curve IDs (OIDs) may correspond to a single +curve/generator combination. Following list defines correspondence +between nettle's view on curves and actual identifiers defined in @cite{RFC +4357} and @cite{RFC 7836}. + +@deftypefun {const struct ecc_curve} nettle_get_gost_gc256b(void) +Returns curve corresponding to following identifiers: +@itemize +@item id-GostR3410-2001-CryptoPro-A-ParamSet (@cite{RFC 4357}) +@item id-GostR3410-2001-CryptoPro-XchA-ParamSet (@cite{RFC 4357}) +@item id-tc26-gost-3410-12-256-paramSetB +@end itemize +@end deftypefun + +@deftypefun {const struct ecc_curve} nettle_get_gost_gc512a(void) +Returns curve corresponding to following identifiers: +@itemize +@item id-tc26-gost-3410-12-512-paramSetA (@cite{RFC 7836}) +@end itemize +@end deftypefun + @node Curve 25519 and Curve 448, , ECDSA, Elliptic curves @comment node-name, next, previous, up @subsubsection Curve25519 and Curve448
From: Dmitry Baryshkov dbaryshkov@gmail.com
Signed-off-by: Dmitry Baryshkov dbaryshkov@gmail.com --- examples/hogweed-benchmark.c | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+)
diff --git a/examples/hogweed-benchmark.c b/examples/hogweed-benchmark.c index 11393df04c81..bceed77e55d9 100644 --- a/examples/hogweed-benchmark.c +++ b/examples/hogweed-benchmark.c @@ -48,6 +48,7 @@ #include "dsa.h" #include "rsa.h" #include "eddsa.h" +#include "gostdsa.h" #include "curve25519.h" #include "curve448.h"
@@ -591,6 +592,107 @@ bench_eddsa_clear (void *p) free (p); }
+static void * +bench_gostdsa_init (unsigned size) +{ + struct ecdsa_ctx *ctx; + const struct ecc_curve *ecc; + + const char *xs; + const char *ys; + const char *zs; + mpz_t x, y, z; + + ctx = xalloc (sizeof(*ctx)); + + dsa_signature_init (&ctx->s); + knuth_lfib_init (&ctx->lfib, 17); + + switch (size) + { + case 256: + ecc = &_nettle_gost_gc256b; + xs = "971566ceda436ee7678f7e07e84ebb7217406c0b4747aa8fd2ab1453c3d0dfba"; + ys = "ad58736965949f8e59830f8de20fc6c0d177f6ab599874f1e2e24ff71f9ce643"; + zs = "bfcf1d623e5cdd3032a7c6eabb4a923c46e43d640ffeaaf2c3ed39a8fa399924"; + ctx->digest = hash_string (&nettle_sha256, "abc"); + ctx->digest_size = 32; + break; + + case 512: + ecc = &_nettle_gost_gc512a; + xs = "03A36340A95BB5F93D131961B5B1C1B3213DF7FF3B5A30376407E2A65C441BC6" + "D1B34662317083243F007B15A8512B526606D3B172B606DCE86DBD6F82DA3D40"; + ys = "DEAD76318012FED79507809C89CC44848743640EAC9A3C847DA9082E050760A1" + "0679F4B707ABC1872640AD20D7441F66C7A8B3BFF1B8E11B4A076F0A86749F73"; + zs = "3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435" + "757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B"; + ctx->digest = hash_string (&nettle_sha512, "abc"); + ctx->digest_size = 64; + break; + + default: + die ("Internal error.\n"); + } + ecc_point_init (&ctx->pub, ecc); + ecc_scalar_init (&ctx->key, ecc); + + mpz_init_set_str (x, xs, 16); + mpz_init_set_str (y, ys, 16); + mpz_init_set_str (z, zs, 16); + + ecc_point_set (&ctx->pub, x, y); + ecc_scalar_set (&ctx->key, z); + + mpz_clear (x); + mpz_clear (y); + mpz_clear (z); + + gostdsa_sign (&ctx->key, + &ctx->lfib, (nettle_random_func *) knuth_lfib_random, + ctx->digest_size, ctx->digest, + &ctx->s); + + return ctx; +} + +static void +bench_gostdsa_sign (void *p) +{ + struct ecdsa_ctx *ctx = p; + struct dsa_signature s; + + dsa_signature_init (&s); + gostdsa_sign (&ctx->key, + &ctx->lfib, (nettle_random_func *) knuth_lfib_random, + ctx->digest_size, ctx->digest, + &s); + dsa_signature_clear (&s); +} + +static void +bench_gostdsa_verify (void *p) +{ + struct ecdsa_ctx *ctx = p; + if (! gostdsa_verify (&ctx->pub, + ctx->digest_size, ctx->digest, + &ctx->s)) + die ("Internal error, _gostdsa_verify failed.\n"); +} + +static void +bench_gostdsa_clear (void *p) +{ + struct ecdsa_ctx *ctx = p; + + ecc_point_clear (&ctx->pub); + ecc_scalar_clear (&ctx->key); + dsa_signature_clear (&ctx->s); + free (ctx->digest); + + free (ctx); +} + #if WITH_OPENSSL struct openssl_rsa_ctx { @@ -838,6 +940,8 @@ struct alg alg_list[] = { { "eddsa", 448, bench_eddsa_init, bench_eddsa_sign, bench_eddsa_verify, bench_eddsa_clear }, { "curve", 255, bench_curve_init, bench_curve_mul_g, bench_curve_mul, bench_curve_clear}, { "curve", 448, bench_curve_init, bench_curve_mul_g, bench_curve_mul, bench_curve_clear }, + { "gostdsa", 256, bench_gostdsa_init, bench_gostdsa_sign, bench_gostdsa_verify, bench_gostdsa_clear }, + { "gostdsa", 512, bench_gostdsa_init, bench_gostdsa_sign, bench_gostdsa_verify, bench_gostdsa_clear }, };
#define numberof(x) (sizeof (x) / sizeof ((x)[0]))
nettle-bugs@lists.lysator.liu.se