[PATCH] ecc: Add Curve448.

NIIBE Yutaka gniibe at fsij.org
Fri Oct 11 05:23:30 CEST 2019


Hello,

It's not yet finished, but I post update of my work.

I modified my patch so that it's more friendly to X488 API.
That is:

    * A scalar is fixed-size little-endian.
    * A point is represented in fixed-size little-endian.
      (with no prefix)

I think that current libgcrypt + GnuPG implementation have an issue
around handling of those scalar and point as an MPI.  I'll describe
this issue in another mail.

--------------------------
diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c
index 4a3139c3..1e0d3e55 100644
--- a/cipher/ecc-curves.c
+++ b/cipher/ecc-curves.c
@@ -54,9 +54,9 @@ static const struct
     { "Ed448",      "1.3.101.113" },         /* rfc8410 */
 
     { "X22519",     "1.3.101.110" },         /* rfc8410 */
+#endif
 
     { "X448",       "1.3.101.111" },         /* rfc8410 */
-#endif
 
     { "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID  */
     { "NIST P-192", "prime192v1" },          /* X9.62 name.  */
@@ -162,6 +162,22 @@ static const ecc_domain_parms_t domain_parms[] =
        * the function _gcry_ecc_fill_in_curve.
        */
     },
+    {
+      /* (y^2 = x^3 + 156326*x^2 + x) */
+      "X448", 448, 0,
+      MPI_EC_MONTGOMERY, ECC_DIALECT_SAFECURVE,
+      "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+      "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+      "0x98A9",
+      "0x01",
+      "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+      "7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3",
+      "0x00000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000005",
+      "0x7D235D1295F5B1F66C98AB6E58326FCECBAE5D34F55545D060F75DC2"
+      "8DF3F6EDB8027E2346430D211312C4B150677AF76FD7223D457B5B1A",
+      "0x04"
+    },
 #if 0 /* No real specs yet found.  */
     {
       /* x^2 + y^2 = 1 + 3617x^2y^2 mod 2^414 - 17 */
diff --git a/cipher/ecc-ecdh.c b/cipher/ecc-ecdh.c
index bfd07d40..dbf0491e 100644
--- a/cipher/ecc-ecdh.c
+++ b/cipher/ecc-ecdh.c
@@ -38,13 +38,18 @@ static mpi_ec_t
 prepare_ec (const char *curve_name, elliptic_curve_t *E)
 {
   mpi_ec_t ec;
+  int flags = 0;
 
   memset (E, 0, sizeof *E);
   if (_gcry_ecc_fill_in_curve (0, curve_name, E, NULL))
     return NULL;
 
+  if (E->dialect != ECC_DIALECT_SAFECURVE)
+    flags = PUBKEY_FLAG_DJB_TWEAK;
+
   ec = _gcry_mpi_ec_p_internal_new (E->model, E->dialect,
-                                    PUBKEY_FLAG_DJB_TWEAK, E->p, E->a, E->b);
+                                    flags, E->p, E->a, E->b);
+  ec->h = mpi_copy (E->h);
   return ec;
 }
 
@@ -89,26 +94,30 @@ _gcry_ecc_mul_point (int algo, unsigned char *result,
     {
       nbits = ECC_CURVE448_BITS;
       curve = "X448";
-      return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
     }
   else
     return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
 
   nbytes = nbits / 8;
 
-  mpi_k = mpi_new (nbits);
   ec = prepare_ec (curve, &E);
   mpi_u = mpi_new (nbits);
   Q = mpi_point_new (nbits);
   x = mpi_new (nbits);
 
   memcpy (buffer, scalar, nbytes);
-  reverse_buffer (buffer, nbytes);
-  _gcry_mpi_set_buffer (mpi_k, buffer, nbytes, 0);
+  if (algo == GCRY_ECC_CURVE25519)
+    {
+      mpi_k = mpi_new (nbits);
+      reverse_buffer (buffer, nbytes);
+      _gcry_mpi_set_buffer (mpi_k, buffer, nbytes, 0);
 
-  for (i = 0; i < mpi_get_nbits (E.h) - 1; i++)
-    mpi_clear_bit (mpi_k, i);
-  mpi_set_highbit (mpi_k, mpi_get_nbits (E.p) - 1);
+      for (i = 0; i < mpi_get_nbits (E.h) - 1; i++)
+        mpi_clear_bit (mpi_k, i);
+      mpi_set_highbit (mpi_k, mpi_get_nbits (E.p) - 1);
+    }
+  else
+    mpi_k = _gcry_mpi_set_opaque_copy (NULL, buffer, nbytes*8);
 
   if (point)
     {
diff --git a/cipher/ecc-misc.c b/cipher/ecc-misc.c
index 8cdfec62..39a6581d 100644
--- a/cipher/ecc-misc.c
+++ b/cipher/ecc-misc.c
@@ -98,6 +98,7 @@ _gcry_ecc_dialect2str (enum ecc_dialects dialect)
     {
     case ECC_DIALECT_STANDARD:  str = "Standard"; break;
     case ECC_DIALECT_ED25519:   str = "Ed25519"; break;
+    case ECC_DIALECT_SAFECURVE: str = "SafeCurve"; break;
     }
   return str;
 }
diff --git a/cipher/ecc.c b/cipher/ecc.c
index 96e267fb..cedcbe8f 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -161,21 +161,29 @@ nist_generate_key (ECC_secret_key *sk, elliptic_curve_t *E, mpi_ec_t ctx,
     random_level = GCRY_VERY_STRONG_RANDOM;
 
   /* Generate a secret.  */
-  if (ctx->dialect == ECC_DIALECT_ED25519 || (flags & PUBKEY_FLAG_DJB_TWEAK))
+  if (ctx->dialect == ECC_DIALECT_ED25519
+      || ctx->dialect == ECC_DIALECT_SAFECURVE
+      || (flags & PUBKEY_FLAG_DJB_TWEAK))
     {
       char *rndbuf;
       int len = (pbits+7)/8;
-      unsigned int h;
 
-      mpi_get_ui (&h, E->h);
-      sk->d = mpi_snew (pbits);
       rndbuf = _gcry_random_bytes_secure (len, random_level);
-      if ((pbits % 8))
-        rndbuf[0] &= (1 << (pbits % 8)) - 1;
-      rndbuf[0] |= (1 << ((pbits + 7) % 8));
-      rndbuf[(pbits-1)/8] &= (256 - h);
-      _gcry_mpi_set_buffer (sk->d, rndbuf, len, 0);
-      xfree (rndbuf);
+      if (ctx->dialect == ECC_DIALECT_SAFECURVE)
+        sk->d = mpi_set_opaque (NULL, rndbuf, len*8);
+      else
+        {
+          unsigned int h;
+
+          mpi_get_ui (&h, E->h);
+          sk->d = mpi_snew (pbits);
+          if ((pbits % 8))
+            rndbuf[0] &= (1 << (pbits % 8)) - 1;
+          rndbuf[0] |= (1 << ((pbits + 7) % 8));
+          rndbuf[(pbits-1)/8] &= (256 - h);
+          _gcry_mpi_set_buffer (sk->d, rndbuf, len, 0);
+          xfree (rndbuf);
+        }
     }
   else
     sk->d = _gcry_dsa_gen_k (E->n, random_level);
@@ -342,22 +350,29 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags)
   point_init (&pk.Q);
   point_set (&pk.Q, &sk->Q);
 
-  if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+  if (sk->E.dialect == ECC_DIALECT_SAFECURVE
+      || (flags & PUBKEY_FLAG_DJB_TWEAK))
     {
       char *rndbuf;
       const unsigned int pbits = mpi_get_nbits (sk->E.p);
       int len = (pbits+7)/8;
-      unsigned int h;
 
-      mpi_get_ui (&h, sk->E.h);
-      test = mpi_new (pbits);
       rndbuf = _gcry_random_bytes (len, GCRY_WEAK_RANDOM);
-      if ((pbits % 8))
-        rndbuf[0] &= (1 << (pbits % 8)) - 1;
-      rndbuf[0] |= (1 << ((pbits + 7) % 8));
-      rndbuf[(pbits-1)/8] &= (256 - h);
-      _gcry_mpi_set_buffer (test, rndbuf, len, 0);
-      xfree (rndbuf);
+      if (sk->E.dialect == ECC_DIALECT_SAFECURVE)
+        test = mpi_set_opaque (NULL, rndbuf, len*8);
+      else
+        {
+          unsigned int h;
+
+          mpi_get_ui (&h, sk->E.h);
+          test = mpi_new (pbits);
+          if ((pbits % 8))
+            rndbuf[0] &= (1 << (pbits % 8)) - 1;
+          rndbuf[0] |= (1 << ((pbits + 7) % 8));
+          rndbuf[(pbits-1)/8] &= (256 - h);
+          _gcry_mpi_set_buffer (test, rndbuf, len, 0);
+          xfree (rndbuf);
+        }
     }
   else
     {
@@ -372,7 +387,7 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags)
 
   /* R_ = hkQ  <=>  R_ = hkdG  */
   _gcry_mpi_ec_mul_point (&R_, test, &pk.Q, ec);
-  if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
+  if (pk.E.dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
     _gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec);
   if (_gcry_mpi_ec_get_affine (x0, NULL, &R_, ec))
     log_fatal ("ecdh: Failed to get affine coordinates for hkQ\n");
@@ -380,7 +395,7 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags)
   _gcry_mpi_ec_mul_point (&R_, test, &pk.E.G, ec);
   _gcry_mpi_ec_mul_point (&R_, sk->d, &R_, ec);
   /* R_ = hdkG */
-  if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
+  if (pk.E.dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
     _gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec);
 
   if (_gcry_mpi_ec_get_affine (x1, NULL, &R_, ec))
@@ -602,6 +617,7 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
     }
 
   ctx = _gcry_mpi_ec_p_internal_new (E.model, E.dialect, flags, E.p, E.a, E.b);
+  ctx->h = mpi_copy (E.h);
 
   if (E.model == MPI_EC_MONTGOMERY)
     rc = nist_generate_key (&sk, &E, ctx, flags, nbits, &Qx, NULL);
@@ -628,7 +644,9 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
       unsigned int encpklen;
 
       if (E.model == MPI_EC_MONTGOMERY)
-        rc = _gcry_ecc_mont_encodepoint (Qx, nbits, 1, &encpk, &encpklen);
+        rc = _gcry_ecc_mont_encodepoint (Qx, nbits,
+                                         sk.E.dialect != ECC_DIALECT_SAFECURVE,
+                                         &encpk, &encpklen);
       else
         /* (Gx and Gy are used as scratch variables)  */
         rc = _gcry_ecc_eddsa_encodepoint (&sk.Q, ctx, Gx, Gy,
@@ -765,12 +783,11 @@ ecc_check_secret_key (gcry_sexp_t keyparms)
 
   /* Extract the parameters.  */
   if ((flags & PUBKEY_FLAG_PARAM))
-    rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?/q?+d",
+    rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?/q?",
                              &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
-                             &sk.E.h, &mpi_q, &sk.d, NULL);
+                             &sk.E.h, &mpi_q, NULL);
   else
-    rc = sexp_extract_param (keyparms, NULL, "/q?+d",
-                             &mpi_q, &sk.d, NULL);
+    rc = sexp_extract_param (keyparms, NULL, "/q?", &mpi_q, NULL);
   if (rc)
     goto leave;
 
@@ -807,8 +824,16 @@ ecc_check_secret_key (gcry_sexp_t keyparms)
                       ? ECC_DIALECT_ED25519
                       : ECC_DIALECT_STANDARD);
       if (!sk.E.h)
-	sk.E.h = mpi_const (MPI_C_ONE);
+        sk.E.h = mpi_const (MPI_C_ONE);
     }
+
+  if (sk.E.dialect == ECC_DIALECT_SAFECURVE)
+    rc = sexp_extract_param (keyparms, NULL, "/d", &sk.d, NULL);
+  else
+    rc = sexp_extract_param (keyparms, NULL, "+d", &sk.d, NULL);
+  if (rc)
+    goto leave;
+
   if (DBG_CIPHER)
     {
       log_debug ("ecc_testkey info: %s/%s\n",
@@ -834,6 +859,7 @@ ecc_check_secret_key (gcry_sexp_t keyparms)
 
   ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect, flags,
                                     sk.E.p, sk.E.a, sk.E.b);
+  ec->h = mpi_copy (sk.E.h);
 
   if (mpi_q)
     {
@@ -939,7 +965,7 @@ ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
                       ? ECC_DIALECT_ED25519
                       : ECC_DIALECT_STANDARD);
       if (!sk.E.h)
-	sk.E.h = mpi_const (MPI_C_ONE);
+        sk.E.h = mpi_const (MPI_C_ONE);
     }
   if (DBG_CIPHER)
     {
@@ -1107,7 +1133,7 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
                       ? ECC_DIALECT_ED25519
                       : ECC_DIALECT_STANDARD);
       if (!pk.E.h)
-	pk.E.h = mpi_const (MPI_C_ONE);
+        pk.E.h = mpi_const (MPI_C_ONE);
     }
 
   if (DBG_CIPHER)
@@ -1261,6 +1287,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   ECC_public_key pk;
   mpi_ec_t ec = NULL;
   int flags = 0;
+  int safecurve;
 
   memset (&pk, 0, sizeof pk);
   _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT,
@@ -1277,18 +1304,6 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   sexp_release (l1);
   l1 = NULL;
 
-  /*
-   * Extract the data.
-   */
-  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
-  if (rc)
-    goto leave;
-  if (mpi_is_opaque (data))
-    {
-      rc = GPG_ERR_INV_DATA;
-      goto leave;
-    }
-
   /*
    * Extract the key.
    */
@@ -1307,22 +1322,42 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   /* Add missing parameters using the optional curve parameter.  */
   l1 = sexp_find_token (keyparms, "curve", 5);
   if (l1)
+    curvename = sexp_nth_string (l1, 1);
+  /* Guess required fields if a curve parameter has not been given.  */
+  if (curvename)
     {
-      curvename = sexp_nth_string (l1, 1);
-      if (curvename)
-        {
-          rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL);
-          if (rc)
-            goto leave;
-        }
+      rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL);
+      if (rc)
+        goto leave;
     }
-  /* Guess required fields if a curve parameter has not been given.  */
-  if (!curvename)
+  else
     {
       pk.E.model = MPI_EC_WEIERSTRASS;
       pk.E.dialect = ECC_DIALECT_STANDARD;
       if (!pk.E.h)
-	pk.E.h = mpi_const (MPI_C_ONE);
+        pk.E.h = mpi_const (MPI_C_ONE);
+    }
+
+  if (pk.E.dialect == ECC_DIALECT_SAFECURVE)
+    {
+      ctx.flags |= PUBKEY_FLAG_RAW_FLAG;
+      safecurve = 1;
+    }
+  else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+    safecurve = 1;
+  else
+    safecurve = 0;
+
+  /*
+   * Extract the data.
+   */
+  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+  if (rc)
+    goto leave;
+  if (pk.E.dialect != ECC_DIALECT_SAFECURVE && mpi_is_opaque (data))
+    {
+      rc = GPG_ERR_INV_DATA;
+      goto leave;
     }
 
   /*
@@ -1364,6 +1399,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   /* Compute the encrypted value.  */
   ec = _gcry_mpi_ec_p_internal_new (pk.E.model, pk.E.dialect, flags,
                                     pk.E.p, pk.E.a, pk.E.b);
+  ec->h = mpi_copy (pk.E.h);
 
   /* Convert the public key.  */
   if (mpi_q)
@@ -1408,7 +1444,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
          * imported public key which might not follow the key
          * generation procedure.
          */
-        if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
+        if (!safecurve)
           { /* It's not for X25519, then, the input data was simply wrong.  */
             rc = GPG_ERR_INV_DATA;
             goto leave_main;
@@ -1418,7 +1454,9 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       mpi_s = _gcry_ecc_ec2os (x, y, pk.E.p);
     else
       {
-        rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen);
+        rc = _gcry_ecc_mont_encodepoint (x, nbits,
+                                         pk.E.dialect != ECC_DIALECT_SAFECURVE,
+                                         &rawmpi, &rawmpilen);
         if (rc)
           goto leave_main;
         mpi_s = mpi_new (0);
@@ -1437,7 +1475,9 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       mpi_e = _gcry_ecc_ec2os (x, y, pk.E.p);
     else
       {
-        rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen);
+        rc = _gcry_ecc_mont_encodepoint (x, nbits,
+                                         pk.E.dialect != ECC_DIALECT_SAFECURVE,
+                                         &rawmpi, &rawmpilen);
         if (!rc)
           {
             mpi_e = mpi_new (0);
@@ -1502,6 +1542,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   mpi_point_struct R;
   gcry_mpi_t r = NULL;
   int flags = 0;
+  int safecurve;
 
   memset (&sk, 0, sizeof sk);
   point_init (&kG);
@@ -1527,23 +1568,18 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
   if (rc)
     goto leave;
-  rc = sexp_extract_param (l1, NULL, "e", &data_e, NULL);
+  rc = sexp_extract_param (l1, NULL, "/e", &data_e, NULL);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
     log_printmpi ("ecc_decrypt  d_e", data_e);
-  if (mpi_is_opaque (data_e))
-    {
-      rc = GPG_ERR_INV_DATA;
-      goto leave;
-    }
 
   /*
    * Extract the key.
    */
-  rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?+d",
+  rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?",
                            &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
-                           &sk.E.h, &sk.d, NULL);
+                           &sk.E.h, NULL);
   if (rc)
     goto leave;
   if (mpi_g)
@@ -1557,23 +1593,29 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   sexp_release (l1);
   l1 = sexp_find_token (keyparms, "curve", 5);
   if (l1)
+    curvename = sexp_nth_string (l1, 1);
+  /* Guess required fields if a curve parameter has not been given.  */
+  if (curvename)
     {
-      curvename = sexp_nth_string (l1, 1);
-      if (curvename)
-        {
-          rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL);
-          if (rc)
-            goto leave;
-        }
+      rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL);
+      if (rc)
+        goto leave;
     }
-  /* Guess required fields if a curve parameter has not been given.  */
-  if (!curvename)
+  else
     {
       sk.E.model = MPI_EC_WEIERSTRASS;
       sk.E.dialect = ECC_DIALECT_STANDARD;
       if (!sk.E.h)
-	sk.E.h = mpi_const (MPI_C_ONE);
+        sk.E.h = mpi_const (MPI_C_ONE);
     }
+
+  if (sk.E.dialect == ECC_DIALECT_SAFECURVE)
+    rc = sexp_extract_param (keyparms, NULL, "/d", &sk.d, NULL);
+  else
+    rc = sexp_extract_param (keyparms, NULL, "+d", &sk.d, NULL);
+  if (rc)
+    goto leave;
+
   if (DBG_CIPHER)
     {
       log_debug ("ecc_decrypt info: %s/%s\n",
@@ -1596,9 +1638,14 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       goto leave;
     }
 
+  if (sk.E.dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
+    safecurve = 1;
+  else
+    safecurve = 0;
 
   ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect, flags,
                                     sk.E.p, sk.E.a, sk.E.b);
+  ec->h = mpi_copy (sk.E.h);
 
   /*
    * Compute the plaintext.
@@ -1613,7 +1660,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   if (DBG_CIPHER)
     log_printpnt ("ecc_decrypt    kG", &kG, NULL);
 
-  if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+  if (safecurve)
     {
       /* For X25519, by its definition, validation should not be done.  */
       /* (Instead, we do output check.)
@@ -1682,7 +1729,9 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
         unsigned char *rawmpi;
         unsigned int rawmpilen;
 
-        rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen);
+        rc = _gcry_ecc_mont_encodepoint (x, nbits,
+                                         sk.E.dialect != ECC_DIALECT_SAFECURVE,
+                                         &rawmpi, &rawmpilen);
         if (rc)
           goto leave;
 
@@ -1877,7 +1926,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparms)
       if (rc)
         goto leave;
     }
-  else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+  else if (dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
     {
       /* Remove the prefix 0x40 for keygrip computation.  */
       raw = mpi_get_opaque (values[6], &n);
@@ -1998,7 +2047,8 @@ _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
       unsigned char *encpk;
       unsigned int encpklen;
 
-      rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits, 1,
+      rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits,
+                                       ec->dialect != ECC_DIALECT_SAFECURVE,
                                        &encpk, &encpklen);
       if (rc)
         goto leave;
diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c
index ae0e1c46..859b54a1 100644
--- a/cipher/pubkey-util.c
+++ b/cipher/pubkey-util.c
@@ -685,7 +685,10 @@ _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
   ldata = sexp_find_token (input, "data", 0);
   if (!ldata)
     { /* assume old style */
-      *ret_mpi = sexp_nth_mpi (input, 0, 0);
+      if ((ctx->flags & PUBKEY_FLAG_RAW_FLAG))
+        *ret_mpi = sexp_nth_mpi (input, 0, GCRYMPI_FMT_OPAQUE);
+      else
+        *ret_mpi = sexp_nth_mpi (input, 0, 0);
       return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ;
     }
 
diff --git a/mpi/ec.c b/mpi/ec.c
index ed936d74..8d3d7284 100644
--- a/mpi/ec.c
+++ b/mpi/ec.c
@@ -30,6 +30,7 @@
 #include "ec-context.h"
 #include "ec-internal.h"
 
+extern void reverse_buffer (unsigned char *buffer, unsigned int length);
 
 #define point_init(a)  _gcry_mpi_point_init ((a))
 #define point_free(a)  _gcry_mpi_point_free_parts ((a))
@@ -366,7 +367,7 @@ mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long set)
       wp[i] = wp[i] ^ x;
     }
 }
-
+
 /* Routines for 2^255 - 19.  */
 
 #define LIMB_SIZE_25519 ((256+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
@@ -477,7 +478,167 @@ ec_pow2_25519 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
 {
   ec_mulm_25519 (w, b, b, ctx);
 }
+
+/* Routines for 2^448 - 2^224 - 1.  */
+
+#define LIMB_SIZE_448 ((448+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
+#define LIMB_SIZE_HALF_448 ((LIMB_SIZE_448+1)/2)
+
+static void
+ec_addm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+  mpi_ptr_t wp, up, vp;
+  mpi_size_t wsize = LIMB_SIZE_448;
+  mpi_limb_t n[LIMB_SIZE_448];
+  mpi_limb_t cy;
+
+  if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+    log_bug ("addm_448: different sizes\n");
+
+  memset (n, 0, sizeof n);
+  up = u->d;
+  vp = v->d;
+  wp = w->d;
+
+  cy = _gcry_mpih_add_n (wp, up, vp, wsize);
+  mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL));
+  _gcry_mpih_sub_n (wp, wp, n, wsize);
+}
+
+static void
+ec_subm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+  mpi_ptr_t wp, up, vp;
+  mpi_size_t wsize = LIMB_SIZE_448;
+  mpi_limb_t n[LIMB_SIZE_448];
+  mpi_limb_t borrow;
 
+  if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+    log_bug ("subm_448: different sizes\n");
+
+  memset (n, 0, sizeof n);
+  up = u->d;
+  vp = v->d;
+  wp = w->d;
+
+  borrow = _gcry_mpih_sub_n (wp, up, vp, wsize);
+  mpih_set_cond (n, ctx->p->d, wsize, (borrow != 0UL));
+  _gcry_mpih_add_n (wp, wp, n, wsize);
+}
+
+static void
+ec_mulm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+  mpi_ptr_t wp, up, vp;
+  mpi_size_t wsize = LIMB_SIZE_448;
+  mpi_limb_t n[LIMB_SIZE_448*2];
+  mpi_limb_t a2[LIMB_SIZE_HALF_448];
+  mpi_limb_t a3[LIMB_SIZE_HALF_448];
+  mpi_limb_t b0[LIMB_SIZE_HALF_448];
+  mpi_limb_t b1[LIMB_SIZE_HALF_448];
+  mpi_limb_t cy;
+  int i;
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+  mpi_limb_t b1_rest, a3_rest;
+#endif
+
+  if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+    log_bug ("mulm_448: different sizes\n");
+
+  up = u->d;
+  vp = v->d;
+  wp = w->d;
+
+  _gcry_mpih_mul_n (n, up, vp, wsize);
+
+  for (i = 0; i < (wsize + 1)/ 2; i++)
+    {
+      b0[i] = n[i];
+      b1[i] = n[i+wsize/2];
+      a2[i] = n[i+wsize];
+      a3[i] = n[i+wsize+wsize/2];
+    }
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+  b0[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1;
+  a2[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1;
+
+  b1_rest = 0;
+  a3_rest = 0;
+
+  for (i = (wsize + 1)/ 2 -1; i >= 0; i--)
+    {
+      mpi_limb_t b1v, a3v;
+      b1v = b1[i];
+      a3v = a3[i];
+      b1[i] = (b1_rest<<32) | (b1v >> 32);
+      a3[i] = (a3_rest<<32) | (a3v >> 32);
+      b1_rest = b1v & ((1UL <<32)-1);
+      a3_rest = a3v & ((1UL <<32)-1);
+    }
+#endif
+
+  cy = _gcry_mpih_add_n (b0, b0, a2, LIMB_SIZE_HALF_448);
+  cy += _gcry_mpih_add_n (b0, b0, a3, LIMB_SIZE_HALF_448);
+  for (i = 0; i < (wsize + 1)/ 2; i++)
+    wp[i] = b0[i];
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+  wp[LIMB_SIZE_HALF_448-1] &= ((1UL <<32)-1);
+#endif
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+  cy = b0[LIMB_SIZE_HALF_448-1] >> 32;
+#endif
+
+  cy = _gcry_mpih_add_1 (b1, b1, LIMB_SIZE_HALF_448, cy);
+  cy += _gcry_mpih_add_n (b1, b1, a2, LIMB_SIZE_HALF_448);
+  cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448);
+  cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448);
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+  b1_rest = 0;
+  for (i = (wsize + 1)/ 2 -1; i >= 0; i--)
+    {
+      mpi_limb_t b1v = b1[i];
+      b1[i] = (b1_rest<<32) | (b1v >> 32);
+      b1_rest = b1v & ((1UL <<32)-1);
+    }
+  wp[LIMB_SIZE_HALF_448-1] |= (b1_rest << 32);
+#endif
+  for (i = 0; i < wsize / 2; i++)
+    wp[i+(wsize + 1) / 2] = b1[i];
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+  cy = b1[LIMB_SIZE_HALF_448-1];
+#endif
+
+  memset (n, 0, wsize * BYTES_PER_MPI_LIMB);
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+  n[LIMB_SIZE_HALF_448-1] = cy << 32;
+#else
+  n[LIMB_SIZE_HALF_448] = cy;
+#endif
+  n[0] = cy;
+  _gcry_mpih_add_n (wp, wp, n, wsize);
+
+  memset (n, 0, wsize * BYTES_PER_MPI_LIMB);
+  cy = _gcry_mpih_sub_n (wp, wp, ctx->p->d, wsize);
+  mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL));
+  _gcry_mpih_add_n (wp, wp, n, wsize);
+}
+
+static void
+ec_mul2_448 (gcry_mpi_t w, gcry_mpi_t u, mpi_ec_t ctx)
+{
+  ec_addm_448 (w, u, u, ctx);
+}
+
+static void
+ec_pow2_448 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
+{
+  ec_mulm_448 (w, b, b, ctx);
+}
+
 struct field_table {
   const char *p;
 
@@ -498,6 +659,15 @@ static const struct field_table field_table[] = {
     ec_mul2_25519,
     ec_pow2_25519
   },
+  {
+   "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+   "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+    ec_addm_448,
+    ec_subm_448,
+    ec_mulm_448,
+    ec_mul2_448,
+    ec_pow2_448
+  },
   { NULL, NULL, NULL, NULL, NULL, NULL },
 };
 
@@ -544,17 +714,37 @@ ec_get_two_inv_p (mpi_ec_t ec)
 }
 
 
-static const char *curve25519_bad_points[] = {
+static const char *const curve25519_bad_points[] = {
+  "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
   "0x0000000000000000000000000000000000000000000000000000000000000000",
   "0x0000000000000000000000000000000000000000000000000000000000000001",
   "0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0",
   "0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f",
   "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec",
-  "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
   "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee",
   NULL
 };
 
+
+static const char *const curve448_bad_points[] = {
+  "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+  "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+  "0x00000000000000000000000000000000000000000000000000000000"
+  "00000000000000000000000000000000000000000000000000000000",
+  "0x00000000000000000000000000000000000000000000000000000000"
+  "00000000000000000000000000000000000000000000000000000001",
+  "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+  "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
+  "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+  "00000000000000000000000000000000000000000000000000000000",
+  NULL
+};
+
+static const char *const *bad_points_table[] = {
+  curve25519_bad_points,
+  curve448_bad_points,
+};
+
 static gcry_mpi_t
 scanval (const char *string)
 {
@@ -607,8 +797,19 @@ ec_p_init (mpi_ec_t ctx, enum gcry_mpi_ec_models model,
 
   if (model == MPI_EC_MONTGOMERY)
     {
-      for (i=0; i< DIM(ctx->t.scratch) && curve25519_bad_points[i]; i++)
-        ctx->t.scratch[i] = scanval (curve25519_bad_points[i]);
+      for (i=0; i< DIM(bad_points_table); i++)
+        {
+          gcry_mpi_t p_candidate = scanval (bad_points_table[i][0]);
+          int match_p = !mpi_cmp (ctx->p, p_candidate);
+          int j;
+
+          mpi_free (p_candidate);
+          if (!match_p)
+            continue;
+
+          for (j=0; i< DIM(ctx->t.scratch) && bad_points_table[i][j]; j++)
+            ctx->t.scratch[j] = scanval (bad_points_table[i][j]);
+        }
     }
   else
     {
@@ -1570,6 +1771,7 @@ _gcry_mpi_ec_mul_point (mpi_point_t result,
       mpi_point_t q1, q2, prd, sum;
       unsigned long sw;
       mpi_size_t rsize;
+      int scalar_copied = 0;
 
       /* Compute scalar point multiplication with Montgomery Ladder.
          Note that we don't use Y-coordinate in the points at all.
@@ -1585,6 +1787,29 @@ _gcry_mpi_ec_mul_point (mpi_point_t result,
       p2.x  = mpi_copy (point->x);
       mpi_set_ui (p2.z, 1);
 
+      if (mpi_is_opaque (scalar))
+        {
+          gcry_mpi_t a;
+          unsigned int cofactor, n;
+          const unsigned int pbits = mpi_get_nbits (ctx->p);
+          unsigned char *raw;
+
+          scalar_copied = 1;
+
+          raw = _gcry_mpi_get_opaque_copy (scalar, &n);
+          mpi_get_ui (&cofactor, ctx->h);
+          reverse_buffer (raw, (n+7)/8);
+          if ((pbits % 8))
+            raw[0] &= (1 << (pbits % 8)) - 1;
+          raw[0] |= (1 << ((pbits + 7) % 8));
+          raw[(pbits-1)/8] &= (256 - cofactor);
+          a = mpi_is_secure (scalar) ? mpi_snew (pbits): mpi_new (pbits);
+          _gcry_mpi_set_buffer (a, raw, (n+7)/8, 0);
+          xfree (raw);
+
+          scalar = a;
+        }
+
       point_resize (&p1, ctx);
       point_resize (&p2, ctx);
       point_resize (&p1_, ctx);
@@ -1634,6 +1859,8 @@ _gcry_mpi_ec_mul_point (mpi_point_t result,
       point_free (&p2);
       point_free (&p1_);
       point_free (&p2_);
+      if (scalar_copied)
+        _gcry_mpi_release (scalar);
       return;
     }
 
diff --git a/src/mpi.h b/src/mpi.h
index aeba7f8b..ee6be2f7 100644
--- a/src/mpi.h
+++ b/src/mpi.h
@@ -266,7 +266,8 @@ enum gcry_mpi_ec_models
 enum ecc_dialects
   {
     ECC_DIALECT_STANDARD = 0,
-    ECC_DIALECT_ED25519
+    ECC_DIALECT_ED25519,
+    ECC_DIALECT_SAFECURVE
   };
 
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9e117970..e463d8c6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,7 +22,8 @@ tests_bin = \
         version t-secmem mpitests t-sexp t-convert \
 	t-mpi-bit t-mpi-point curves t-lock \
 	prime basic keygen pubkey hmac hashtest t-kdf keygrip \
-	fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 t-ed25519 t-cv25519
+	fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 \
+	t-ed25519 t-cv25519 t-x448
 
 tests_bin_last = benchmark bench-slope
 
diff --git a/tests/curves.c b/tests/curves.c
index dc5ebe77..bacc1302 100644
--- a/tests/curves.c
+++ b/tests/curves.c
@@ -33,7 +33,7 @@
 #include "t-common.h"
 
 /* Number of curves defined in ../cipger/ecc.c */
-#define N_CURVES 22
+#define N_CURVES 23
 
 /* A real world sample public key.  */
 static char const sample_key_1[] =
diff --git a/tests/t-x448.c b/tests/t-x448.c
new file mode 100644
index 00000000..5c3cbeb9
--- /dev/null
+++ b/tests/t-x448.c
@@ -0,0 +1,593 @@
+/* t-x448.c - Check the X488 computation
+ * Copyright (C) 2019 g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "stopwatch.h"
+
+#define PGM "t-x448"
+#include "t-common.h"
+#define N_TESTS 9
+
+
+static void
+print_mpi (const char *text, gcry_mpi_t a)
+{
+  gcry_error_t err;
+  char *buf;
+  void *bufaddr = &buf;
+
+  err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
+  if (err)
+    fprintf (stderr, "%s: [error printing number: %s]\n",
+             text, gpg_strerror (err));
+  else
+    {
+      fprintf (stderr, "%s: %s\n", text, buf);
+      gcry_free (buf);
+    }
+}
+
+
+static void
+show_note (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  if (!verbose && getenv ("srcdir"))
+    fputs ("      ", stderr);  /* To align above "PASS: ".  */
+  else
+    fprintf (stderr, "%s: ", PGM);
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  if (*format && format[strlen(format)-1] != '\n')
+    putc ('\n', stderr);
+  va_end (arg_ptr);
+}
+
+
+/* Convert STRING consisting of hex characters into its binary
+   representation and return it as an allocated buffer. The valid
+   length of the buffer is returned at R_LENGTH.  The string is
+   delimited by end of string.  The function returns NULL on
+   error.  */
+static void *
+hex2buffer (const char *string, size_t *r_length)
+{
+  const char *s;
+  unsigned char *buffer;
+  size_t length;
+
+  buffer = xmalloc (strlen(string)/2+1);
+  length = 0;
+  for (s=string; *s; s +=2 )
+    {
+      if (!hexdigitp (s) || !hexdigitp (s+1))
+        return NULL;           /* Invalid hex digits. */
+      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
+    }
+  *r_length = length;
+  return buffer;
+}
+
+static void
+reverse_buffer (unsigned char *buffer, unsigned int length)
+{
+  unsigned int tmp, i;
+
+  for (i=0; i < length/2; i++)
+    {
+      tmp = buffer[i];
+      buffer[i] = buffer[length-1-i];
+      buffer[length-1-i] = tmp;
+    }
+}
+
+
+/*
+ * Test X448 functionality through higher layer crypto routines.
+ *
+ * Input: K (as hex string), U (as hex string), R (as hex string)
+ *
+ * where R is expected result of X448 (K, U).
+ *
+ */
+static void
+test_cv_hl (int testno, const char *k_str, const char *u_str,
+              const char *result_str)
+{
+  gpg_error_t err;
+  void *buffer = NULL;
+  size_t buflen;
+  gcry_sexp_t s_pk = NULL;
+  gcry_mpi_t mpi_k = NULL;
+  gcry_sexp_t s_data = NULL;
+  gcry_sexp_t s_result = NULL;
+  gcry_sexp_t s_tmp = NULL;
+  unsigned char *res = NULL;
+  size_t res_len;
+
+  if (verbose > 1)
+    info ("Running test %d\n", testno);
+
+  if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56)
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "k", "invalid hex string");
+      goto leave;
+    }
+
+  mpi_k = gcry_mpi_set_opaque (NULL, buffer, buflen*8);
+  if ((err = gcry_sexp_build (&s_data, NULL, "%m", mpi_k)))
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "data", gpg_strerror (err));
+      goto leave;
+    }
+
+  if (!(buffer = hex2buffer (u_str, &buflen)) || buflen != 56)
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "u", "invalid hex string");
+      goto leave;
+    }
+
+  /*
+   * The procedure of decodeUCoordinate will be done internally
+   * by _gcry_ecc_mont_decodepoint.  So, we just put the little-endian
+   * binary to build S-exp.
+   *
+   * We could add the prefix 0x40, but libgcrypt also supports
+   * format with no prefix.  So, it is OK not to put the prefix.
+   */
+  if ((err = gcry_sexp_build (&s_pk, NULL,
+                              "(public-key"
+                              " (ecc"
+                              "  (curve \"X448\")"
+                              "  (q%b)))", (int)buflen, buffer)))
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "pk", gpg_strerror (err));
+      goto leave;
+    }
+
+  xfree (buffer);
+  buffer = NULL;
+
+  if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk)))
+    fail ("gcry_pk_encrypt failed for test %d: %s", testno,
+          gpg_strerror (err));
+
+  s_tmp = gcry_sexp_find_token (s_result, "s", 0);
+  if (!s_tmp || !(res = gcry_sexp_nth_buffer (s_tmp, 1, &res_len)))
+    fail ("gcry_pk_encrypt failed for test %d: %s", testno, "missing value");
+  else
+    {
+      char *r, *r0;
+      int i;
+
+      r0 = r = xmalloc (2*(res_len)+1);
+      if (!r0)
+        {
+          fail ("memory allocation for test %d", testno);
+          goto leave;
+        }
+
+      for (i=0; i < res_len; i++, r += 2)
+        snprintf (r, 3, "%02x", res[i]);
+      if (strcmp (result_str, r0))
+        {
+          fail ("gcry_pk_encrypt failed for test %d: %s",
+                testno, "wrong value returned");
+          info ("  expected: '%s'", result_str);
+          info ("       got: '%s'", r0);
+        }
+      xfree (r0);
+    }
+
+ leave:
+  xfree (res);
+  gcry_mpi_release (mpi_k);
+  gcry_sexp_release (s_tmp);
+  gcry_sexp_release (s_result);
+  gcry_sexp_release (s_data);
+  gcry_sexp_release (s_pk);
+  xfree (buffer);
+}
+
+/*
+ * Test X448 functionality through the API for X448.
+ *
+ * Input: K (as hex string), U (as hex string), R (as hex string)
+ *
+ * where R is expected result of X448 (K, U).
+ *
+ */
+static void
+test_cv_x448 (int testno, const char *k_str, const char *u_str,
+                const char *result_str)
+{
+  gpg_error_t err;
+  void *scalar;
+  void *point = NULL;
+  size_t buflen;
+  unsigned char result[56];
+  char result_hex[113];
+  int i;
+
+  if (verbose > 1)
+    info ("Running test %d\n", testno);
+
+  if (!(scalar = hex2buffer (k_str, &buflen)) || buflen != 56)
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "k", "invalid hex string");
+      goto leave;
+    }
+
+  if (!(point = hex2buffer (u_str, &buflen)) || buflen != 56)
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "u", "invalid hex string");
+      goto leave;
+    }
+
+  if ((err = gcry_ecc_mul_point (GCRY_ECC_CURVE448, result, scalar, point)))
+    fail ("gcry_ecc_mul_point failed for test %d: %s", testno,
+          gpg_strerror (err));
+
+  for (i=0; i < 56; i++)
+    snprintf (&result_hex[i*2], 3, "%02x", result[i]);
+
+  if (strcmp (result_str, result_hex))
+    {
+      fail ("gcry_ecc_mul_point failed for test %d: %s",
+            testno, "wrong value returned");
+      info ("  expected: '%s'", result_str);
+      info ("       got: '%s'", result_hex);
+    }
+
+ leave:
+  xfree (scalar);
+  xfree (point);
+}
+
+static void
+test_cv (int testno, const char *k_str, const char *u_str,
+         const char *result_str)
+{
+  test_cv_hl (testno, k_str, u_str, result_str);
+  test_cv_x448 (testno, k_str, u_str, result_str);
+}
+
+/*
+ * Test iterative X448 computation through lower layer MPI routines.
+ *
+ * Input: K (as hex string), ITER, R (as hex string)
+ *
+ * where R is expected result of iterating X448 by ITER times.
+ *
+ */
+static void
+test_it (int testno, const char *k_str, int iter, const char *result_str)
+{
+  gcry_ctx_t ctx;
+  gpg_error_t err;
+  void *buffer = NULL;
+  size_t buflen;
+  gcry_mpi_t mpi_k = NULL;
+  gcry_mpi_t mpi_x = NULL;
+  gcry_mpi_point_t P = NULL;
+  gcry_mpi_point_t Q;
+  int i;
+  gcry_mpi_t mpi_kk = NULL;
+
+  if (verbose > 1)
+    info ("Running test %d: iteration=%d\n", testno, iter);
+
+  gcry_mpi_ec_new (&ctx, NULL, "X448");
+  Q = gcry_mpi_point_new (0);
+
+  if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56)
+    {
+      fail ("error scanning MPI for test %d, %s: %s",
+            testno, "k", "invalid hex string");
+      goto leave;
+    }
+  reverse_buffer (buffer, buflen);
+  if ((err = gcry_mpi_scan (&mpi_x, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
+    {
+      fail ("error scanning MPI for test %d, %s: %s",
+            testno, "x", gpg_strerror (err));
+      goto leave;
+    }
+
+  xfree (buffer);
+  buffer = NULL;
+
+  P = gcry_mpi_point_set (NULL, mpi_x, NULL, GCRYMPI_CONST_ONE);
+
+  mpi_k = gcry_mpi_copy (mpi_x);
+  if (debug)
+    print_mpi ("k", mpi_k);
+
+  for (i = 0; i < iter; i++)
+    {
+      /*
+       * Another variant of decodeScalar448 thing.
+       */
+      mpi_kk = gcry_mpi_set (mpi_kk, mpi_k);
+      gcry_mpi_set_bit (mpi_kk, 447);
+      gcry_mpi_clear_bit (mpi_kk, 0);
+      gcry_mpi_clear_bit (mpi_kk, 1);
+
+      gcry_mpi_ec_mul (Q, mpi_kk, P, ctx);
+
+      P = gcry_mpi_point_set (P, mpi_k, NULL, GCRYMPI_CONST_ONE);
+      gcry_mpi_ec_get_affine (mpi_k, NULL, Q, ctx);
+
+      if (debug)
+        print_mpi ("k", mpi_k);
+    }
+
+  {
+    unsigned char res[56];
+    char *r, *r0;
+
+    gcry_mpi_print (GCRYMPI_FMT_USG, res, 56, NULL, mpi_k);
+    reverse_buffer (res, 56);
+
+    r0 = r = xmalloc (113);
+    if (!r0)
+      {
+        fail ("memory allocation for test %d", testno);
+        goto leave;
+      }
+
+    for (i=0; i < 56; i++, r += 2)
+      snprintf (r, 3, "%02x", res[i]);
+
+    if (strcmp (result_str, r0))
+      {
+        fail ("X448 failed for test %d: %s",
+              testno, "wrong value returned");
+        info ("  expected: '%s'", result_str);
+        info ("       got: '%s'", r0);
+      }
+    xfree (r0);
+  }
+
+ leave:
+  gcry_mpi_release (mpi_kk);
+  gcry_mpi_release (mpi_k);
+  gcry_mpi_point_release (P);
+  gcry_mpi_release (mpi_x);
+  xfree (buffer);
+  gcry_mpi_point_release (Q);
+  gcry_ctx_release (ctx);
+}
+
+/*
+ * X-coordinate of generator of the X448.
+ */
+#define G_X ("0500000000000000000000000000000000000000000000000000000000000000" \
+             "000000000000000000000000000000000000000000000000")
+
+/*
+ * Test Diffie-Hellman in RFC-7748.
+ *
+ * Note that it's not like the ECDH of OpenPGP, where we use
+ * ephemeral public key.
+ */
+static void
+test_dh (int testno, const char *a_priv_str, const char *a_pub_str,
+          const char *b_priv_str, const char *b_pub_str,
+          const char *result_str)
+{
+  /* Test A for private key corresponds to public key. */
+  test_cv (testno, a_priv_str, G_X, a_pub_str);
+  /* Test B for private key corresponds to public key. */
+  test_cv (testno, b_priv_str, G_X, b_pub_str);
+  /* Test DH with A's private key and B's public key. */
+  test_cv (testno, a_priv_str, b_pub_str, result_str);
+  /* Test DH with B's private key and A's public key. */
+  test_cv (testno, b_priv_str, a_pub_str, result_str);
+}
+
+
+static void
+check_x448 (void)
+{
+  int ntests;
+
+  info ("Checking X448.\n");
+
+  ntests = 0;
+
+  /*
+   * Values are cited from RFC-7748: 5.2.  Test Vectors.
+   * Following two tests are for the first type test.
+   */
+  test_cv (1,
+           "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
+           "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
+           "06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9"
+           "814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086",
+           "ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239f"
+           "e14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f");
+  ntests++;
+  test_cv (2,
+           "203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c5"
+           "38345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f",
+           "0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b"
+           "165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db",
+           "884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7"
+           "ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d");
+  ntests++;
+
+  /*
+   * Additional test.  Value is from second type test.
+   */
+  test_cv (3,
+           G_X,
+           G_X,
+           "3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a"
+           "4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113");
+  ntests++;
+
+  /*
+   * Following two tests are for the second type test,
+   * with one iteration and 1,000 iterations.  (1,000,000 iterations
+   * takes too long.)
+   */
+  test_it (4,
+           G_X,
+           1,
+           "3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a"
+           "4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113");
+  ntests++;
+
+  test_it (5,
+           G_X,
+           1000,
+           "aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4"
+           "af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38");
+  ntests++;
+
+  /*
+   * Last test is from: 6.  Diffie-Hellman, 6.2.  Curve448
+   */
+  test_dh (6,
+           /* Alice's private key, a */
+           "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d"
+           "d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b",
+           /* Alice's public key, X448(a, 5) */
+           "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c"
+           "22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0",
+           /* Bob's private key, b */
+           "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d"
+           "6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d",
+           /* Bob's public key, X448(b, 5) */
+           "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430"
+           "27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609",
+           /* Their shared secret, K */
+           "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282b"
+           "b60c0b56fd2464c335543936521c24403085d59a449a5037514a879d");
+  ntests++;
+
+  /* three tests which results 0. */
+  test_cv (7,
+           "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
+           "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
+           "00000000000000000000000000000000000000000000000000000000"
+           "00000000000000000000000000000000000000000000000000000000",
+           "00000000000000000000000000000000000000000000000000000000"
+           "00000000000000000000000000000000000000000000000000000000");
+  ntests++;
+
+  test_cv (8,
+           "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
+           "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
+           "01000000000000000000000000000000000000000000000000000000"
+           "00000000000000000000000000000000000000000000000000000000",
+           "00000000000000000000000000000000000000000000000000000000"
+           "00000000000000000000000000000000000000000000000000000000");
+  ntests++;
+
+  test_cv (9,
+           "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
+           "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
+           "feffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+           "feffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+           "00000000000000000000000000000000000000000000000000000000"
+           "00000000000000000000000000000000000000000000000000000000");
+  ntests++;
+
+  if (ntests != N_TESTS)
+    fail ("did %d tests but expected %d", ntests, N_TESTS);
+  else if ((ntests % 256))
+    show_note ("%d tests done\n", ntests);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+
+  if (argc)
+    { argc--; argv++; }
+
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--help"))
+        {
+          fputs ("usage: " PGM " [options]\n"
+                 "Options:\n"
+                 "  --verbose       print timings etc.\n"
+                 "  --debug         flyswatter\n",
+                 stdout);
+          exit (0);
+        }
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose++;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--debug"))
+        {
+          verbose += 2;
+          debug++;
+          argc--; argv++;
+        }
+      else if (!strncmp (*argv, "--", 2))
+        die ("unknown option '%s'", *argv);
+    }
+
+  xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0));
+  if (!gcry_check_version (GCRYPT_VERSION))
+    die ("version mismatch\n");
+  if (debug)
+    xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0));
+  xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0));
+  xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0));
+
+  start_timer ();
+  check_x448 ();
+  stop_timer ();
+
+  info ("All tests completed in %s.  Errors: %d\n",
+        elapsed_time (1), error_count);
+  return !!error_count;
+}



More information about the Gcrypt-devel mailing list