From 05624cdeb26d5f3fa79126e75f3eb8204434e4dd Mon Sep 17 00:00:00 2001
From: Wim Lewis <wiml@hhhh.org>
Date: Mon, 27 May 2019 14:08:15 -0700
Subject: [PATCH] First draft at reconstructing points from their compact
 representation (X + sign(Y)). Implementations of sqrt in P-256, P-384, and
 P-521.

---
 Makefile.in                            |   4 +-
 ecc-192.c                              |   2 +
 ecc-224.c                              |   2 +
 ecc-25519.c                            |   2 +
 ecc-256.c                              |  38 ++-
 ecc-384.c                              |  55 ++++-
 ecc-521.c                              |  20 +-
 ecc-internal.h                         |  35 ++-
 ecc-mod-arith.c                        |  57 +++++
 ecc-point-compact.c                    |  51 ++++
 ecc-point-from-octets.c                | 111 +++++++++
 ecc-point-recover.c                    | 183 +++++++++++++++
 ecc.h                                  |   8 +-
 testsuite/.gitignore                   |   2 +
 testsuite/Makefile.in                  |   3 +-
 testsuite/ecc-point-compact-test.c     | 308 +++++++++++++++++++++++++
 testsuite/ecc-point-from-octets-test.c | 206 +++++++++++++++++
 17 files changed, 1080 insertions(+), 7 deletions(-)
 create mode 100644 ecc-point-compact.c
 create mode 100644 ecc-point-from-octets.c
 create mode 100644 ecc-point-recover.c
 create mode 100644 testsuite/ecc-point-compact-test.c
 create mode 100644 testsuite/ecc-point-from-octets-test.c

diff --git a/Makefile.in b/Makefile.in
index a6b8ffd6..1b1cd45c 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -179,7 +179,9 @@ hogweed_SOURCES = sexp.c sexp-format.c \
 		  ecc-dup-eh.c ecc-add-eh.c ecc-add-ehh.c \
 		  ecc-mul-g-eh.c ecc-mul-a-eh.c \
 		  ecc-mul-g.c ecc-mul-a.c ecc-hash.c ecc-random.c \
-		  ecc-point.c ecc-scalar.c ecc-point-mul.c ecc-point-mul-g.c \
+		  ecc-point.c ecc-point-from-octets.c ecc-point-compact.c \
+		  ecc-point-recover.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 \
 		  curve25519-mul-g.c curve25519-mul.c curve25519-eh-to-x.c \
diff --git a/ecc-192.c b/ecc-192.c
index 4f428113..c6b5d403 100644
--- a/ecc-192.c
+++ b/ecc-192.c
@@ -130,6 +130,7 @@ const struct ecc_curve _nettle_secp_192r1 =
     ecc_192_modp,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
   {
     192,
@@ -149,6 +150,7 @@ const struct ecc_curve _nettle_secp_192r1 =
     ecc_mod,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
   
   USE_REDC,
diff --git a/ecc-224.c b/ecc-224.c
index 5962e1b8..bd44c409 100644
--- a/ecc-224.c
+++ b/ecc-224.c
@@ -82,6 +82,7 @@ const struct ecc_curve _nettle_secp_224r1 =
     USE_REDC ? ecc_224_redc : ecc_224_modp,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
   {
     224,
@@ -101,6 +102,7 @@ const struct ecc_curve _nettle_secp_224r1 =
     ecc_mod,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
   
   USE_REDC,
diff --git a/ecc-25519.c b/ecc-25519.c
index bb71a36b..891965cb 100644
--- a/ecc-25519.c
+++ b/ecc-25519.c
@@ -310,6 +310,7 @@ const struct ecc_curve _nettle_curve25519 =
     ecc_25519_modp,
     ecc_25519_inv,
     ecc_25519_sqrt,
+    NULL,
   },
   {
     253,
@@ -329,6 +330,7 @@ const struct ecc_curve _nettle_curve25519 =
     ecc_25519_modq,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
 
   0, /* No redc */
diff --git a/ecc-256.c b/ecc-256.c
index 7eed2835..2e710463 100644
--- a/ecc-256.c
+++ b/ecc-256.c
@@ -239,6 +239,40 @@ ecc_256_modq (const struct ecc_modulo *q, mp_limb_t *rp)
 #error Unsupported parameters
 #endif
 
+static void
+ecc_256_sqrt (const struct ecc_modulo *m,
+	      mp_limb_t *rp,
+	      const mp_limb_t *cp,
+	      mp_limb_t *scratch)
+{
+  mp_size_t size = m->size;
+
+  /* This computes the square root modulo p256 using the identity:
+
+     sqrt(c) = c^(2^254 − 2^222 + 2^190 + 2^94)  (mod P-256)
+  */
+
+  /* We use our scratch space for three temporaries (TA, TB, TC) all of
+     which are 2*size long to allow for multiplication/squaring */
+
+#define TA scratch
+#define TB (scratch + 2*size)
+#define TC (scratch + 4*size)
+
+  ecc_mod_pow2n_mul(m, TB, cp, cp, TC, 1);   /* [1] TB <-- c^3 */
+  ecc_mod_pow2n_mul(m, TA, TB, TB, TC, 2);   /* [2] TA <-- c^(2^4 - 1) */
+  ecc_mod_pow2n_mul(m, TB, TA, TA, TC, 4);   /* [3] TB <-- c^(2^8 - 1) */
+  ecc_mod_pow2n_mul(m, TC, TB, TB, TA, 8);   /* [4] TC <-- c^(2^16 - 1) */
+  ecc_mod_pow2n_mul(m, TA, TC, TC, TB, 16);  /* [5] TA <-- c^(2^32 - 1) */
+  ecc_mod_pow2n_mul(m, TB, TA, cp, TC, 32);  /* [6] TB <-- c^(2^64 - 2^32 + 1) */
+  ecc_mod_pow2n_mul(m, TC, TB, cp, TA, 96);  /* [7] TC <-- c^(2^160 - 2^128 + 2^96 + 1) */
+  ecc_mod_pow2n    (m, rp, TC,     TA, 94);  /* [8] TB <-- c^(2^254 - 2^222 + 2^190 + 2^94) */
+
+#undef TA
+#undef TB
+#undef TC
+}
+
 const struct ecc_curve _nettle_secp_256r1 =
 {
   {
@@ -247,7 +281,7 @@ const struct ecc_curve _nettle_secp_256r1 =
     ECC_BMODP_SIZE,
     ECC_REDC_SIZE,
     ECC_MOD_INV_ITCH (ECC_LIMB_SIZE),
-    0,
+    6 * ECC_LIMB_SIZE,
 
     ecc_p,
     ecc_Bmodp,
@@ -259,6 +293,7 @@ const struct ecc_curve _nettle_secp_256r1 =
     USE_REDC ? ecc_256_redc : ecc_256_modp,
     ecc_mod_inv,
     NULL,
+    ecc_256_sqrt,
   },
   {
     256,
@@ -278,6 +313,7 @@ const struct ecc_curve _nettle_secp_256r1 =
     ecc_256_modq,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
 
   USE_REDC,
diff --git a/ecc-384.c b/ecc-384.c
index 94b8af91..f3303ec7 100644
--- a/ecc-384.c
+++ b/ecc-384.c
@@ -146,7 +146,58 @@ ecc_384_modp (const struct ecc_modulo *p, mp_limb_t *rp)
 #else
 #define ecc_384_modp ecc_mod
 #endif
+
+static void
+ecc_384_sqrt (const struct ecc_modulo *m,
+	      mp_limb_t *rp,
+	      const mp_limb_t *cp,
+	      mp_limb_t *scratch)
+{
+  mp_size_t size = ECC_LIMB_SIZE;
+
+  /* This computes the square root modulo p256 using the identity:
+
+     sqrt(c) = c^(2^382 − 2^126 - 2^94 + 2^30)  (mod P-384)
+
+     which is from Routine 3.2.12 of "Mathematical routines for the NIST prime elliptic curves", April 5, 2010, author unknown.
+  */
+
+  /* We use our scratch space for several temporaries, all of
+     which are 2*size long to allow for multiplication/squaring */
   
+#define T1 scratch
+#define T2 (scratch + 2*size)
+#define T3 (scratch + 4*size)
+#define T4 (scratch + 6*size)
+#define Tr T2  /* T2 and R are not live at the same time */
+#define Tx (scratch + 8*size)
+
+  ecc_mod_pow2n_mul(m, T1, cp, cp, T2, 1);   /* [ 1] T1 <- c^(2^1 - 1) */
+  ecc_mod_pow2n_mul(m, Tx, T1, T1, T3, 2);   /* [ 2] T2 <- c^(2^4 - 1) */
+  ecc_mod_pow2n_mul(m, T2, Tx, cp, T3, 1);   /* [ 3] T2 <- c^(2^5 - 1) */
+  ecc_mod_pow2n_mul(m, T3, T2, T2, Tx, 5);   /* [ 4] T3 <- c^(2^10 - 1) */
+  ecc_mod_pow2n_mul(m, T4, T3, T2, Tx, 5);   /* [ 5] T4 <- c^(2^15 - 1) */
+  ecc_mod_pow2n_mul(m, T2, T4, T4, Tx, 15);  /* [ 6] T2 <- c^(2^30 - 1) */
+  ecc_mod_pow2n    (m, T3, T2,     Tx, 2);   /* [ 7] T3 <- c^(2^32 - 4) */
+  ecc_mod_inplc_mul(m, T1, T3,     Tx);      /* [ 8] T1 <- c^(2^32 - 1) */
+  ecc_mod_pow2n    (m, T3, T3,     Tx, 28);  /* [ 9] T3 <- c^(2^60 - 2^30) */
+  ecc_mod_inplc_mul(m, T2, T3,     Tx);      /*      T2 <- c^(2^60 - 1) */
+  ecc_mod_pow2n_mul(m, T3, T2, T2, Tx, 60);  /* [10] T3 <- c^(2^120 - 1) */
+  ecc_mod_pow2n_mul(m, Tr, T3, T3, Tx, 120); /* [11] r  <- c^(2^240 - 1) */
+  ecc_mod_pow2n_mul(m, T3, Tr, T4, Tx, 15);  /* [12] r  <- c^(2^255 - 1) */
+  ecc_mod_pow2n_mul(m, Tr, T3, T1, Tx, 33);  /* [13] r  <- c^(2^288 - 2^32 - 1) */
+  ecc_mod_pow2n_mul(m, T3, Tr, cp, T1, 64);  /* [14] r  <- c^(2^352 - 2^96 - 2^64 + 1) */
+  ecc_mod_pow2n    (m, rp, T3,     T1, 30);  /* [15] r  <- c^(2^382 - 2^126 - 2^94 + 2^30) */
+
+#undef T1
+#undef T2
+#undef T3
+#undef T4
+#undef Tr
+#undef Tx
+}
+
+
 const struct ecc_curve _nettle_secp_384r1 =
 {
   {
@@ -155,7 +206,7 @@ const struct ecc_curve _nettle_secp_384r1 =
     ECC_BMODP_SIZE,
     ECC_REDC_SIZE,
     ECC_MOD_INV_ITCH (ECC_LIMB_SIZE),
-    0,
+    10 * ECC_LIMB_SIZE,
 
     ecc_p,
     ecc_Bmodp,
@@ -167,6 +218,7 @@ const struct ecc_curve _nettle_secp_384r1 =
     ecc_384_modp,
     ecc_mod_inv,
     NULL,
+    ecc_384_sqrt,
   },
   {
     384,
@@ -186,6 +238,7 @@ const struct ecc_curve _nettle_secp_384r1 =
     ecc_mod,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
 
   USE_REDC,
diff --git a/ecc-521.c b/ecc-521.c
index 52a018dd..cf1cd752 100644
--- a/ecc-521.c
+++ b/ecc-521.c
@@ -75,6 +75,22 @@ ecc_521_modp (const struct ecc_modulo *m UNUSED, mp_limb_t *rp)
 }
 #endif
 
+static void
+ecc_521_sqrt (const struct ecc_modulo *m,
+	      mp_limb_t *rp,
+	      const mp_limb_t *cp,
+	      mp_limb_t *scratch)
+{
+  /* This computes the square root modulo p256 using the identity:
+
+     sqrt(c) = c^(2^519) (mod P-521)
+  */
+
+  mpn_copyi(scratch, cp, m->size);
+  ecc_mod_pow2n(m, rp, scratch, scratch+m->size, 519);
+}
+
+
 const struct ecc_curve _nettle_secp_521r1 =
 {
   {
@@ -83,7 +99,7 @@ const struct ecc_curve _nettle_secp_521r1 =
     ECC_BMODP_SIZE,
     ECC_REDC_SIZE,
     ECC_MOD_INV_ITCH (ECC_LIMB_SIZE),
-    0,
+    3 * ECC_LIMB_SIZE,
 
     ecc_p,
     ecc_Bmodp,
@@ -95,6 +111,7 @@ const struct ecc_curve _nettle_secp_521r1 =
     ecc_521_modp,
     ecc_mod_inv,
     NULL,
+    ecc_521_sqrt,
   },
   {
     521,
@@ -114,6 +131,7 @@ const struct ecc_curve _nettle_secp_521r1 =
     ecc_mod,
     ecc_mod_inv,
     NULL,
+    NULL,
   },
   
   USE_REDC,
diff --git a/ecc-internal.h b/ecc-internal.h
index 94fc218b..6c98fc08 100644
--- a/ecc-internal.h
+++ b/ecc-internal.h
@@ -49,6 +49,9 @@
 #define ecc_mod_submul_1 _nettle_ecc_mod_submul_1
 #define ecc_mod_mul _nettle_ecc_mod_mul
 #define ecc_mod_sqr _nettle_ecc_mod_sqr
+#define ecc_mod_pow2n_mul _nettle_ecc_mod_pow2n_mul
+#define ecc_mod_pow2n _nettle_ecc_mod_pow2n
+#define ecc_mod_inplc_mul _nettle_ecc_mod_inplc_mul
 #define ecc_mod_random _nettle_ecc_mod_random
 #define ecc_mod _nettle_ecc_mod
 #define ecc_mod_inv _nettle_ecc_mod_inv
@@ -56,6 +59,7 @@
 #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
+#define ecc_point_recover_y _nettle_ecc_point_recover_y
 #define ecc_dup_jj _nettle_ecc_dup_jj
 #define ecc_add_jja _nettle_ecc_add_jja
 #define ecc_add_jjj _nettle_ecc_add_jjj
@@ -97,6 +101,7 @@ extern const struct ecc_curve _nettle_curve25519;
 #define ECC_MUL_A_EH_WBITS 4
 
 struct ecc_modulo;
+struct ecc_point;
 
 /* Reduces from 2*ecc->size to ecc->size. */
 /* Required to return a result < 2q. This property is inherited by
@@ -113,6 +118,13 @@ typedef int ecc_mod_sqrt_func (const struct ecc_modulo *m,
 			       const mp_limb_t *up, const mp_limb_t *vp,
 			       mp_limb_t *scratch);
 
+/* Computes the square root of u (mod p).
+   2*size space must be available at rp. up and rp may overlap, but must not overlap with scratch. */
+typedef void ecc_mod_sqrt_func2 (const struct ecc_modulo *m,
+				 mp_limb_t *rp,
+				 const mp_limb_t *up,
+				 mp_limb_t *scratch);
+
 typedef void ecc_add_func (const struct ecc_curve *ecc,
 			   mp_limb_t *r,
 			   const mp_limb_t *p, const mp_limb_t *q,
@@ -155,6 +167,7 @@ struct ecc_modulo
   ecc_mod_func *reduce;
   ecc_mod_inv_func *invert;
   ecc_mod_sqrt_func *sqrt;
+  ecc_mod_sqrt_func2 *sqrt2;
 };
 
 /* Represents an elliptic curve of the form
@@ -234,7 +247,7 @@ void
 ecc_mod_submul_1 (const struct ecc_modulo *m, mp_limb_t *rp,
 		  const mp_limb_t *ap, mp_limb_t b);
 
-/* NOTE: mul and sqr needs 2*ecc->size limbs at rp */
+/* NOTE: mul, sqr, pow2n[_mul] all need 2*ecc->size limbs at rp */
 void
 ecc_mod_mul (const struct ecc_modulo *m, mp_limb_t *rp,
 	     const mp_limb_t *ap, const mp_limb_t *bp);
@@ -243,6 +256,20 @@ void
 ecc_mod_sqr (const struct ecc_modulo *m, mp_limb_t *rp,
 	     const mp_limb_t *ap);
 
+void
+ecc_mod_pow2n_mul (const struct ecc_modulo *m,
+		   mp_limb_t *dst, const mp_limb_t *src1, const mp_limb_t *src2,
+		   mp_limb_t *scratch, unsigned scount);
+void
+ecc_mod_pow2n (const struct ecc_modulo *m,
+	       mp_limb_t *dst, const mp_limb_t *src,
+	       mp_limb_t *scratch, unsigned scount);
+void
+ecc_mod_inplc_mul(const struct ecc_modulo *m,
+		  mp_limb_t *srcdst,
+		  const mp_limb_t *src2,
+		  mp_limb_t *scratch);
+
 #define ecc_modp_add(ecc, r, a, b) \
   ecc_mod_add (&(ecc)->p, (r), (a), (b))
 #define ecc_modp_sub(ecc, r, a, b) \
@@ -298,6 +325,12 @@ ecc_eh_to_a (const struct ecc_curve *ecc,
 	     mp_limb_t *r, const mp_limb_t *p,
 	     mp_limb_t *scratch);
 
+/* Reconstructs the y-coordinate of a curve point in affine
+   coordinates, based on its x-value and ~y (which indicates the
+   parity/sign of the y-coordinate) */
+int
+ecc_point_recover_y(struct ecc_point *p, int y_sign);
+
 /* Group operations */
 
 /* Point doubling, with jacobian input and output. Corner cases:
diff --git a/ecc-mod-arith.c b/ecc-mod-arith.c
index f2e47f67..3f94b47d 100644
--- a/ecc-mod-arith.c
+++ b/ecc-mod-arith.c
@@ -125,3 +125,60 @@ ecc_mod_sqr (const struct ecc_modulo *m, mp_limb_t *rp,
   mpn_sqr (rp, ap, m->size);
   m->reduce (m, rp);
 }
+
+/* Computes src1 ^ ( 2 ^ scount ) * src2 */
+/* dst and scratch must both have 2*size space */
+void
+ecc_mod_pow2n_mul (const struct ecc_modulo *m,
+		   mp_limb_t * dst,
+		   const mp_limb_t * src1,
+		   const mp_limb_t * src2,
+		   mp_limb_t * scratch, unsigned scount)
+{
+  ecc_mod_pow2n (m, scratch, src1, dst, scount);
+  mpn_mul (dst, scratch, m->size, src2, m->size);
+  m->reduce (m, dst);
+}
+
+/* computes x <- x * y; performs an extra copy since mpn_mul can't share src and dst buffers */
+void
+ecc_mod_inplc_mul (const struct ecc_modulo *m,
+		   mp_limb_t * srcdst,
+		   const mp_limb_t * src2, mp_limb_t * scratch)
+{
+  mpn_mul (scratch, srcdst, m->size, src2, m->size);
+  m->reduce (m, scratch);
+  mpn_copyi (srcdst, scratch, m->size);
+}
+
+void
+ecc_mod_pow2n (const struct ecc_modulo *m,
+	       mp_limb_t * dst,
+	       const mp_limb_t * src, mp_limb_t * scratch, unsigned scount)
+{
+  mp_size_t size = m->size;
+
+  if (scount & 1)
+    {
+      mpn_sqr (dst, src, size);
+      m->reduce (m, dst);
+      scount -= 1;
+    }
+  else
+    {
+      mpn_sqr (scratch, src, size);
+      m->reduce (m, scratch);
+      mpn_sqr (dst, scratch, size);
+      m->reduce (m, dst);
+      scount -= 2;
+    }
+
+  while (scount)
+    {
+      mpn_sqr (scratch, dst, size);
+      m->reduce (m, scratch);
+      mpn_sqr (dst, scratch, size);
+      m->reduce (m, dst);
+      scount -= 2;
+    }
+}
diff --git a/ecc-point-compact.c b/ecc-point-compact.c
new file mode 100644
index 00000000..bb9d8df5
--- /dev/null
+++ b/ecc-point-compact.c
@@ -0,0 +1,51 @@
+/* ecc-point-compact.c
+
+   Copyright (C) 2019 Wim Lewis
+
+   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 "ecc.h"
+#include "ecc-internal.h"
+
+int
+ecc_point_set_compact (struct ecc_point *p, const mpz_t x, int y_sign)
+{
+  const struct ecc_curve *curve = p->ecc;
+  mp_size_t size = curve->p.size;
+
+  if (mpz_sgn (x) < 0 || mpz_limbs_cmp (x, curve->p.m, size) >= 0)
+    return 0;
+
+  mpz_limbs_copy (p->p, x, size);
+
+  return ecc_point_recover_y (p, y_sign);
+}
diff --git a/ecc-point-from-octets.c b/ecc-point-from-octets.c
new file mode 100644
index 00000000..4705dab6
--- /dev/null
+++ b/ecc-point-from-octets.c
@@ -0,0 +1,111 @@
+/* ecc-point-from-octets.c
+
+   Copyright (C) 2019 Wim Lewis
+
+   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 "ecc.h"
+#include "ecc-internal.h"
+
+static int
+import_mp (const struct ecc_modulo *m, mp_limb_t * dst, size_t len,
+	   const uint8_t * buf)
+{
+  mp_size_t sz = mpn_set_str (dst, buf, len, 256);
+  mp_size_t msize = m->size;
+
+  if (sz < msize)
+    {
+      mpn_zero (dst + sz, msize - sz);
+    }
+
+  if (mpn_cmp (dst, m->m, msize) >= 0)
+    return 0;
+  else
+    return 1;
+}
+
+int
+ecc_point_set_from_octets (struct ecc_point *p, size_t len,
+			   const uint8_t * buf)
+{
+  const struct ecc_curve *curve = p->ecc;
+  size_t byte_size = (7 + curve->p.bit_size) / 8;
+  int res;
+
+  if (len < 1 + byte_size)
+    {
+      /* This might be the point at infinity (which is represented as
+         just the octet 0x00), but we can't represent that in affine
+         coordinates */
+      return 0;
+    }
+
+  if (buf[0] == 0x02 || buf[0] == 0x03)
+    {
+      /* Compressed point */
+      if (len != 1 + byte_size)
+	return 0;
+
+      if (!import_mp (&(curve->p), p->p, len, buf))
+	return 0;
+
+      return ecc_point_recover_y (p, (buf[0] == 0x02 ? 0 : 1));
+    }
+
+  if (buf[0] == 0x04 || buf[0] == 0x06 || buf[0] == 0x07)
+    {
+      /* Uncompressed point, or hybrid point */
+      if (len != 1 + 2 * byte_size)
+	return 0;
+
+      mpz_t x, y;
+
+      nettle_mpz_init_set_str_256_u (x, byte_size, buf + 1);
+      nettle_mpz_init_set_str_256_u (y, byte_size, buf + 1 + byte_size);
+
+      res = ecc_point_set (p, x, y);
+
+      mpz_clear (x);
+      mpz_clear (y);
+
+      /* If this was a "hybrid" point, check that the y-sign flag was
+         consistent */
+      if (buf[0] == ((p->p[curve->p.size] & 1) ? 0x06 : 0x07))
+	return 0;
+
+      return res;
+    }
+
+  /* Unrecognized point format */
+  return 0;
+}
diff --git a/ecc-point-recover.c b/ecc-point-recover.c
new file mode 100644
index 00000000..77536567
--- /dev/null
+++ b/ecc-point-recover.c
@@ -0,0 +1,183 @@
+/* ecc-point-recover.c
+
+   Copyright (C) 2019 Wim Lewis
+
+   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 "ecc.h"
+#include "ecc-internal.h"
+#include "gmp-glue.h"
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+static int
+ecc_recover_y_weierstrass_itch (const struct ecc_curve *c)
+{
+  const struct ecc_modulo *m = &(c->p);
+  mp_size_t size = m->size;
+  return 3 * (m->size) + MAX (2 * size, m->sqrt_itch);
+}
+
+/* Test whether a and b are congruent.
+   "a" and "b" may be in the range [0, 2p) (that is, almost modulo-reduced, 
+   but may contain one multiple of p).
+
+   The "b" input is clobbered.
+*/
+static int
+congruent (const struct ecc_modulo *p, const mp_limb_t * a, mp_limb_t * b)
+{
+  mp_size_t size = p->size;
+  int cy;
+
+  cy = mpn_sub_n (b, b, a, size);
+
+  if (cy == 0)
+    {
+      /* b was larger than or equal to a; if they were congruent, b now contains either 0 or p. */
+      return (mpn_zero_p (b, size) || (mpn_cmp (b, p->m, size) == 0));
+    }
+  else
+    {
+      /* b was smaller than a; if they were congruent, b now contains -p. */
+      cy = mpn_add_n (b, b, p->m, size);
+      if (!cy)
+	return 0;
+      if (mpn_zero_p (b, size))
+	return 1;
+      return 0;
+    }
+}
+
+static int
+ecc_recover_y_weierstrass (const struct ecc_curve *c,
+			   mp_limb_t * x_y, int y_sign, mp_limb_t * scratch)
+{
+  const struct ecc_modulo *m = &(c->p);
+  mp_size_t size = m->size;
+  mp_limb_t *rp = x_y + size;	/* result buffer: second half of X || Y */
+  mp_limb_t cy;
+
+  /* Compute y^2 = x^3 - 3*x + b (mod p) */
+  mpn_sqr (scratch, x_y, size);
+  m->mod (m, scratch);
+  if (mpn_sub_1 (scratch, scratch, size, (mp_limb_t) 3))
+    {
+      mpn_add (scratch, scratch, size, m->m, size);
+    }
+  mpn_mul (scratch + size, scratch, size, x_y, size);
+  cy = mpn_add (scratch + size, scratch + size, 2 * size, c->b, size);
+  assert (cy == 0);
+  m->mod (m, scratch + size);
+
+  /* At this point, y^2 is at scratch+size */
+
+  if (c->use_redc)
+    {
+      /* Convert to Montgomery form (at scratch+0) */
+      mpn_zero (scratch, size);
+      m->mod (m, scratch);
+
+      /* Square root */
+      m->sqrt2 (m, scratch + size, scratch, scratch + 3 * size);
+
+      /* Square the result to verify we started with an actual square */
+      /* (this tests that the point we're constructing is actually on the curve) */
+      ecc_mod_sqr (m, scratch + 2 * size, scratch + size);
+      if (!congruent (m, scratch, scratch + 2 * size))
+	return 0;
+
+      /* Convert the result from Montgomery back to conventional form */
+      mpn_zero (scratch + 2 * size, size);
+      m->reduce (m, scratch + size);
+    }
+  else
+    {
+      /* Save a copy */
+      mpn_copyi (scratch, scratch + size, size);
+
+      /* Square root */
+      m->sqrt2 (m, scratch + size, scratch + size, scratch + 3 * size);
+
+      /* Verify that the computed value is in fact a root of y^2; this tests that the point we're constructing is actually on the curve */
+      ecc_mod_sqr (m, scratch + 2 * size, scratch + size);
+      if (!congruent (m, scratch, scratch + 2 * size))
+	return 0;
+    }
+
+  /* The computed Y value is now at scratch+size */
+
+  /* Both reduce and mod can leave an extra `p` in their result */
+  if (mpn_cmp (scratch + size, m->m, size) >= 0)
+    mpn_sub_n (rp, scratch + size, m->m, size);
+  else
+    mpn_copyi (rp, scratch + size, size);
+
+  /* Check whether we need to negate it to get the other root */
+  if ((rp[0] & 1) ? !y_sign : y_sign)
+    {
+      /* Zero is its own negation, which means we can't satisfy this request */
+      /* TODO: Check whether any of our curves intersect y=0? P-256 doesn't. */
+      if (mpn_zero_p (rp, size))
+	return 0;
+
+      cy = mpn_sub (rp, m->m, size, rp, size);
+      assert (cy == 0);
+    }
+
+  return 1;
+}
+
+int
+ecc_point_recover_y (struct ecc_point *p, int y_sign)
+{
+  const struct ecc_curve *curve = p->ecc;
+
+  if (curve->p.bit_size == 255)
+    {
+      /* ed25519 special case. FIXME: Do in some cleaner way? */
+      /* We don't implement point decompression for Ed25519 curves because the
+         standard representation for that algorithm uses a different
+         compression style */
+      return 0;
+    }
+  else
+    {
+      mp_size_t itch = ecc_recover_y_weierstrass_itch (curve);
+      mp_limb_t *scratch = gmp_alloc_limbs (itch);
+      int res = ecc_recover_y_weierstrass (curve, p->p, y_sign, scratch);
+      gmp_free_limbs (scratch, itch);
+
+      return res;
+    }
+}
diff --git a/ecc.h b/ecc.h
index 93fc9e87..6d228097 100644
--- a/ecc.h
+++ b/ecc.h
@@ -45,6 +45,8 @@ extern "C" {
 #define ecc_point_init nettle_ecc_point_init
 #define ecc_point_clear nettle_ecc_point_clear
 #define ecc_point_set nettle_ecc_point_set
+#define ecc_point_set_compact nettle_ecc_point_set_compact
+#define ecc_point_set_from_octets nettle_ecc_point_set_from_octets
 #define ecc_point_get nettle_ecc_point_get
 #define ecc_point_mul nettle_ecc_point_mul
 #define ecc_point_mul_g nettle_ecc_point_mul_g
@@ -90,7 +92,11 @@ int
 ecc_point_set (struct ecc_point *p, const mpz_t x, const mpz_t y);
 void
 ecc_point_get (const struct ecc_point *p, mpz_t x, mpz_t y);
-
+int
+ecc_point_set_compact (struct ecc_point *p, const mpz_t x, int y);
+int
+ecc_point_set_from_octets (struct ecc_point *p, size_t len, const uint8_t *buf);
+  
 void
 ecc_scalar_init (struct ecc_scalar *s, const struct ecc_curve *ecc);
 void
diff --git a/testsuite/.gitignore b/testsuite/.gitignore
index 4d680cd1..d0df5a86 100644
--- a/testsuite/.gitignore
+++ b/testsuite/.gitignore
@@ -31,6 +31,8 @@
 /ecc-modinv-test
 /ecc-mul-a-test
 /ecc-mul-g-test
+/ecc-point-compact-test
+/ecc-point-from-octets-test
 /ecc-redc-test
 /ecc-sqrt-test
 /ecdh-test
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in
index 9a1fe209..a4d52493 100644
--- a/testsuite/Makefile.in
+++ b/testsuite/Makefile.in
@@ -46,7 +46,8 @@ TS_HOGWEED_SOURCES = sexp-test.c sexp-format-test.c \
 		     dsa-test.c dsa-keygen-test.c \
 		     curve25519-dh-test.c \
 		     ecc-mod-test.c ecc-modinv-test.c ecc-redc-test.c \
-		     ecc-sqrt-test.c \
+		     ecc-sqrt-test.c ecc-point-compact-test.c \
+		     ecc-point-from-octets-test.c \
 		     ecc-dup-test.c ecc-add-test.c \
 		     ecc-mul-g-test.c ecc-mul-a-test.c \
 		     ecdsa-sign-test.c ecdsa-verify-test.c \
diff --git a/testsuite/ecc-point-compact-test.c b/testsuite/ecc-point-compact-test.c
new file mode 100644
index 00000000..94f2a267
--- /dev/null
+++ b/testsuite/ecc-point-compact-test.c
@@ -0,0 +1,308 @@
+/* ecc-point-compact-test.c
+
+   Copyright (C) 2019 Wim Lewis
+
+   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/.
+*/
+
+#include "testutils.h"
+
+#include "ecc.h"
+#include "ecc-internal.h"
+
+static void
+dump_testcase_info (const struct ecc_curve *ecc, mpz_t x, int y_sign)
+{
+  fprintf (stderr, "Curve   = %d bits\nx       = ", ecc->p.bit_size);
+  mpz_out_str (stderr, 16, x);
+  fprintf (stderr, "\ny-sign  = ");
+  if (y_sign)
+    {
+      fprintf (stderr, "negative (odd)\n");
+    }
+  else
+    {
+      fprintf (stderr, "positive (even)\n");
+    }
+}
+
+static void
+dump_result_point (const struct ecc_point *pt)
+{
+  fprintf (stderr, "Computed values:\nx     = ");
+  mpz_t x, y;
+  mpz_init (x);
+  mpz_init (y);
+  ecc_point_get (pt, x, y);
+  mpz_out_str (stderr, 16, x);
+  fprintf (stderr, "\ny     = ");
+  mpz_out_str (stderr, 16, y);
+  fprintf (stderr, "\n");
+  mpz_clear (x);
+  mpz_clear (y);
+}
+
+static void
+test_point_reconstruction (const struct ecc_curve *ecc,
+			   /* X-value */
+			   const char *xv,
+			   /* Y sign bit */
+			   int y_sign,
+			   /* Expected Y-value, or NULL to expect failure */
+			   const char *yv)
+{
+  mpz_t x, y_in, x_found, y_found;
+  struct ecc_point pt;
+  int res;
+
+  mpz_init_set_str (x, xv, 16);
+  if (yv)
+    {
+      mpz_init_set_str (y_in, yv, 16);
+    }
+  else
+    {
+      mpz_init (y_in);
+    }
+  mpz_init (x_found);
+  mpz_init (y_found);
+
+  ecc_point_init (&pt, ecc);
+
+  res = ecc_point_set_compact (&pt, x, y_sign);
+  if (res > 0)
+    {
+      if (yv == NULL)
+	{
+	  fprintf (stderr,
+		   "ecc_point_set_compact() succeeded when it should have failed\n");
+	  dump_testcase_info (ecc, x, y_sign);
+	  dump_result_point (&pt);
+	  abort ();
+	}
+
+      ecc_point_get (&pt, x_found, y_found);
+      if (mpz_cmp (y_in, y_found) != 0)
+	{
+	  fprintf (stderr, "ecc_point_set_compact() produced incorrect Y\n");
+	  dump_testcase_info (ecc, x, y_sign);
+	  fprintf (stderr, "y_in  = ");
+	  mpz_out_str (stderr, 16, y_in);
+	  fprintf (stderr, "\n");
+	  dump_result_point (&pt);
+	  abort ();
+	}
+
+      if (mpz_cmp (x, x_found) != 0)
+	{
+	  fprintf (stderr, "ecc_point_set_compact() produced incorrect X\n");
+	  dump_testcase_info (ecc, x, y_sign);
+	  dump_result_point (&pt);
+	  abort ();
+	}
+    }
+  else
+    {
+      if (yv != NULL)
+	{
+	  fprintf (stderr,
+		   "ecc_point_set_compact() failed when it should have succeeded\n");
+	  dump_testcase_info (ecc, x, y_sign);
+	  abort ();
+	}
+    }
+
+  ecc_point_clear (&pt);
+  mpz_clear (x_found);
+  mpz_clear (y_found);
+  mpz_clear (x);
+  mpz_clear (y_in);
+}
+
+static void
+test_secp256r1_compact (void)
+{
+  const struct ecc_curve *ecc = &_nettle_secp_256r1;
+
+  /* The generator point */
+  test_point_reconstruction (ecc,
+			     "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
+			     1,
+			     "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5");
+
+  /* Some randomly generated public key points */
+  test_point_reconstruction (ecc,
+			     "bd937cced2832294ef2373921b9a93dc6654a42d4acc0f19cd2f8b5ebc4976cb",
+			     1,
+			     "1566e322666211348e8733781c8815d4edf8fb16b71ac02d244eee5d128605d9");
+  test_point_reconstruction (ecc,
+			     "2b0c788d3b01a1d16cb45b74301f9be9bb50632279980483ee23ab2ec88abf6e",
+			     0,
+			     "5eb630230b9ece843f25f2581e6b22137224607f2ec25fae44262844bfe2635a");
+
+  /* Invalid public keys: from Google Wycheproof */
+  test_point_reconstruction (ecc,
+			     "fd4bf61763b46581fd9174d623516cf3c81edd40e29ffa2777fb6cb0ae3ce535",
+			     0, NULL);
+  test_point_reconstruction (ecc,
+			     "efdde3b32872a9effcf3b94cbf73aa7b39f9683ece9121b9852167f4e3da609b",
+			     1, NULL);
+
+  /* Special cses tailored to our implementation (thanks to Wolfram Alpha for computations) */
+
+  /* Incur an annoying underflow in computation of Y */
+  test_point_reconstruction (ecc,
+			     "507442007322aa895340cba4abc2d730bfd0b16c2c79a46815f8780d2c55a2dd",
+			     0,
+			     "b9e6295f66bf0aea9c55edc7e4383098742e58d6b6043ee4f49634dd2e5060d2");
+
+  /* X = 0 */
+  test_point_reconstruction (ecc,
+			     "0000000000000000000000000000000000000000000000000000000000000000",
+			     0,
+			     "66485c780e2f83d72433bd5d84a06bb6541c2af31dae871728bf856a174f93f4");
+
+  /* Y = 1 */
+  test_point_reconstruction (ecc,
+			     "09e78d4ef60d05f750f6636209092bc43cbdd6b47e11a9de20a9feb2a50bb96c",
+			     1,
+			     "0000000000000000000000000000000000000000000000000000000000000001");
+
+  /* Y = sqrt(-1), not on the curve, maximal intermediate value */
+  test_point_reconstruction (ecc,
+			     "5a7791a91a311985212b9666ee834b704fdc69b0936c0cc43baa5875cf7f75c8",
+			     1, NULL);
+
+  /* As above, but large intermediate value in Montgomery representation with B = 2^256 */
+  test_point_reconstruction (ecc,
+			     "21747cc7b9bb570fa3c1d8a29577ad5912426f033e3f675719577db83a1f01f7",
+			     0, NULL);
+  test_point_reconstruction (ecc,
+			     "4538f734977b56ee3e90841be9dae751f024b109eb7b83025cf2099f0764a3b7",
+			     0,
+			     "32fb1b2b0f1d1a553e1a328d84ad8f00228d555aad10b3ef7da720642f4b0680");
+}
+
+static void
+test_secp384r1_compact (void)
+{
+  const struct ecc_curve *ecc = &_nettle_secp_384r1;
+
+  /* The generator point */
+  test_point_reconstruction (ecc,
+			     "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7",
+			     1,
+			     "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f");
+
+  /* Some vectors from Wycheproof */
+  test_point_reconstruction (ecc,
+			     "6fcaf82d982d222d6096ba83e55b1c7dcb71a41e88f323333f44284d95c4bd3616da7a1bef928f31c26f885ba7adb487",
+			     1,
+			     "826fde2ed9f5649c11cf8465f8bf8ad50f68914936fc39666f68219d066506bea4001fdc816c9a90e7e2afb19bea085f");
+  test_point_reconstruction (ecc,
+			     "4424530ea70bace90601f8d5869e4179a6cd689b6a18fdfec50cecf17cb836d24820211ada67815b42c2c2606303f69e",
+			     0, NULL);
+  test_point_reconstruction (ecc,
+			     "0000000000000000000000000000000000000000000000000000000036a2907c00000000000000000000000000000000",
+			     0,
+			     "ffffffff80a84965feb87c2405b6984d06305987590f4916302be9b7313a4c3a6718deac25c07d2c25d17161710c84ee");
+  test_point_reconstruction (ecc,
+			     "0000000000000000000000000000000000000000000000000000000036a2907c00000000000000000000000000000000",
+			     1,
+			     "000000007f57b69a014783dbfa4967b2f9cfa678a6f0b6e9cfd41648cec5b3c498e72152da3f82d3da2e8e9f8ef37b11");
+  test_point_reconstruction (ecc,
+			     "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffe",
+			     1,
+			     "732152442fb6ee5c3e6ce1d920c059bc623563814d79042b903ce60f1d4487fccd450a86da03f3e6ed525d02017bfdb3");
+  test_point_reconstruction (ecc,
+			     "0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff00000000000000010000000000000001",
+			     0,
+			     "141b9ee5310ea8170131b604484a6d677ed42576045b7143c026710ae92b277afbbea0c4458c220d561e69404dc7d888");
+}
+
+static void
+test_secp521r1_compact (void)
+{
+  const struct ecc_curve *ecc = &_nettle_secp_521r1;
+
+  /* The generator point */
+  test_point_reconstruction (ecc,
+			     "0c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3d"
+			     "baa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
+			     0,
+			     "11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e66"
+			     "2c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650");
+
+  /* Special cases tailored to our implementation (thanks to Wolfram Alpha for computations) */
+  test_point_reconstruction (ecc,
+			     "18bde04c38d7081cf87b4b9526163c3cead63de0cfcdb3acaedeb830fadda25fe"
+			     "bcd096cb771c0bc95e3eaaeab69c94e66296108383a913caf87830fc19f410ba3e",
+			     0, NULL /* Y = sqrt(-1) */ );
+  test_point_reconstruction (ecc,
+			     "0d9cb7a32dab342f863edb340f3ea61ddf833e755ce66bb1a918a42714ba05bcd"
+			     "f4ff10994f616a9d80cd0b48b326e3a8a2a8f5634d824875b6e71fb7cddd7b5018",
+			     1, "1");
+
+  /* Some Wycheproof edge cases */
+  test_point_reconstruction (ecc,
+			     "00000000000000000000000000000000000000000000000000000000000000000"
+			     "000000000000000000000000000000000000000000000000000000000000000001",
+			     0,
+			     "010e59be93c4f269c0269c79e2afd65d6aeaa9b701eacc194fb3ee03df47849bf"
+			     "550ec636ebee0ddd4a16f1cd9406605af38f584567770e3f272d688c832e843564");
+  test_point_reconstruction (ecc,
+			     "1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+			     "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd",
+			     0,
+			     "010e59be93c4f269c0269c79e2afd65d6aeaa9b701eacc194fb3ee03df47849bf"
+			     "550ec636ebee0ddd4a16f1cd9406605af38f584567770e3f272d688c832e843564");
+  test_point_reconstruction (ecc,
+			     "1ff00000000000000000000000000000000ffffffffffffffffffffffffffffff"
+			     "ff0000000000000000000000000000000100000000000000000000000000000000",
+			     1,
+			     "0b5e1191b449fa1ebdbd677daa48f90e2d1d6c058c877087cafd9364d99dbb283"
+			     "c68402e6e6c5f5411b2ed42824d8b280ceb910aba6847883a7e3780e2132af41c1");
+
+  /* Some invalid points */
+  test_point_reconstruction (ecc,
+			     "047b9cf28e04b38796858545d60d6133fbdc20ede086e5d95111c982b8c276628"
+			     "235e536c075637a97c0a6c30d02b83b19e578203473eea16dfdeaeccb1dc0d9b19",
+			     1, NULL);
+  test_point_reconstruction (ecc,
+			     "0429cb431c18f5f4e4e502f74214e6ac5ec2c3f86b830bac24de95feae142ca7d"
+			     "9aa8aa5b34f55af4b2848f2e6ba6df4c3ecd401a1d7b2a8287a332b202196fadbb",
+			     0, NULL);
+
+}
+
+void
+test_main (void)
+{
+  test_secp256r1_compact ();
+  test_secp384r1_compact ();
+  test_secp521r1_compact ();
+}
diff --git a/testsuite/ecc-point-from-octets-test.c b/testsuite/ecc-point-from-octets-test.c
new file mode 100644
index 00000000..c0501f6a
--- /dev/null
+++ b/testsuite/ecc-point-from-octets-test.c
@@ -0,0 +1,206 @@
+/* ecc-point-compact-test.c
+
+   Copyright (C) 2019 Wim Lewis
+
+   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/.
+*/
+
+#include "testutils.h"
+
+#include "ecc.h"
+#include "base16.h"
+
+static void
+test_point_from_octets (const struct ecc_curve *ecc,
+			/* External representation */
+			const char *pt,
+			/* Expected X and Y */
+			const char *xv, const char *yv)
+{
+  struct ecc_point p;
+  int res;
+  struct base16_decode_ctx hexc;
+  uint8_t *pbuf;
+  size_t pbuf_capacity, pbuf_len;
+
+  base16_decode_init (&hexc);
+  pbuf_capacity = BASE16_DECODE_LENGTH (strlen (pt));
+  pbuf = malloc (pbuf_capacity + 1);
+  pbuf_len = 0;
+  if (!base16_decode_update (&hexc, &pbuf_len, pbuf, strlen (pt), pt) ||
+      !base16_decode_final (&hexc))
+    {
+      abort ();
+    }
+
+  ecc_point_init (&p, ecc);
+  res = ecc_point_set_from_octets (&p, pbuf_len, pbuf);
+  free (pbuf);
+
+  if (res)
+    {
+      mpz_t xexpected, xfound, yexpected, yfound;
+
+      if (!xv)
+	{
+	  fprintf (stderr,
+		   "ecc_point_set_from_octets succeeded when it should have failed.\npt = \"%s\"\n",
+		   pt);
+	  abort ();
+	}
+
+      mpz_init (xfound);
+      mpz_init (yfound);
+      ecc_point_get (&p, xfound, yfound);
+
+      mpz_init_set_str (xexpected, xv, 16);
+      mpz_init_set_str (yexpected, yv, 16);
+
+      if (mpz_cmp (xexpected, xfound) != 0)
+	{
+	  fprintf (stderr, "X-coordinate mismatch\n");
+	  res = 0;
+	}
+      if (mpz_cmp (yexpected, yfound) != 0)
+	{
+	  fprintf (stderr, "Y-coordinate mismatch\n");
+	  res = 0;
+	}
+
+      if (!res)
+	abort ();
+
+      mpz_clear (xfound);
+      mpz_clear (yfound);
+      mpz_clear (xexpected);
+      mpz_clear (yexpected);
+    }
+  else
+    {
+      if (xv)
+	{
+	  fprintf (stderr,
+		   "ecc_point_set_from_octets failed when it should have succeeded.\npt = \"%s\"\n",
+		   pt);
+	  abort ();
+	}
+    }
+}
+
+void
+test_main (void)
+{
+
+  test_point_from_octets (&_nettle_secp_256r1, "", NULL, NULL);
+
+  /* Technically a valid point representation, but it's the point at
+     infinity, which we can't represent in affine coordinates and
+     which isn't a valid public key. */
+  test_point_from_octets (&_nettle_secp_256r1, "00", NULL, NULL);
+
+  test_point_from_octets (&_nettle_secp_256r1, "02", NULL, NULL);
+  test_point_from_octets (&_nettle_secp_256r1, "04", NULL, NULL);
+  test_point_from_octets (&_nettle_secp_256r1, "06", NULL, NULL);
+
+  /* A valid point */
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "04"
+			  "450b6b6e2097178e9d2850109518d28eb3b6ded2922a5452003bc2e4a4ec775c"
+			  "894e90f0df1b0e6cadb03b9de24f6a22d1bd0a4a58cd645c273cae1c619bfd61",
+			  "450b6b6e2097178e9d2850109518d28eb3b6ded2922a5452003bc2e4a4ec775c",
+			  "894e90f0df1b0e6cadb03b9de24f6a22d1bd0a4a58cd645c273cae1c619bfd61");
+
+  /* A point not on the curve */
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "04"
+			  "450b6b6e2097178e9d2850109518d28eb3b6ded2922a5452003bc2e4a4ec775c"
+			  "894e90f0df1b0e6cadb03b9de24f6a22d1cd0a4a58cd645c273cae1c619bfd61",
+			  NULL, NULL);
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "04"
+			  "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
+			  "0000000000000000000000000000000000000000000000000000000000000000",
+			  NULL, NULL);
+
+  /* Compressed points */
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "0362d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f26",
+			  "62d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f26",
+			  "ac333a93a9e70a81cd5a95b5bf8d13990eb741c8c38872b4a07d275a014e30cf");
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "02"
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62",
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62",
+			  "080d36bd224d7405509295eed02a17150e03b314f96da37445b0d1d29377d12c");
+
+  /* An invalid compressed point (wrong length) */
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "02"
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e",
+			  NULL, NULL);
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "02"
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e625a",
+			  NULL, NULL);
+
+  test_point_from_octets (&_nettle_secp_384r1,
+			  "02"
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62",
+			  NULL, NULL);
+
+  /* Hybrid points, and internally-inconsistent hybrid points */
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "06"
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62"
+			  "080d36bd224d7405509295eed02a17150e03b314f96da37445b0d1d29377d12c",
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62",
+			  "080d36bd224d7405509295eed02a17150e03b314f96da37445b0d1d29377d12c");
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "07"
+			  "58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62"
+			  "080d36bd224d7405509295eed02a17150e03b314f96da37445b0d1d29377d12c",
+			  NULL, NULL);
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "07"
+			  "e9484e58f3331b66ffed6d90cb1c78065fa28cfba5c7dd4352013d3252ee4277"
+			  "bd7503b045a38b4b247b32c59593580f39e6abfa376c3dca20cf7f9cfb659e13",
+			  "e9484e58f3331b66ffed6d90cb1c78065fa28cfba5c7dd4352013d3252ee4277",
+			  "bd7503b045a38b4b247b32c59593580f39e6abfa376c3dca20cf7f9cfb659e13");
+
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "06"
+			  "e9484e58f3331b66ffed6d90cb1c78065fa28cfba5c7dd4352013d3252ee4277"
+			  "bd7503b045a38b4b247b32c59593580f39e6abfa376c3dca20cf7f9cfb659e13",
+			  NULL, NULL);
+
+  test_point_from_octets (&_nettle_secp_256r1,
+			  "07"
+			  "e9484e58f3331b66ffed6d90cb1c78065fa28cfba5c7dd4352013d3252ee4277"
+			  "bd7503b045a38b4b247b32c59593581f39e6abfa376c3dca20cf7f9cfb659e13",
+			  NULL, NULL);
+
+}
-- 
GitLab