diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c
--- a/cipher/ecc-curves.c
+++ b/cipher/ecc-curves.c
@@ -48,7 +48,7 @@
     { "Curve25519", "1.3.101.110" },         /* rfc8410 */
     { "Curve25519", "X25519" },              /* rfc8410 */
 
-    /* { "Ed448",      "1.3.101.113" },         /\* rfc8410 *\/ */
+    { "Ed448",      "1.3.101.113" },         /* rfc8410 */
     { "X448",       "1.3.101.111" },         /* rfc8410 */
 
     { "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID  */
@@ -170,6 +170,23 @@
        * the function _gcry_ecc_fill_in_curve.  See bug #4712.
        */
     },
+    {
+      /* (x^2 + y^2 = 1 + dx^2y^2) */
+      "Ed448", 448, 0,
+      MPI_EC_EDWARDS, ECC_DIALECT_SAFECURVE,
+      "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+      "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+      "0x01",
+      "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+      "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6756",
+      "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+      "7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3",
+      "0x4F1970C66BED0DED221D15A622BF36DA9E146570470F1767EA6DE324"
+      "A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05E",
+      "0x693F46716EB6BC248876203756C9C7624BEA73736CA3984087789C1E"
+      "05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA14",
+      4,
+    },
     {
       /* (y^2 = x^3 + 156326*x^2 + x) */
       "X448", 448, 0,
@@ -1138,12 +1155,15 @@
       if (ec->d && is_opaque_bytes)
         {
           unsigned int n = mpi_get_nbits (ec->d);
+          unsigned int len;
+
+          len = (ec->nbits%8) == 0 ? (ec->nbits/8 + 1) : (ec->nbits+7)/8;
 
-          if ((n+7)/8 != (ec->nbits+7)/8)
+          if ((n+7)/8 != len)
             {
               if (DBG_CIPHER)
                 log_debug ("scalar size (%d) != prime size (%d)",
-                           (n+7)/8, (ec->nbits+7)/8);
+                           (n+7)/8, len);
 
               errc = GPG_ERR_INV_OBJ;
             }
diff --git a/cipher/ecc-eddsa.c b/cipher/ecc-eddsa.c
--- a/cipher/ecc-eddsa.c
+++ b/cipher/ecc-eddsa.c
@@ -66,11 +66,12 @@
    malloced buffer with the encoded point is stored at R_BUFFER; the
    length of this buffer is stored at R_BUFLEN.  */
 static gpg_err_code_t
-eddsa_encodempi (gcry_mpi_t mpi, unsigned int minlen,
+eddsa_encodempi (gcry_mpi_t mpi, unsigned int nbits,
                  unsigned char **r_buffer, unsigned int *r_buflen)
 {
   unsigned char *rawmpi;
   unsigned int rawmpilen;
+  unsigned int minlen = (nbits%8) == 0 ? (nbits/8 + 1): (nbits+7)/8;
 
   rawmpi = _gcry_mpi_get_buffer (mpi, minlen, &rawmpilen, NULL);
   if (!rawmpi)
@@ -82,19 +83,20 @@
 }
 
 
-/* Encode (X,Y) using the EdDSA scheme.  MINLEN is the required length
-   in bytes for the result.  If WITH_PREFIX is set the returned buffer
-   is prefixed with a 0x40 byte.  On success 0 is returned and a
-   malloced buffer with the encoded point is stored at R_BUFFER; the
+/* Encode (X,Y) using the EdDSA scheme.  NBITS is the number of bits
+   of the field of the curve.  If WITH_PREFIX is set the returned
+   buffer is prefixed with a 0x40 byte.  On success 0 is returned and
+   a malloced buffer with the encoded point is stored at R_BUFFER; the
    length of this buffer is stored at R_BUFLEN.  */
 static gpg_err_code_t
-eddsa_encode_x_y (gcry_mpi_t x, gcry_mpi_t y, unsigned int minlen,
+eddsa_encode_x_y (gcry_mpi_t x, gcry_mpi_t y, unsigned int nbits,
                   int with_prefix,
                   unsigned char **r_buffer, unsigned int *r_buflen)
 {
   unsigned char *rawmpi;
   unsigned int rawmpilen;
   int off = with_prefix? 1:0;
+  unsigned int minlen = (nbits%8) == 0 ? (nbits/8 + 1): (nbits+7)/8;
 
   rawmpi = _gcry_mpi_get_buffer_extra (y, minlen, off?-1:0, &rawmpilen, NULL);
   if (!rawmpi)
@@ -133,7 +135,7 @@
       rc = GPG_ERR_INTERNAL;
     }
   else
-    rc = eddsa_encode_x_y (x, y, (ec->nbits+7)/8, with_prefix, r_buffer, r_buflen);
+    rc = eddsa_encode_x_y (x, y, ec->nbits, with_prefix, r_buffer, r_buflen);
 
   if (!x_in)
     mpi_free (x);
@@ -180,7 +182,7 @@
               return rc;
             }
 
-          rc = eddsa_encode_x_y (x, y, (nbits+7)/8, 0, &enc, &enclen);
+          rc = eddsa_encode_x_y (x, y, nbits, 0, &enc, &enclen);
           mpi_free (x);
           mpi_free (y);
           if (rc)
@@ -202,6 +204,76 @@
 }
 
 
+static gpg_err_code_t
+ecc_ed448_recover_x (gcry_mpi_t x, gcry_mpi_t y, int x_0, mpi_ec_t ec)
+{
+  gpg_err_code_t rc = 0;
+  gcry_mpi_t u, v, u3, v3, t;
+  static gcry_mpi_t p34; /* Hard coded (P-3)/4 */
+
+  if (mpi_cmp (y, ec->p) >= 0)
+    rc = GPG_ERR_INV_OBJ;
+
+  if (!p34)
+    p34 = scanval ("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+		   "BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+  u   = mpi_new (0);
+  v   = mpi_new (0);
+  u3  = mpi_new (0);
+  v3  = mpi_new (0);
+  t   = mpi_new (0);
+
+  /* Compute u and v */
+  /* u = y^2    */
+  mpi_mulm (u, y, y, ec->p);
+  /* v = b*y^2   */
+  mpi_mulm (v, ec->b, u, ec->p);
+  /* u = y^2-1  */
+  mpi_sub_ui (u, u, 1);
+  /* v = b*y^2-1 */
+  mpi_sub_ui (v, v, 1);
+
+  /* Compute sqrt(u/v) */
+  /* u3 = u^3 */
+  mpi_powm (u3, u, mpi_const (MPI_C_THREE), ec->p);
+  mpi_powm (v3, v, mpi_const (MPI_C_THREE), ec->p);
+  /* t = u^4 * u * v3 = u^5 * v^3 */
+  mpi_powm (t, u, mpi_const (MPI_C_FOUR), ec->p);
+  mpi_mulm (t, t, u, ec->p);
+  mpi_mulm (t, t, v3, ec->p);
+  /* t = t^((p-3)/4) = (u^5 * v^3)^((p-3)/4)  */
+  mpi_powm (t, t, p34, ec->p);
+  /* x = t * u^3 * v = (u^3 * v) * (u^5 * v^3)^((p-3)/4) */
+  mpi_mulm (t, t, u3, ec->p);
+  mpi_mulm (x, t, v, ec->p);
+
+  /* t = v * x^2  */
+  mpi_mulm (t, x, x, ec->p);
+  mpi_mulm (t, t, v, ec->p);
+
+  if (mpi_cmp (t, u) != 0)
+    rc = GPG_ERR_INV_OBJ;
+  else
+    {
+      if (!mpi_cmp_ui (x, 0) && x_0)
+        rc = GPG_ERR_INV_OBJ;
+
+      /* Choose the desired square root according to parity */
+      if (mpi_test_bit (x, 0) != !!x_0)
+        mpi_sub (x, ec->p, x);
+    }
+
+  mpi_free (t);
+  mpi_free (u3);
+  mpi_free (v3);
+  mpi_free (v);
+  mpi_free (u);
+
+  return rc;
+}
+
+
 /* Recover X from Y and SIGN (which actually is a parity bit).  */
 gpg_err_code_t
 _gcry_ecc_eddsa_recover_x (gcry_mpi_t x, gcry_mpi_t y, int sign, mpi_ec_t ec)
@@ -210,8 +282,16 @@
   gcry_mpi_t u, v, v3, t;
   static gcry_mpi_t p58, seven;
 
+  /*
+   * This routine is actually curve specific.  Now, only supports
+   * Ed25519 and Ed448.
+   */
+
   if (ec->dialect != ECC_DIALECT_ED25519)
-    return GPG_ERR_NOT_IMPLEMENTED;
+    /* For now, it's only Ed448.  */
+    return ecc_ed448_recover_x (x, y, sign, ec);
+
+  /* It's Ed25519.  */
 
   if (!p58)
     p58 = scanval ("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
@@ -299,20 +379,23 @@
   if (mpi_is_opaque (pk))
     {
       const unsigned char *buf;
+      unsigned int len;
+
+      len = (ctx->nbits%8) == 0 ? (ctx->nbits/8 + 1): (ctx->nbits+7)/8;
 
       buf = mpi_get_opaque (pk, &rawmpilen);
       if (!buf)
         return GPG_ERR_INV_OBJ;
       rawmpilen = (rawmpilen + 7)/8;
 
-      if (!(rawmpilen == (ctx->nbits+7)/8
-            || rawmpilen == (ctx->nbits+7)/8 + 1
-            || rawmpilen == (ctx->nbits+7)/8 * 2 + 1))
+      if (!(rawmpilen == len
+            || rawmpilen == len + 1
+            || rawmpilen == len * 2 + 1))
         return GPG_ERR_INV_OBJ;
 
       /* Handle compression prefixes.  The size of the buffer will be
          odd in this case.  */
-      if (rawmpilen > 1 && (rawmpilen%2))
+      if (rawmpilen > 1 && (rawmpilen == len + 1 || rawmpilen == len * 2 + 1))
         {
           /* First check whether the public key has been given in
              standard uncompressed format (SEC1).  No need to recover
@@ -335,7 +418,7 @@
 
               if (r_encpk)
                 {
-                  rc = eddsa_encode_x_y (x, y, (ctx->nbits+7)/8, 0,
+                  rc = eddsa_encode_x_y (x, y, ctx->nbits, 0,
                                          r_encpk, r_encpklen);
                   if (rc)
                     {
@@ -417,18 +500,25 @@
   unsigned char *rawmpi = NULL;
   unsigned int rawmpilen;
   unsigned char *digest;
-  gcry_buffer_t hvec[2];
   int hashalgo, b;
 
   *r_digest = NULL;
 
-  hashalgo = GCRY_MD_SHA512;
-  if (hashalgo != GCRY_MD_SHA512)
-    return GPG_ERR_DIGEST_ALGO;
-
   b = (ec->nbits+7)/8;
-  if (b != 256/8)
-    return GPG_ERR_INTERNAL; /* We only support 256 bit. */
+
+  /*
+   * Choice of hashalgo is curve specific.
+   * For now, it's determine by the bit size of the field.
+   */
+  if (ec->nbits == 255)
+    hashalgo = GCRY_MD_SHA512;
+  else if (ec->nbits == 448)
+    {
+      b++;
+      hashalgo = GCRY_MD_SHAKE256;
+    }
+  else
+    return GPG_ERR_NOT_IMPLEMENTED;
 
   /* Note that we clear DIGEST so we can use it as input to left pad
      the key with zeroes for hashing.  */
@@ -436,8 +526,6 @@
   if (!digest)
     return gpg_err_code_from_syserror ();
 
-  memset (hvec, 0, sizeof hvec);
-
   rawmpi = _gcry_mpi_get_buffer (ec->d, 0, &rawmpilen, NULL);
   if (!rawmpi)
     {
@@ -445,13 +533,38 @@
       return gpg_err_code_from_syserror ();
     }
 
-  hvec[0].data = digest;
-  hvec[0].off = 0;
-  hvec[0].len = b > rawmpilen? b - rawmpilen : 0;
-  hvec[1].data = rawmpi;
-  hvec[1].off = 0;
-  hvec[1].len = rawmpilen;
-  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2);
+  if (hashalgo == GCRY_MD_SHAKE256)
+    {
+      gcry_error_t err;
+      gcry_md_hd_t hd;
+
+      err = _gcry_md_open (&hd, hashalgo, 0);
+      if (err)
+        rc = gcry_err_code (err);
+      else
+        {
+          _gcry_md_write (hd, rawmpi, rawmpilen);
+          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
+          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
+          _gcry_md_close (hd);
+          rc = 0;
+        }
+    }
+  else
+    {
+      gcry_buffer_t hvec[2];
+
+      memset (hvec, 0, sizeof hvec);
+
+      hvec[0].data = digest;
+      hvec[0].off = 0;
+      hvec[0].len = b > rawmpilen? b - rawmpilen : 0;
+      hvec[1].data = rawmpi;
+      hvec[1].off = 0;
+      hvec[1].len = rawmpilen;
+      rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2);
+    }
+
   xfree (rawmpi);
   if (rc)
     {
@@ -460,9 +573,20 @@
     }
 
   /* Compute the A value.  */
-  reverse_buffer (digest, 32);  /* Only the first half of the hash.  */
-  digest[0]   = (digest[0] & 0x7f) | 0x40;
-  digest[31] &= 0xf8;
+  reverse_buffer (digest, b);  /* Only the first half of the hash.  */
+
+  /* Field specific handling of clearing/setting bits. */
+  if (ec->nbits == 255)
+    {
+      digest[0]   = (digest[0] & 0x7f) | 0x40;
+      digest[31] &= 0xf8;
+    }
+  else
+    {
+      digest[0]   = 0;
+      digest[1]  |= 0x80;
+      digest[56] &= 0xfc;
+    }
 
   *r_digest = digest;
   return 0;
@@ -483,51 +607,42 @@
 gpg_err_code_t
 _gcry_ecc_eddsa_genkey (mpi_ec_t ec, int flags)
 {
-  gpg_err_code_t rc;
-  int b = 256/8;             /* The only size we currently support.  */
+  int b;
   gcry_mpi_t a, x, y;
   mpi_point_struct Q;
   gcry_random_level_t random_level;
   char *dbuf;
   size_t dlen;
-  gcry_buffer_t hvec[1];
   unsigned char *hash_d = NULL;
 
   point_init (&Q);
-  memset (hvec, 0, sizeof hvec);
 
   if ((flags & PUBKEY_FLAG_TRANSIENT_KEY))
     random_level = GCRY_STRONG_RANDOM;
   else
     random_level = GCRY_VERY_STRONG_RANDOM;
 
+  b = (ec->nbits+7)/8;
+
+  if (ec->nbits == 255)
+    ;
+  else if (ec->nbits == 448)
+    b++;
+  else
+    return GPG_ERR_NOT_IMPLEMENTED;
+
+  dlen = b;
+
   a = mpi_snew (0);
   x = mpi_new (0);
   y = mpi_new (0);
 
   /* Generate a secret.  */
-  hash_d = xtrymalloc_secure (2*b);
-  if (!hash_d)
-    {
-      rc = gpg_err_code_from_syserror ();
-      goto leave;
-    }
-  dlen = b;
   dbuf = _gcry_random_bytes_secure (dlen, random_level);
-
-  /* Compute the A value.  */
-  hvec[0].data = dbuf;
-  hvec[0].len = dlen;
-  rc = _gcry_md_hash_buffers (GCRY_MD_SHA512, 0, hash_d, hvec, 1);
-  if (rc)
-    goto leave;
   ec->d = _gcry_mpi_set_opaque (NULL, dbuf, dlen*8);
-  dbuf = NULL;
-  reverse_buffer (hash_d, 32);  /* Only the first half of the hash.  */
-  hash_d[0] = (hash_d[0] & 0x7f) | 0x40;
-  hash_d[31] &= 0xf8;
-  _gcry_mpi_set_buffer (a, hash_d, 32, 0);
-  xfree (hash_d); hash_d = NULL;
+  _gcry_ecc_eddsa_compute_h_d (&hash_d, ec);
+  _gcry_mpi_set_buffer (a, hash_d, b, 0);
+  xfree (hash_d);
   /* log_printmpi ("ecgen         a", a); */
 
   /* Compute Q.  */
@@ -540,12 +655,10 @@
   Q.y = NULL;
   Q.x = NULL;
 
- leave:
   _gcry_mpi_release (a);
   _gcry_mpi_release (x);
   _gcry_mpi_release (y);
-  xfree (hash_d);
-  return rc;
+  return 0;
 }
 
 
@@ -558,21 +671,23 @@
  *
  * Despite that this function requires the specification of a hash
  * algorithm, we only support what has been specified by the paper.
- * This may change in the future.  Note that we don't check the used
- * curve; the user is responsible to use Ed25519.
+ * This may change in the future.
  *
  * Return the signature struct (r,s) from the message hash.  The caller
  * must have allocated R_R and S.
  */
+
+/* String to be used with Ed448 */
+#define DOM4_0_NONE     "SigEd448\0\0"
+#define DOM4_0_NONE_LEN 10
+
 gpg_err_code_t
 _gcry_ecc_eddsa_sign (gcry_mpi_t input, mpi_ec_t ec,
                       gcry_mpi_t r_r, gcry_mpi_t s, int hashalgo)
 {
   int rc;
-  int b;
   unsigned int tmp;
   unsigned char *digest = NULL;
-  gcry_buffer_t hvec[3];
   const void *mbuf;
   size_t mlen;
   unsigned char *rawmpi = NULL;
@@ -581,8 +696,16 @@
   unsigned int encpklen;
   mpi_point_struct I;          /* Intermediate value.  */
   gcry_mpi_t a, x, y, r;
+  int b;
 
-  memset (hvec, 0, sizeof hvec);
+  b = (ec->nbits+7)/8;
+
+  if (ec->nbits == 255)
+    ;
+  else if (ec->nbits == 448)
+    b++;
+  else
+    return GPG_ERR_NOT_IMPLEMENTED;
 
   if (!mpi_is_opaque (input))
     return GPG_ERR_INV_DATA;
@@ -594,17 +717,10 @@
   y = mpi_new (0);
   r = mpi_snew (0);
 
-  b = (ec->nbits+7)/8;
-  if (b != 256/8)
-    {
-      rc = GPG_ERR_INTERNAL; /* We only support 256 bit. */
-      goto leave;
-    }
-
   rc = _gcry_ecc_eddsa_compute_h_d (&digest, ec);
   if (rc)
     goto leave;
-  _gcry_mpi_set_buffer (a, digest, 32, 0);
+  _gcry_mpi_set_buffer (a, digest, b, 0);
 
   /* Compute the public key if it's not available (only secret part).  */
   if (ec->Q == NULL)
@@ -627,18 +743,46 @@
   if (DBG_CIPHER)
     log_printhex ("     m", mbuf, mlen);
 
-  hvec[0].data = digest;
-  hvec[0].off  = 32;
-  hvec[0].len  = 32;
-  hvec[1].data = (char*)mbuf;
-  hvec[1].len  = mlen;
-  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2);
+  if (hashalgo == GCRY_MD_SHAKE256)
+    {
+      gcry_error_t err;
+      gcry_md_hd_t hd;
+
+      err = _gcry_md_open (&hd, hashalgo, 0);
+      if (err)
+        rc = gcry_err_code (err);
+      else
+        {
+          _gcry_md_write (hd, DOM4_0_NONE, DOM4_0_NONE_LEN);
+          _gcry_md_write (hd, digest+b, b);
+          _gcry_md_write (hd, mbuf, mlen);
+          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
+          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
+          _gcry_md_close (hd);
+          rc = 0;
+        }
+    }
+  else
+    {
+      gcry_buffer_t hvec[3];
+
+      memset (hvec, 0, sizeof hvec);
+
+      hvec[0].data = digest;
+      hvec[0].off  = b;
+      hvec[0].len  = b;
+      hvec[1].data = (char*)mbuf;
+      hvec[1].len  = mlen;
+      rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2);
+    }
+
   if (rc)
     goto leave;
-  reverse_buffer (digest, 64);
+  reverse_buffer (digest, 2*b);
   if (DBG_CIPHER)
-    log_printhex ("     r", digest, 64);
-  _gcry_mpi_set_buffer (r, digest, 64, 0);
+    log_printhex ("     r", digest, 2*b);
+  _gcry_mpi_set_buffer (r, digest, 2*b, 0);
+  mpi_mod (r, r, ec->n);
   _gcry_mpi_ec_mul_point (&I, r, ec->G, ec);
   if (DBG_CIPHER)
     log_printpnt ("   r", &I, ec);
@@ -650,17 +794,45 @@
   if (DBG_CIPHER)
     log_printhex ("   e_r", rawmpi, rawmpilen);
 
-  /* S = r + a * H(encodepoint(R) + encodepoint(pk) + m) mod n  */
-  hvec[0].data = rawmpi;  /* (this is R) */
-  hvec[0].off  = 0;
-  hvec[0].len  = rawmpilen;
-  hvec[1].data = encpk;
-  hvec[1].off  = 0;
-  hvec[1].len  = encpklen;
-  hvec[2].data = (char*)mbuf;
-  hvec[2].off  = 0;
-  hvec[2].len  = mlen;
-  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 3);
+  if (hashalgo == GCRY_MD_SHAKE256)
+    {
+      gcry_error_t err;
+      gcry_md_hd_t hd;
+
+      err = _gcry_md_open (&hd, hashalgo, 0);
+      if (err)
+        rc = gcry_err_code (err);
+      else
+        {
+          _gcry_md_write (hd, DOM4_0_NONE, DOM4_0_NONE_LEN);
+          _gcry_md_write (hd, rawmpi, rawmpilen);
+          _gcry_md_write (hd, encpk, encpklen);
+          _gcry_md_write (hd, mbuf, mlen);
+          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
+          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
+          _gcry_md_close (hd);
+          rc = 0;
+        }
+    }
+  else
+    {
+      gcry_buffer_t hvec[3];
+
+      memset (hvec, 0, sizeof hvec);
+
+      /* S = r + a * H(encodepoint(R) + encodepoint(pk) + m) mod n  */
+      hvec[0].data = rawmpi;  /* (this is R) */
+      hvec[0].off  = 0;
+      hvec[0].len  = rawmpilen;
+      hvec[1].data = encpk;
+      hvec[1].off  = 0;
+      hvec[1].len  = encpklen;
+      hvec[2].data = (char*)mbuf;
+      hvec[2].off  = 0;
+      hvec[2].len  = mlen;
+      rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 3);
+    }
+
   if (rc)
     goto leave;
 
@@ -668,13 +840,13 @@
   mpi_set_opaque (r_r, rawmpi, rawmpilen*8);
   rawmpi = NULL;
 
-  reverse_buffer (digest, 64);
+  reverse_buffer (digest, 2*b);
   if (DBG_CIPHER)
-    log_printhex (" H(R+)", digest, 64);
-  _gcry_mpi_set_buffer (s, digest, 64, 0);
+    log_printhex (" H(R+)", digest, 2*b);
+  _gcry_mpi_set_buffer (s, digest, 2*b, 0);
   mpi_mulm (s, s, a, ec->n);
   mpi_addm (s, s, r, ec->n);
-  rc = eddsa_encodempi (s, b, &rawmpi, &rawmpilen);
+  rc = eddsa_encodempi (s, ec->nbits, &rawmpi, &rawmpilen);
   if (rc)
     goto leave;
   if (DBG_CIPHER)
@@ -713,15 +885,12 @@
   unsigned char *tbuf = NULL;
   size_t mlen, rlen;
   unsigned int tlen;
-  unsigned char digest[64];
-  gcry_buffer_t hvec[3];
+  unsigned char digest[114];
   gcry_mpi_t h, s;
   mpi_point_struct Ia, Ib;
 
   if (!mpi_is_opaque (input) || !mpi_is_opaque (r_in) || !mpi_is_opaque (s_in))
     return GPG_ERR_INV_DATA;
-  if (hashalgo != GCRY_MD_SHA512)
-    return GPG_ERR_DIGEST_ALGO;
 
   point_init (&Ia);
   point_init (&Ib);
@@ -729,11 +898,13 @@
   s = mpi_new (0);
 
   b = (ec->nbits+7)/8;
-  if (b != 256/8)
-    {
-      rc = GPG_ERR_INTERNAL; /* We only support 256 bit. */
-      goto leave;
-    }
+
+  if (ec->nbits == 255)
+    ;
+  else if (ec->nbits == 448)
+    b++;
+  else
+    return GPG_ERR_NOT_IMPLEMENTED;
 
   /* Encode and check the public key.  */
   rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL, 0,
@@ -768,23 +939,51 @@
       goto leave;
     }
 
-  /* h = H(encodepoint(R) + encodepoint(pk) + m)  */
-  hvec[0].data = (char*)rbuf;
-  hvec[0].off  = 0;
-  hvec[0].len  = rlen;
-  hvec[1].data = encpk;
-  hvec[1].off  = 0;
-  hvec[1].len  = encpklen;
-  hvec[2].data = (char*)mbuf;
-  hvec[2].off  = 0;
-  hvec[2].len  = mlen;
-  rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 3);
+  if (hashalgo == GCRY_MD_SHAKE256)
+    {
+      gcry_error_t err;
+      gcry_md_hd_t hd;
+
+      err = _gcry_md_open (&hd, hashalgo, 0);
+      if (err)
+        rc = gcry_err_code (err);
+      else
+        {
+          _gcry_md_write (hd, DOM4_0_NONE, DOM4_0_NONE_LEN);
+          _gcry_md_write (hd, rbuf, rlen);
+          _gcry_md_write (hd, encpk, encpklen);
+          _gcry_md_write (hd, mbuf, mlen);
+          _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0);
+          _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b);
+          _gcry_md_close (hd);
+          rc = 0;
+        }
+    }
+  else
+    {
+      gcry_buffer_t hvec[3];
+
+      memset (hvec, 0, sizeof hvec);
+
+      /* h = H(encodepoint(R) + encodepoint(pk) + m)  */
+      hvec[0].data = (char*)rbuf;
+      hvec[0].off  = 0;
+      hvec[0].len  = rlen;
+      hvec[1].data = encpk;
+      hvec[1].off  = 0;
+      hvec[1].len  = encpklen;
+      hvec[2].data = (char*)mbuf;
+      hvec[2].off  = 0;
+      hvec[2].len  = mlen;
+      rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 3);
+    }
+
   if (rc)
     goto leave;
-  reverse_buffer (digest, 64);
+  reverse_buffer (digest, 2*b);
   if (DBG_CIPHER)
-    log_printhex (" H(R+)", digest, 64);
-  _gcry_mpi_set_buffer (h, digest, 64, 0);
+    log_printhex (" H(R+)", digest, 2*b);
+  _gcry_mpi_set_buffer (h, digest, 2*b, 0);
 
   /* According to the paper the best way for verification is:
          encodepoint(sG - h·Q) = encodepoint(r)
diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c
--- a/cipher/pubkey-util.c
+++ b/cipher/pubkey-util.c
@@ -227,6 +227,8 @@
     { "sha3-384", GCRY_MD_SHA3_384 },
     { "sha3-512", GCRY_MD_SHA3_512 },
     { "sm3", GCRY_MD_SM3 },
+    { "shake128", GCRY_MD_SHAKE128 },
+    { "shake256", GCRY_MD_SHAKE256 },
     { NULL, 0 }
   };
   int algo;
diff --git a/mpi/ec.c b/mpi/ec.c
--- a/mpi/ec.c
+++ b/mpi/ec.c
@@ -1015,7 +1015,10 @@
 {
   gcry_err_code_t rc;
 
-  if (ec && ec->dialect == ECC_DIALECT_ED25519)
+  if (ec
+      && (ec->dialect == ECC_DIALECT_ED25519
+          || (ec->model == MPI_EC_EDWARDS
+              && ec->dialect == ECC_DIALECT_SAFECURVE)))
     rc = _gcry_ecc_eddsa_decodepoint (value, ec, result, NULL, NULL);
   else if (ec && ec->model == MPI_EC_MONTGOMERY)
     rc = _gcry_ecc_mont_decodepoint (value, ec, result);
diff --git a/tests/Makefile.am b/tests/Makefile.am
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -23,7 +23,7 @@
 	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 t-x448
+	t-ed25519 t-cv25519 t-x448 t-ed448
 
 tests_bin_last = benchmark bench-slope
 
@@ -58,7 +58,7 @@
 
 EXTRA_DIST = README rsa-16k.key cavs_tests.sh cavs_driver.pl \
 	     pkcs1v2-oaep.h pkcs1v2-pss.h pkcs1v2-v15c.h pkcs1v2-v15s.h \
-	     t-ed25519.inp stopwatch.h hashtest-256g.in \
+	     t-ed25519.inp t-ed448.inp stopwatch.h hashtest-256g.in \
 	     sha3-224.h sha3-256.h sha3-384.h sha3-512.h \
 	     blake2b.h blake2s.h \
 	     basic-disable-all-hwf.in basic_all_hwfeature_combinations.sh
diff --git a/tests/curves.c b/tests/curves.c
--- a/tests/curves.c
+++ b/tests/curves.c
@@ -33,7 +33,7 @@
 #include "t-common.h"
 
 /* Number of curves defined in ../cipger/ecc-curves.c */
-#define N_CURVES 26
+#define N_CURVES 27
 
 /* A real world sample public key.  */
 static char const sample_key_1[] =
diff --git a/tests/t-ed448.c b/tests/t-ed448.c
new file mode 100644
--- /dev/null
+++ b/tests/t-ed448.c
@@ -0,0 +1,497 @@
+/* t-ed448.c - Check the Ed448 crypto
+ * Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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-ed448"
+#include "t-common.h"
+#define N_TESTS 8
+
+static int sign_with_pk;
+static int no_verify;
+static int custom_data_file;
+
+
+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);
+}
+
+
+static void
+show_sexp (const char *prefix, gcry_sexp_t a)
+{
+  char *buf;
+  size_t size;
+
+  fprintf (stderr, "%s: ", PGM);
+  if (prefix)
+    fputs (prefix, stderr);
+  size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+  buf = xmalloc (size);
+
+  gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
+  fprintf (stderr, "%.*s", (int)size, buf);
+  gcry_free (buf);
+}
+
+
+/* Prepend FNAME with the srcdir environment variable's value and
+ * return an allocated filename.  */
+char *
+prepend_srcdir (const char *fname)
+{
+  static const char *srcdir;
+  char *result;
+
+  if (!srcdir && !(srcdir = getenv ("srcdir")))
+    srcdir = ".";
+
+  result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
+  strcpy (result, srcdir);
+  strcat (result, "/");
+  strcat (result, fname);
+  return result;
+}
+
+
+/* Read next line but skip over empty and comment lines.  Caller must
+   xfree the result.  */
+static char *
+read_textline (FILE *fp, int *lineno)
+{
+  char line[4096];
+  char *p;
+
+  do
+    {
+      if (!fgets (line, sizeof line, fp))
+        {
+          if (feof (fp))
+            return NULL;
+          die ("error reading input line: %s\n", strerror (errno));
+        }
+      ++*lineno;
+      p = strchr (line, '\n');
+      if (!p)
+        die ("input line %d not terminated or too long\n", *lineno);
+      *p = 0;
+      for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
+        *p = 0;
+    }
+  while (!*line || *line == '#');
+  /* if (debug) */
+  /*   info ("read line: '%s'\n", line); */
+  return xstrdup (line);
+}
+
+
+/* Copy the data after the tag to BUFFER.  BUFFER will be allocated as
+   needed.  */
+static void
+copy_data (char **buffer, const char *line, int lineno)
+{
+  const char *s;
+
+  xfree (*buffer);
+  *buffer = NULL;
+
+  s = strchr (line, ':');
+  if (!s)
+    {
+      fail ("syntax error at input line %d", lineno);
+      return;
+    }
+  for (s++; my_isascii (*s) && isspace (*s); s++)
+    ;
+  *buffer = xstrdup (s);
+}
+
+
+/* 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
+hexdowncase (char *string)
+{
+  char *p;
+
+  for (p=string; *p; p++)
+    if (my_isascii (*p))
+      *p = tolower (*p);
+}
+
+
+static void
+one_test (int testno, const char *sk, const char *pk,
+          const char *msg, const char *sig)
+{
+  gpg_error_t err;
+  int i;
+  char *p;
+  void *buffer = NULL;
+  void *buffer2 = NULL;
+  size_t buflen, buflen2;
+  gcry_sexp_t s_tmp, s_tmp2;
+  gcry_sexp_t s_sk = NULL;
+  gcry_sexp_t s_pk = NULL;
+  gcry_sexp_t s_msg= NULL;
+  gcry_sexp_t s_sig= NULL;
+  unsigned char *sig_r = NULL;
+  unsigned char *sig_s = NULL;
+  char *sig_rs_string = NULL;
+  size_t sig_r_len, sig_s_len;
+
+  if (verbose > 1)
+    info ("Running test %d\n", testno);
+
+  if (!(buffer = hex2buffer (sk, &buflen)))
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "sk", "invalid hex string");
+      goto leave;
+    }
+  if (!(buffer2 = hex2buffer (pk, &buflen2)))
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "pk", "invalid hex string");
+      goto leave;
+    }
+  if (sign_with_pk)
+    err = gcry_sexp_build (&s_sk, NULL,
+                           "(private-key"
+                           " (ecc"
+                           "  (curve \"Ed448\")"
+                           "  (flags eddsa)"
+                           "  (q %b)"
+                           "  (d %b)))",
+                           (int)buflen2, buffer2,
+                           (int)buflen, buffer);
+  else
+    err = gcry_sexp_build (&s_sk, NULL,
+                           "(private-key"
+                           " (ecc"
+                           "  (curve \"Ed448\")"
+                           "  (flags eddsa)"
+                           "  (d %b)))",
+                           (int)buflen, buffer);
+  if (err)
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "sk", gpg_strerror (err));
+      goto leave;
+    }
+
+  if ((err = gcry_sexp_build (&s_pk, NULL,
+                              "(public-key"
+                              " (ecc"
+                              "  (curve \"Ed448\")"
+                              "  (flags eddsa)"
+                              "  (q %b)))",  (int)buflen2, buffer2)))
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "pk", gpg_strerror (err));
+      goto leave;
+    }
+
+  xfree (buffer);
+  if (!(buffer = hex2buffer (msg, &buflen)))
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "msg", "invalid hex string");
+      goto leave;
+    }
+  if ((err = gcry_sexp_build (&s_msg, NULL,
+                              "(data"
+                              " (flags eddsa)"
+                              " (hash-algo shake256)"
+                              " (value %b))",  (int)buflen, buffer)))
+    {
+      fail ("error building s-exp for test %d, %s: %s",
+            testno, "msg", gpg_strerror (err));
+      goto leave;
+    }
+
+  if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
+    fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
+  if (debug)
+    show_sexp ("sig=", s_sig);
+
+  s_tmp2 = NULL;
+  s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
+  if (s_tmp)
+    {
+      s_tmp2 = s_tmp;
+      s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
+      if (s_tmp)
+        {
+          gcry_sexp_release (s_tmp2);
+          s_tmp2 = s_tmp;
+          s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
+          if (s_tmp)
+            {
+              sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
+              gcry_sexp_release (s_tmp);
+            }
+          s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
+          if (s_tmp)
+            {
+              sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
+              gcry_sexp_release (s_tmp);
+            }
+        }
+    }
+  gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
+
+  if (!sig_r || !sig_s)
+    fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
+  else
+    {
+      sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
+      p = sig_rs_string;
+      *p = 0;
+      for (i=0; i < sig_r_len; i++, p += 2)
+        snprintf (p, 3, "%02x", sig_r[i]);
+      for (i=0; i < sig_s_len; i++, p += 2)
+        snprintf (p, 3, "%02x", sig_s[i]);
+      if (strcmp (sig_rs_string, sig))
+        {
+          fail ("gcry_pk_sign failed for test %d: %s",
+                testno, "wrong value returned");
+          info ("  expected: '%s'", sig);
+          info ("       got: '%s'", sig_rs_string);
+        }
+    }
+
+  if (!no_verify)
+    if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
+      fail ("gcry_pk_verify failed for test %d: %s",
+            testno, gpg_strerror (err));
+
+
+ leave:
+  gcry_sexp_release (s_sig);
+  gcry_sexp_release (s_sk);
+  gcry_sexp_release (s_pk);
+  gcry_sexp_release (s_msg);
+  xfree (buffer);
+  xfree (buffer2);
+  xfree (sig_r);
+  xfree (sig_s);
+  xfree (sig_rs_string);
+}
+
+
+static void
+check_ed448 (const char *fname)
+{
+  FILE *fp;
+  int lineno, ntests;
+  char *line;
+  int testno;
+  char *sk, *pk, *msg, *sig;
+
+  info ("Checking Ed448.\n");
+
+  fp = fopen (fname, "r");
+  if (!fp)
+    die ("error opening '%s': %s\n", fname, strerror (errno));
+
+  testno = 0;
+  sk = pk = msg = sig = NULL;
+  lineno = ntests = 0;
+  while ((line = read_textline (fp, &lineno)))
+    {
+      if (!strncmp (line, "TST:", 4))
+        testno = atoi (line+4);
+      else if (!strncmp (line, "SK:", 3))
+        copy_data (&sk, line, lineno);
+      else if (!strncmp (line, "PK:", 3))
+        copy_data (&pk, line, lineno);
+      else if (!strncmp (line, "MSG:", 4))
+        copy_data (&msg, line, lineno);
+      else if (!strncmp (line, "SIG:", 4))
+        copy_data (&sig, line, lineno);
+      else
+        fail ("unknown tag at input line %d", lineno);
+
+      xfree (line);
+      if (testno && sk && pk && msg && sig)
+        {
+          hexdowncase (sig);
+          one_test (testno, sk, pk, msg, sig);
+          ntests++;
+          if (!(ntests % 256))
+            show_note ("%d of %d tests done\n", ntests, N_TESTS);
+          xfree (pk);  pk = NULL;
+          xfree (sk);  sk = NULL;
+          xfree (msg); msg = NULL;
+          xfree (sig); sig = NULL;
+        }
+
+    }
+  xfree (pk);
+  xfree (sk);
+  xfree (msg);
+  xfree (sig);
+
+  if (ntests != N_TESTS && !custom_data_file)
+    fail ("did %d tests but expected %d", ntests, N_TESTS);
+  else if ((ntests % 256))
+    show_note ("%d tests done\n", ntests);
+
+  fclose (fp);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+  char *fname = NULL;
+
+  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"
+                 "  --sign-with-pk  also use the public key for signing\n"
+                 "  --no-verify     skip the verify test\n"
+                 "  --data FNAME    take test data from file FNAME\n",
+                 stdout);
+          exit (0);
+        }
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose++;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--debug"))
+        {
+          verbose += 2;
+          debug++;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--sign-with-pk"))
+        {
+          sign_with_pk = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--no-verify"))
+        {
+          no_verify = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--data"))
+        {
+          argc--; argv++;
+          if (argc)
+            {
+              xfree (fname);
+              fname = xstrdup (*argv);
+              argc--; argv++;
+            }
+        }
+      else if (!strncmp (*argv, "--", 2))
+        die ("unknown option '%s'", *argv);
+
+    }
+
+  if (!fname)
+    fname = prepend_srcdir ("t-ed448.inp");
+  else
+    custom_data_file = 1;
+
+  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));
+
+  /* Ed448 isn't supported in fips mode */
+  if (gcry_fips_mode_active())
+    return 77;
+
+  start_timer ();
+  check_ed448 (fname);
+  stop_timer ();
+
+  xfree (fname);
+
+  info ("All tests completed in %s.  Errors: %d\n",
+        elapsed_time (1), error_count);
+  return !!error_count;
+}
diff --git a/tests/t-ed448.inp b/tests/t-ed448.inp
new file mode 100644
--- /dev/null
+++ b/tests/t-ed448.inp
@@ -0,0 +1,53 @@
+# t-ed448.inp
+# This is from RFC 8032, the section
+#
+#     7.4.  Test Vectors for Ed448
+#
+
+TST: 1
+SK: 6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b
+PK: 5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180
+MSG:
+SIG: 533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600
+
+TST: 2
+SK: c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e
+PK: 43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480
+MSG: 03
+SIG: 26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00
+
+TST: 3
+SK: cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffdf60500553abc0e05cd02184bdb89c4ccd67e187951267eb328
+PK: dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400
+MSG: 0c3e544074ec63b0265e0c
+SIG: 1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d389dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b051068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5028961c9bf8ffd973fe5d5c206492b140e00
+
+TST: 4
+SK: 258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b
+PK: 3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580
+MSG: 64a65f3cdedcdd66811e2915
+SIG: 7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457eb1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e72003cbae6d6b8b827e4e6c143064ff3c00
+
+TST: 5
+SK: 7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e
+PK: b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80
+MSG: 64a65f3cdedcdd66811e2915e7
+SIG: 6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb4890810c162c027468675ecf645a83176c0d7323a2ccde2d80efe5a1268e8aca1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e28196fd1caf56b4e7e0ba5519234d047155ac727a1053100
+
+TST: 6
+SK: d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01
+PK: df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00
+MSG: bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944
+SIG: 554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b18dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722ef552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba5f30e88e36ec2703b349ca229c2670833900
+
+TST: 7
+SK: 2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5
+PK: 79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00
+MSG: 15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a6039c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11
+SIG: c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00
+
+TST: 8
+SK: 872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8
+PK: a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400
+MSG: 6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e972660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f52096cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db977025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f0410a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf9696149e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a6059d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87
+SIG: e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bddf7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed573603ce30d8bb761785dc30dbc320869e1a00