From 0b9860dfa63becdc2e2d8468889c35a2991c0329 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <dueno@redhat.com>
Date: Sun, 14 Apr 2024 09:05:19 +0900
Subject: [PATCH] Add support for deterministic DSA and ECDSA

This implements deterministic nonce construction for DSA and ECDSA,
as defined in RFC 6979.

Signed-off-by: Daiki Ueno <dueno@redhat.com>
---
 Makefile.in                          |   5 +-
 dsa-compute-k.c                      | 173 +++++++++++++++++++++++++++
 dsa-compute-k.h                      |  49 ++++++++
 dsa-sign.c                           |  65 ++++++++++
 dsa.h                                |   9 ++
 ecdsa-sign.c                         |  26 ++++
 ecdsa.h                              |  10 ++
 gmp-glue.h                           |   5 +-
 nettle-internal.h                    |   1 +
 testsuite/.gitignore                 |   2 +
 testsuite/Makefile.in                |   3 +-
 testsuite/deterministic-dsa-test.c   |  96 +++++++++++++++
 testsuite/deterministic-ecdsa-test.c |  82 +++++++++++++
 13 files changed, 522 insertions(+), 4 deletions(-)
 create mode 100644 dsa-compute-k.c
 create mode 100644 dsa-compute-k.h
 create mode 100644 testsuite/deterministic-dsa-test.c
 create mode 100644 testsuite/deterministic-ecdsa-test.c

diff --git a/Makefile.in b/Makefile.in
index 29ad54d7..227c459a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -225,7 +225,8 @@ hogweed_SOURCES = sexp.c sexp-format.c \
 		  ed25519-sha512.c ed25519-sha512-pubkey.c \
 		  ed25519-sha512-sign.c ed25519-sha512-verify.c \
 		  ed448-shake256.c ed448-shake256-pubkey.c \
-		  ed448-shake256-sign.c ed448-shake256-verify.c
+		  ed448-shake256-sign.c ed448-shake256-verify.c \
+		  dsa-compute-k.c
 
 OPT_SOURCES = fat-arm.c fat-arm64.c fat-ppc.c fat-s390x.c fat-x86_64.c mini-gmp.c
 
@@ -278,7 +279,7 @@ DISTFILES = $(SOURCES) $(HEADERS) getopt.h getopt_int.h \
 	ctr-internal.h chacha-internal.h sha3-internal.h \
 	salsa20-internal.h umac-internal.h hogweed-internal.h \
 	rsa-internal.h pkcs1-internal.h dsa-internal.h eddsa-internal.h \
-	gmp-glue.h ecc-internal.h fat-setup.h oaep.h \
+	gmp-glue.h ecc-internal.h fat-setup.h oaep.h dsa-compute-k.h \
 	mini-gmp.h asm.m4 m4-utils.m4 \
 	nettle.texinfo nettle.info nettle.html nettle.pdf sha-example.c
 
diff --git a/dsa-compute-k.c b/dsa-compute-k.c
new file mode 100644
index 00000000..28b4f6d5
--- /dev/null
+++ b/dsa-compute-k.c
@@ -0,0 +1,173 @@
+/* dsa-compute-k.c
+
+   The DSA publickey algorithm, deterministic nonce construction (RFC 6979).
+
+   Copyright (C) 2019-2024 Red Hat, Inc.
+
+   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 "dsa-compute-k.h"
+
+#include "gmp-glue.h"
+#include "nettle-internal.h"
+#include <string.h>
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+void
+_dsa_compute_k (mp_limb_t *k, const mp_limb_t *q, const mp_limb_t *x,
+		mp_bitcnt_t q_bits,
+		void *hmac_ctx, const struct nettle_mac *hmac,
+		const uint8_t *digest)
+{
+  uint8_t V[NETTLE_MAX_HASH_DIGEST_SIZE];
+  uint8_t K[NETTLE_MAX_HASH_DIGEST_SIZE];
+  TMP_GMP_DECL(xp, uint8_t);
+  TMP_GMP_DECL(tp, uint8_t);
+  const mp_bitcnt_t k_bits = hmac->digest_size * 8;
+  mp_size_t kn = NETTLE_BIT_SIZE_TO_LIMB_SIZE (k_bits);
+  mp_size_t qn = NETTLE_BIT_SIZE_TO_LIMB_SIZE (q_bits);
+  size_t nbytes = (q_bits + 7) / 8;
+  const uint8_t c0 = 0x00;
+  const uint8_t c1 = 0x01;
+  mp_limb_t cy;
+  mp_limb_t *scratch;
+
+  assert (hmac->digest_size == hmac->key_size);
+
+  TMP_GMP_ALLOC (xp, nbytes);
+  TMP_GMP_ALLOC (tp, nbytes);
+
+  scratch = gmp_alloc_limbs (NETTLE_OCTET_SIZE_TO_LIMB_SIZE (nbytes));
+
+  /* int2octets(x) */
+  mpn_get_base256 (xp, nbytes, x, qn);
+
+  /* bits2octets (k) */
+  mpn_set_base256 (k, kn, digest, hmac->digest_size);
+
+  if (kn < qn)
+    /* qlen > blen: add zero bits to the left */
+    mpn_zero (&k[kn], qn - kn);
+  else if (k_bits > q_bits)
+    {
+      /* qlen < blen: keep the leftmost qlen bits.  We do this in 2
+       * steps because mpn_rshift only accepts shift count in the
+       * range 1 to mp_bits_per_limb-1.
+       */
+      mp_bitcnt_t shift = k_bits - q_bits;
+
+      if (shift / GMP_NUMB_BITS > 0)
+	{
+	  mpn_copyi (k, &k[shift / GMP_NUMB_BITS], qn);
+	  kn -= shift / GMP_NUMB_BITS;
+	}
+
+      if (shift % GMP_NUMB_BITS > 0)
+	mpn_rshift (k, k, kn, shift % GMP_NUMB_BITS);
+    }
+
+  cy = mpn_sub_n (k, k, q, qn);
+  mpn_cnd_add_n (cy, k, k, q, qn);
+  mpn_get_base256 (tp, nbytes, k, qn);
+
+  /* Step b */
+  memset (V, c1, hmac->digest_size);
+
+  /* Step c */
+  memset (K, c0, hmac->digest_size);
+
+  /* Step d */
+  hmac->set_key (hmac_ctx, K);
+  hmac->update (hmac_ctx, hmac->digest_size, V);
+  hmac->update (hmac_ctx, 1, &c0);
+  hmac->update (hmac_ctx, nbytes, xp);
+  hmac->update (hmac_ctx, nbytes, tp);
+  hmac->digest (hmac_ctx, hmac->digest_size, K);
+
+  /* Step e */
+  hmac->set_key (hmac_ctx, K);
+  hmac->update (hmac_ctx, hmac->digest_size, V);
+  hmac->digest (hmac_ctx, hmac->digest_size, V);
+
+  /* Step f */
+  hmac->set_key (hmac_ctx, K);
+  hmac->update (hmac_ctx, hmac->digest_size, V);
+  hmac->update (hmac_ctx, 1, &c1);
+  hmac->update (hmac_ctx, nbytes, xp);
+  hmac->update (hmac_ctx, nbytes, tp);
+  hmac->digest (hmac_ctx, hmac->digest_size, K);
+
+  /* Step g */
+  hmac->set_key (hmac_ctx, K);
+  hmac->update (hmac_ctx, hmac->digest_size, V);
+  hmac->digest (hmac_ctx, hmac->digest_size, V);
+
+  /* Step h */
+  for (;;)
+    {
+      /* Step 1 */
+      size_t tlen = 0;
+
+      /* Step 2 */
+      while (tlen < nbytes)
+	{
+	  size_t remaining = MIN (nbytes - tlen, hmac->digest_size);
+	  hmac->set_key (hmac_ctx, K);
+	  hmac->update (hmac_ctx, hmac->digest_size, V);
+	  hmac->digest (hmac_ctx, hmac->digest_size, V);
+	  memcpy (&tp[tlen], V, remaining);
+	  tlen += remaining;
+	}
+
+      /* Step 3 */
+      mpn_set_base256 (k, qn, tp, tlen);
+      if (tlen * 8 > q_bits)
+	mpn_rshift (k, k, qn, tlen * 8 - q_bits);
+      /* Check if k is in [1,q-1] */
+      if (!sec_zero_p (k, qn) && mpn_sub_n (scratch, k, q, qn))
+	break;
+
+      hmac->set_key (hmac_ctx, K);
+      hmac->update (hmac_ctx, hmac->digest_size, V);
+      hmac->update (hmac_ctx, 1, &c0);
+      hmac->digest (hmac_ctx, hmac->digest_size, K);
+
+      hmac->set_key (hmac_ctx, K);
+      hmac->update (hmac_ctx, hmac->digest_size, V);
+      hmac->digest (hmac_ctx, hmac->digest_size, V);
+  }
+
+  TMP_GMP_FREE (xp);
+  TMP_GMP_FREE (tp);
+  gmp_free_limbs (scratch, NETTLE_OCTET_SIZE_TO_LIMB_SIZE (nbytes));
+}
diff --git a/dsa-compute-k.h b/dsa-compute-k.h
new file mode 100644
index 00000000..a65c1f0a
--- /dev/null
+++ b/dsa-compute-k.h
@@ -0,0 +1,49 @@
+/* dsa-compute-k.h
+
+   The DSA publickey algorithm, deterministic nonce construction (RFC 6979).
+
+   Copyright (C) 2019-2024 Red Hat, Inc.
+
+   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_DSA_COMPUTE_K_H_INCLUDED
+#define NETTLE_DSA_COMPUTE_K_H_INCLUDED
+
+#include "bignum.h"
+#include "nettle-meta.h"
+
+/* Namespace mangling */
+#define _dsa_compute_k _nettle_dsa_compute_k
+
+void
+_dsa_compute_k (mp_limb_t *k, const mp_limb_t *q, const mp_limb_t *x,
+		mp_bitcnt_t q_bits,
+		void *hmac_ctx, const struct nettle_mac *hmac,
+		const uint8_t *digest);
+
+#endif /* NETTLE_DSA_COMPUTE_K_H_INCLUDED */
diff --git a/dsa-sign.c b/dsa-sign.c
index 42a0a581..8a561499 100644
--- a/dsa-sign.c
+++ b/dsa-sign.c
@@ -39,9 +39,12 @@
 #include <stdlib.h>
 
 #include "dsa.h"
+#include "dsa-compute-k.h"
 #include "dsa-internal.h"
 
 #include "bignum.h"
+#include "gmp-glue.h"
+#include "nettle-internal.h"
 
 
 int
@@ -99,3 +102,65 @@ dsa_sign(const struct dsa_params *params,
 
   return res;
 }
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+int
+dsa_sign_deterministic(const struct dsa_params *params,
+		       const mpz_t x,
+		       void *hmac_ctx, const struct nettle_mac *hmac,
+		       const uint8_t *digest,
+		       struct dsa_signature *signature)
+{
+  mp_limb_t *kp;
+  mpz_t k;
+  mpz_t i;
+  mpz_t h;
+  mpz_t tmp;
+  int res;
+
+  /* Check that p is odd, so that invalid keys don't result in a crash
+     inside mpz_powm_sec. */
+  if (mpz_even_p (params->p))
+    return 0;
+
+  kp = gmp_alloc_limbs (MAX(mpz_size (params->q),
+			    NETTLE_OCTET_SIZE_TO_LIMB_SIZE (NETTLE_MAX_HASH_DIGEST_SIZE)));
+
+  _dsa_compute_k (kp, mpz_limbs_read (params->q), mpz_limbs_read (x),
+		  mpz_sizeinbase(params->q, 2), hmac_ctx, hmac,
+		  digest);
+
+  mpz_roinit_n (k, kp, mpz_size (params->q));
+
+  /* Compute r = (g^k (mod p)) (mod q) */
+  mpz_powm_sec(tmp, params->g, k, params->p);
+  mpz_fdiv_r(signature->r, tmp, params->q);
+
+  /* Compute hash */
+  mpz_init(h);
+  _nettle_dsa_hash (h, mpz_sizeinbase(params->q, 2), hmac->digest_size, digest);
+
+  /* Compute i = k^-1 (mod q) */
+  mpz_init(i);
+  if (mpz_invert(i, k, params->q))
+    {
+      /* Compute signature s = k^-1 (h + xr) (mod q) */
+      mpz_mul(tmp, signature->r, x);
+      mpz_fdiv_r(tmp, tmp, params->q);
+      mpz_add(tmp, tmp, h);
+      mpz_mul(tmp, tmp, i);
+      mpz_fdiv_r(signature->s, tmp, params->q);
+      res = 1;
+    }
+  else
+    /* What do we do now? The key is invalid. */
+    res = 0;
+
+  mpz_clear(i);
+  mpz_clear(h);
+  mpz_clear(tmp);
+  gmp_free_limbs (kp, mpz_size (params->q));
+
+  return res;
+}
diff --git a/dsa.h b/dsa.h
index 553ef327..b612fdb6 100644
--- a/dsa.h
+++ b/dsa.h
@@ -36,6 +36,7 @@
 
 #include "nettle-types.h"
 #include "bignum.h"
+#include "nettle-meta.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -47,6 +48,7 @@ extern "C" {
 #define dsa_signature_init nettle_dsa_signature_init
 #define dsa_signature_clear nettle_dsa_signature_clear
 #define dsa_sign nettle_dsa_sign
+#define dsa_sign_deterministic nettle_dsa_sign_deterministic
 #define dsa_verify nettle_dsa_verify
 #define dsa_generate_params nettle_dsa_generate_params
 #define dsa_generate_keypair nettle_dsa_generate_keypair
@@ -109,6 +111,13 @@ dsa_sign(const struct dsa_params *params,
 	 const uint8_t *digest,
 	 struct dsa_signature *signature);
 
+int
+dsa_sign_deterministic(const struct dsa_params *params,
+		       const mpz_t x,
+		       void *hmac_ctx, const struct nettle_mac *hmac,
+		       const uint8_t *digest,
+		       struct dsa_signature *signature);
+
 int
 dsa_verify(const struct dsa_params *params,
 	   const mpz_t y,
diff --git a/ecdsa-sign.c b/ecdsa-sign.c
index e6fb3287..6e7fb2e0 100644
--- a/ecdsa-sign.c
+++ b/ecdsa-sign.c
@@ -40,6 +40,7 @@
 
 #include "ecdsa.h"
 #include "ecc-internal.h"
+#include "dsa-compute-k.h"
 #include "nettle-internal.h"
 
 void
@@ -69,3 +70,28 @@ ecdsa_sign (const struct ecc_scalar *key,
     }
   while (mpz_sgn (signature->r) == 0 || mpz_sgn (signature->s) == 0);
 }
+
+void
+ecdsa_sign_deterministic (const struct ecc_scalar *key,
+			  void *hmac_ctx, const struct nettle_mac *hmac,
+			  const uint8_t *digest,
+			  struct dsa_signature *signature)
+{
+  /* At most 936 bytes. */
+  TMP_DECL(k, mp_limb_t, ECC_MAX_SIZE + ECC_ECDSA_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_ECDSA_SIGN_ITCH (size));
+
+  _dsa_compute_k (k, key->ecc->q.m, key->p, key->ecc->q.bit_size,
+		  hmac_ctx, hmac, digest);
+
+  ecc_ecdsa_sign (key->ecc, key->p, k, hmac->digest_size, digest,
+		  rp, sp, k + size);
+  mpz_limbs_finish (signature->r, size);
+  mpz_limbs_finish (signature->s, size);
+
+  assert (mpz_sgn (signature->r) != 0 && mpz_sgn (signature->s) != 0);
+}
diff --git a/ecdsa.h b/ecdsa.h
index 259efc10..c9510c71 100644
--- a/ecdsa.h
+++ b/ecdsa.h
@@ -36,6 +36,7 @@
 
 #include "ecc.h"
 #include "dsa.h"
+#include "nettle-meta.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -43,6 +44,7 @@ extern "C" {
 
 /* Name mangling */
 #define ecdsa_sign nettle_ecdsa_sign
+#define ecdsa_sign_deterministic nettle_ecdsa_sign_deterministic
 #define ecdsa_verify nettle_ecdsa_verify
 #define ecdsa_generate_keypair nettle_ecdsa_generate_keypair
 #define ecc_ecdsa_sign nettle_ecc_ecdsa_sign
@@ -61,6 +63,14 @@ ecdsa_sign (const struct ecc_scalar *key,
 	    const uint8_t *digest,
 	    struct dsa_signature *signature);
 
+/* Similar to ecdsa_sign, but this uses a deterministic construction
+   of nonce according to RFC 6979. */
+void
+ecdsa_sign_deterministic (const struct ecc_scalar *key,
+			  void *hmac_ctx, const struct nettle_mac *hmac,
+			  const uint8_t *digest,
+			  struct dsa_signature *signature);
+
 int
 ecdsa_verify (const struct ecc_point *pub,
 	      size_t length, const uint8_t *digest,
diff --git a/gmp-glue.h b/gmp-glue.h
index afe94635..7e5b664f 100644
--- a/gmp-glue.h
+++ b/gmp-glue.h
@@ -85,8 +85,11 @@ is_zero_limb (mp_limb_t x)
 int
 sec_zero_p (const mp_limb_t *ap, mp_size_t n);
 
+#define NETTLE_BIT_SIZE_TO_LIMB_SIZE(n) \
+  (((n) + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS)
+
 #define NETTLE_OCTET_SIZE_TO_LIMB_SIZE(n) \
-  (((n) * 8 + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS)
+  NETTLE_BIT_SIZE_TO_LIMB_SIZE((n) * 8)
 
 /* Convenience functions */
 
diff --git a/nettle-internal.h b/nettle-internal.h
index 81d06e80..97190c92 100644
--- a/nettle-internal.h
+++ b/nettle-internal.h
@@ -75,6 +75,7 @@
 #define NETTLE_MAX_HASH_BLOCK_SIZE 144  /* For sha3_224*/
 #define NETTLE_MAX_HASH_DIGEST_SIZE 64
 #define NETTLE_MAX_HASH_CONTEXT_SIZE (sizeof(struct sha3_224_ctx))
+#define NETTLE_MAX_HMAC_CONTEXT_SIZE (sizeof(struct hmac_sha512_ctx))
 #define NETTLE_MAX_SEXP_ASSOC 17
 #define NETTLE_MAX_CIPHER_BLOCK_SIZE 32
 #define NETTLE_MAX_CIPHER_KEY_SIZE 32
diff --git a/testsuite/.gitignore b/testsuite/.gitignore
index b7929caa..60f9504b 100644
--- a/testsuite/.gitignore
+++ b/testsuite/.gitignore
@@ -24,6 +24,8 @@
 /cxx-test
 /des-test
 /des3-test
+/deterministic-dsa-test
+/deterministic-ecdsa-test
 /dlopen-test
 /dsa-keygen-test
 /dsa-test
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in
index de7fa590..48b6f2fb 100644
--- a/testsuite/Makefile.in
+++ b/testsuite/Makefile.in
@@ -58,7 +58,8 @@ TS_HOGWEED_SOURCES = sexp-test.c sexp-format-test.c \
 		     eddsa-compress-test.c eddsa-sign-test.c \
 		     eddsa-verify-test.c ed25519-test.c ed448-test.c \
 		     gostdsa-sign-test.c gostdsa-verify-test.c \
-		     gostdsa-keygen-test.c gostdsa-vko-test.c
+		     gostdsa-keygen-test.c gostdsa-vko-test.c \
+		     deterministic-dsa-test.c deterministic-ecdsa-test.c
 
 TS_SOURCES = $(TS_NETTLE_SOURCES) $(TS_HOGWEED_SOURCES)
 CXX_SOURCES = cxx-test.cxx
diff --git a/testsuite/deterministic-dsa-test.c b/testsuite/deterministic-dsa-test.c
new file mode 100644
index 00000000..4af9d417
--- /dev/null
+++ b/testsuite/deterministic-dsa-test.c
@@ -0,0 +1,96 @@
+#include "testutils.h"
+#include "nettle-internal.h"
+#include "nettle-meta.h"
+
+static void
+test_dsa (const struct dsa_params *params,
+	  const struct nettle_mac *hmac,
+	  const struct nettle_hash *hash,
+	  /* Private key */
+	  const char *sz,
+	  size_t length,
+	  const char *message,
+	  /* Expected signature */
+	  const char *r, const char *s)
+{
+  mpz_t z;
+  void *hmac_ctx;
+  void *hash_ctx;
+  uint8_t digest[NETTLE_MAX_HASH_DIGEST_SIZE];
+  struct dsa_signature ref;
+  struct dsa_signature sig;
+
+  mpz_init_set_str (z, sz, 16);
+
+  dsa_signature_init (&ref);
+  dsa_signature_init (&sig);
+
+  hash_ctx = xalloc (hash->context_size);
+  hash->init (hash_ctx);
+  hash->update (hash_ctx, length, (const uint8_t *) message);
+  hash->digest (hash_ctx, hash->digest_size, digest);
+
+  hmac_ctx = xalloc (hmac->context_size);
+  dsa_sign_deterministic (params, z,
+			  hmac_ctx, hmac,
+			  digest,
+			  &sig);
+
+  mpz_set_str (ref.r, r, 16);
+  mpz_set_str (ref.s, s, 16);
+
+  if (mpz_cmp (ref.r, sig.r) != 0 || mpz_cmp (ref.s, sig.s) != 0)
+    {
+      fprintf (stderr, "dsa_sign_deterministic failed, bit_size = %lu\n",
+	       mpz_sizeinbase (params->q, 2));
+      fprintf (stderr, "r     = ");
+      mpz_out_str (stderr, 16, sig.r);
+      fprintf (stderr, "\ns     = ");
+      mpz_out_str (stderr, 16, sig.s);
+      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 (hash_ctx);
+  free (hmac_ctx);
+
+  dsa_signature_clear (&ref);
+  dsa_signature_clear (&sig);
+  mpz_clear (z);
+}
+
+void
+test_main (void)
+{
+  struct dsa_params params;
+
+  mpz_init_set_str (params.p,
+		    "86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447"
+		    "E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED88"
+		    "73ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C"
+		    "881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779",
+		    16);
+  mpz_init_set_str (params.q,
+		    "996F967F6C8E388D9E28D01E205FBA957A5698B1",
+		    16);
+  mpz_init_set_str (params.g,
+		    "07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D"
+		    "89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD"
+		    "87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA4"
+		    "17BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD",
+		    16);
+
+  test_dsa (&params,
+	    &nettle_hmac_sha256,
+	    &nettle_sha256,
+	    "411602CB19A6CCC34494D79D98EF1E7ED5AF25F7", /* Private key */
+	    6, "sample", /* message */
+	    "81F2F5850BE5BC123C43F71A3033E9384611C545", /* r */
+	    "4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89"); /* s */
+
+  dsa_params_clear (&params);
+}
diff --git a/testsuite/deterministic-ecdsa-test.c b/testsuite/deterministic-ecdsa-test.c
new file mode 100644
index 00000000..1f9025a5
--- /dev/null
+++ b/testsuite/deterministic-ecdsa-test.c
@@ -0,0 +1,82 @@
+#include "testutils.h"
+#include "nettle-internal.h"
+#include "nettle-meta.h"
+
+static void
+test_ecdsa (const struct ecc_curve *ecc,
+	    const struct nettle_mac *hmac,
+	    const struct nettle_hash *hash,
+	    /* Private key */
+	    const char *sz,
+	    size_t length,
+	    const char *message,
+	    /* Expected signature */
+	    const char *r, const char *s)
+{
+  struct ecc_scalar key;
+  mp_limb_t *zp = xalloc_limbs (ecc->p.size);
+  mpz_t z;
+  void *hmac_ctx;
+  void *hash_ctx;
+  uint8_t digest[NETTLE_MAX_HASH_DIGEST_SIZE];
+  struct dsa_signature ref;
+  struct dsa_signature sig;
+
+  dsa_signature_init (&ref);
+  dsa_signature_init (&sig);
+
+  hash_ctx = xalloc (hash->context_size);
+  hash->init (hash_ctx);
+  hash->update (hash_ctx, length, (const uint8_t *) message);
+  hash->digest (hash_ctx, hash->digest_size, digest);
+
+  ecc_scalar_init (&key, ecc);
+
+  mpz_init_set_str (z, sz, 16);
+  ASSERT (ecc_scalar_set (&key, z));
+
+  hmac_ctx = xalloc (hmac->context_size);
+  ecdsa_sign_deterministic (&key,
+			    hmac_ctx, hmac,
+			    digest,
+			    &sig);
+
+  mpz_set_str (ref.r, r, 16);
+  mpz_set_str (ref.s, s, 16);
+
+  if (mpz_cmp (ref.r, sig.r) != 0 || mpz_cmp (ref.s, sig.s) != 0)
+    {
+      fprintf (stderr, "ecdsa_sign_deterministic failed, bit_size = %u\n", ecc->p.bit_size);
+      fprintf (stderr, "r     = ");
+      mpz_out_str (stderr, 16, sig.r);
+      fprintf (stderr, "\ns     = ");
+      mpz_out_str (stderr, 16, sig.s);
+      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 (hash_ctx);
+  free (hmac_ctx);
+  free (zp);
+
+  ecc_scalar_clear (&key);
+  dsa_signature_clear (&ref);
+  dsa_signature_clear (&sig);
+  mpz_clear (z);
+}
+
+void
+test_main (void)
+{
+  test_ecdsa (&_nettle_secp_256r1,
+	      &nettle_hmac_sha256,
+	      &nettle_sha256,
+	      "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721", /* Private key */
+	      6, "sample", /* message */
+	      "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716", /* r */
+	      "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8"); /* s */
+}
-- 
2.44.0

