gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet] branch master updated: GNS: Add EDKEY support.


From: gnunet
Subject: [gnunet] branch master updated: GNS: Add EDKEY support.
Date: Sun, 02 May 2021 10:42:11 +0200

This is an automated email from the git hooks/post-receive script.

martin-schanzenbach pushed a commit to branch master
in repository gnunet.

The following commit(s) were added to refs/heads/master by this push:
     new 572f4d6f7 GNS: Add EDKEY support.
572f4d6f7 is described below

commit 572f4d6f7b19dec42d571829384ac9cd356bb092
Author: Martin Schanzenbach <mschanzenbach@posteo.de>
AuthorDate: Sat May 1 22:05:15 2021 +0200

    GNS: Add EDKEY support.
    
    GNS and GNSRECORD can now handle EdDSA keys
    in addition to the existing ECDSA scheme.
    See also LSD0001.
---
 po/POTFILES.in                          |   2 +
 src/gnsrecord/gnsrecord_crypto.c        | 426 ++++++++++++++++++++++++++++----
 src/gnsrecord/gnsrecord_misc.c          |  17 ++
 src/gnsrecord/gnsrecord_serialization.c |   3 +-
 src/gnsrecord/test_gnsrecord_crypto.c   |  36 ++-
 src/include/gnunet_crypto_lib.h         |  78 ++++++
 src/include/gnunet_gnsrecord_lib.h      |  33 ++-
 src/util/Makefile.am                    |   1 +
 src/util/crypto_ecc.c                   | 129 ----------
 src/util/crypto_ecc_gnsrecord.c         | 351 ++++++++++++++++++++++++++
 10 files changed, 891 insertions(+), 185 deletions(-)

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3416e6c84..996b8bda2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -313,6 +313,7 @@ src/reclaim/plugin_rest_reclaim.c
 src/reclaim/reclaim_api.c
 src/reclaim/reclaim_attribute.c
 src/reclaim/reclaim_credential.c
+src/reclaim/test.c
 src/regex/gnunet-daemon-regexprofiler.c
 src/regex/gnunet-regex-profiler.c
 src/regex/gnunet-regex-simulation-profiler.c
@@ -517,6 +518,7 @@ src/util/crypto_abe.c
 src/util/crypto_crc.c
 src/util/crypto_ecc.c
 src/util/crypto_ecc_dlog.c
+src/util/crypto_ecc_gnsrecord.c
 src/util/crypto_ecc_setup.c
 src/util/crypto_hash.c
 src/util/crypto_hash_file.c
diff --git a/src/gnsrecord/gnsrecord_crypto.c b/src/gnsrecord/gnsrecord_crypto.c
index 9c551a936..289f0e885 100644
--- a/src/gnsrecord/gnsrecord_crypto.c
+++ b/src/gnsrecord/gnsrecord_crypto.c
@@ -64,7 +64,6 @@ ecdsa_symmetric_decrypt (
 }
 
 
-
 ssize_t
 ecdsa_symmetric_encrypt (
   const void *block,
@@ -92,6 +91,34 @@ ecdsa_symmetric_encrypt (
 }
 
 
+enum GNUNET_GenericReturnValue
+eddsa_symmetric_decrypt (
+  const void *block,
+  size_t size,
+  const unsigned char *key,
+  const unsigned char *nonce,
+  void *result)
+{
+  if (0 != crypto_secretbox_open_easy (result, block, size, nonce, key))
+  {
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+eddsa_symmetric_encrypt (
+  const void *block,
+  size_t size,
+  const unsigned char *key,
+  const unsigned char *nonce,
+  void *result)
+{
+  crypto_secretbox_easy (result, block, size, nonce, key);
+  return GNUNET_OK;
+}
+
 
 /**
  * Derive session key and iv from label and public key.
@@ -130,6 +157,42 @@ derive_block_aes_key (unsigned char *ctr,
 }
 
 
+/**
+ * Derive session key and iv from label and public key.
+ *
+ * @param nonce initialization vector to initialize
+ * @param skey session key to initialize
+ * @param label label to use for KDF
+ * @param pub public key to use for KDF
+ */
+static void
+derive_block_xsalsa_key (unsigned char *nonce,
+                         unsigned char *key,
+                         const char *label,
+                         uint64_t exp,
+                         const struct GNUNET_CRYPTO_EddsaPublicKey *pub)
+{
+  static const char ctx_key[] = "gns-aes-ctx-key";
+  static const char ctx_iv[] = "gns-aes-ctx-iv";
+
+  GNUNET_CRYPTO_kdf (key, crypto_secretbox_KEYBYTES,
+                     ctx_key, strlen (ctx_key),
+                     pub, sizeof(struct GNUNET_CRYPTO_EddsaPublicKey),
+                     label, strlen (label),
+                     NULL, 0);
+  memset (nonce, 0, crypto_secretbox_NONCEBYTES);
+  /** 16 byte nonce **/
+  GNUNET_CRYPTO_kdf (nonce, (crypto_secretbox_NONCEBYTES - sizeof (exp)),
+                     ctx_iv, strlen (ctx_iv),
+                     pub, sizeof(struct GNUNET_CRYPTO_EddsaPublicKey),
+                     label, strlen (label),
+                     NULL, 0);
+  /** Expiration time 64 bit. **/
+  memcpy (nonce + (crypto_secretbox_NONCEBYTES - sizeof (exp)),
+          &exp, sizeof (exp));
+}
+
+
 /**
  * Sign name and records
  *
@@ -245,6 +308,119 @@ block_create_ecdsa (const struct 
GNUNET_CRYPTO_EcdsaPrivateKey *key,
 }
 
 
+/**
+ * Sign name and records (EDDSA version)
+ *
+ * @param key the private key
+ * @param pkey associated public key
+ * @param expire block expiration
+ * @param label the name for the records
+ * @param rd record data
+ * @param rd_count number of records
+ * @return NULL on error (block too large)
+ */
+static struct GNUNET_GNSRECORD_Block *
+block_create_eddsa (const struct GNUNET_CRYPTO_EddsaPrivateKey *key,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
+                    struct GNUNET_TIME_Absolute expire,
+                    const char *label,
+                    const struct GNUNET_GNSRECORD_Data *rd,
+                    unsigned int rd_count)
+{
+  ssize_t payload_len = GNUNET_GNSRECORD_records_get_size (rd_count,
+                                                           rd);
+  struct GNUNET_GNSRECORD_Block *block;
+  struct GNUNET_GNSRECORD_EddsaBlock *edblock;
+  struct GNUNET_CRYPTO_EddsaPrivateScalar dkey;
+  unsigned char nonce[crypto_secretbox_NONCEBYTES];
+  unsigned char skey[crypto_secretbox_KEYBYTES];
+  struct GNUNET_GNSRECORD_Data rdc[GNUNET_NZL (rd_count)];
+  uint32_t rd_count_nbo;
+  struct GNUNET_TIME_Absolute now;
+
+  if (payload_len < 0)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  if (payload_len > GNUNET_GNSRECORD_MAX_BLOCK_SIZE)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  /* convert relative to absolute times */
+  now = GNUNET_TIME_absolute_get ();
+  for (unsigned int i = 0; i < rd_count; i++)
+  {
+    rdc[i] = rd[i];
+    if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
+    {
+      struct GNUNET_TIME_Relative t;
+
+      /* encrypted blocks must never have relative expiration times, convert! 
*/
+      rdc[i].flags &= ~GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
+      t.rel_value_us = rdc[i].expiration_time;
+      rdc[i].expiration_time = GNUNET_TIME_absolute_add (now, t).abs_value_us;
+    }
+  }
+  /* serialize */
+  rd_count_nbo = htonl (rd_count);
+  {
+    char payload[sizeof(uint32_t) + payload_len];
+
+    GNUNET_memcpy (payload,
+                   &rd_count_nbo,
+                   sizeof(uint32_t));
+    GNUNET_assert (payload_len ==
+                   GNUNET_GNSRECORD_records_serialize (rd_count,
+                                                       rdc,
+                                                       payload_len,
+                                                       
&payload[sizeof(uint32_t)
+                                                       ]));
+    block = GNUNET_malloc (sizeof(struct GNUNET_GNSRECORD_Block)
+                           + sizeof(uint32_t)
+                           + payload_len
+                           + crypto_secretbox_MACBYTES);
+    edblock = &block->eddsa_block;
+    block->type = htonl (GNUNET_GNSRECORD_TYPE_EDKEY);
+    edblock->purpose.size = htonl (sizeof(uint32_t)
+                                   + payload_len
+                                   + sizeof(struct
+                                            GNUNET_CRYPTO_EccSignaturePurpose)
+                                   + sizeof(struct GNUNET_TIME_AbsoluteNBO)
+                                   + crypto_secretbox_MACBYTES);
+    edblock->purpose.purpose = htonl 
(GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
+    edblock->expiration_time = GNUNET_TIME_absolute_hton (expire);
+    /* encrypt and sign */
+    GNUNET_CRYPTO_eddsa_private_key_derive (key,
+                                            label,
+                                            "gns",
+                                            &dkey);
+    // FIXME: We may want a key_get_public_from_private_scalar function
+    struct GNUNET_CRYPTO_EddsaPublicKey test;
+    crypto_scalarmult_ed25519_base_noclamp (test.q_y,
+                                            dkey.s);
+    edblock->derived_key = test;
+    derive_block_xsalsa_key (nonce,
+                             skey,
+                             label,
+                             edblock->expiration_time.abs_value_us__,
+                             pkey);
+    GNUNET_break (GNUNET_OK ==
+                  eddsa_symmetric_encrypt (payload,
+                                           payload_len
+                                           + sizeof(uint32_t),
+                                           skey,
+                                           nonce,
+                                           &edblock[1]));
+  }
+  GNUNET_CRYPTO_eddsa_sign_with_scalar (&dkey,
+                                        &edblock->purpose,
+                                        &edblock->signature);
+  return block;
+}
+
+
 /**
  * Sign name and records
  *
@@ -263,6 +439,7 @@ GNUNET_GNSRECORD_block_create (const struct 
GNUNET_IDENTITY_PrivateKey *key,
                                unsigned int rd_count)
 {
   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
+  struct GNUNET_CRYPTO_EddsaPublicKey edkey;
 
   switch (ntohl (key->type))
   {
@@ -275,6 +452,15 @@ GNUNET_GNSRECORD_block_create (const struct 
GNUNET_IDENTITY_PrivateKey *key,
                                label,
                                rd,
                                rd_count);
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
+    GNUNET_CRYPTO_eddsa_key_get_public (&key->eddsa_key,
+                                        &edkey);
+    return block_create_eddsa (&key->eddsa_key,
+                               &edkey,
+                               expire,
+                               label,
+                               rd,
+                               rd_count);
   default:
     GNUNET_assert (0);
   }
@@ -319,33 +505,45 @@ GNUNET_GNSRECORD_block_create2 (const struct 
GNUNET_IDENTITY_PrivateKey *pkey,
                                 unsigned int rd_count)
 {
   const struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
+  struct GNUNET_CRYPTO_EddsaPublicKey edpubkey;
 
-  if (GNUNET_IDENTITY_TYPE_ECDSA != ntohl (pkey->type))
+  if (GNUNET_IDENTITY_TYPE_ECDSA == ntohl (pkey->type))
   {
-    return NULL; // FIXME
-  }
-  key = &pkey->ecdsa_key;
+    key = &pkey->ecdsa_key;
 #define CSIZE 64
-  static struct KeyCacheLine cache[CSIZE];
-  struct KeyCacheLine *line;
+    static struct KeyCacheLine cache[CSIZE];
+    struct KeyCacheLine *line;
 
-  line = &cache[(*(unsigned int *) key) % CSIZE];
-  if (0 != memcmp (&line->key,
-                   key,
-                   sizeof(*key)))
+    line = &cache[(*(unsigned int *) key) % CSIZE];
+    if (0 != memcmp (&line->key,
+                     key,
+                     sizeof(*key)))
+    {
+      /* cache miss, recompute */
+      line->key = *key;
+      GNUNET_CRYPTO_ecdsa_key_get_public (key,
+                                          &line->pkey);
+    }
+#undef CSIZE
+    return block_create_ecdsa (key,
+                               &line->pkey,
+                               expire,
+                               label,
+                               rd,
+                               rd_count);
+  }
+  else if (GNUNET_IDENTITY_TYPE_EDDSA == ntohl (pkey->type))
   {
-    /* cache miss, recompute */
-    line->key = *key;
-    GNUNET_CRYPTO_ecdsa_key_get_public (key,
-                                        &line->pkey);
+    GNUNET_CRYPTO_eddsa_key_get_public (&pkey->eddsa_key,
+                                        &edpubkey);
+    return block_create_eddsa (&pkey->eddsa_key,
+                               &edpubkey,
+                               expire,
+                               label,
+                               rd,
+                               rd_count);
   }
-#undef CSIZE
-  return block_create_ecdsa (key,
-                             &line->pkey,
-                             expire,
-                             label,
-                             rd,
-                             rd_count);
+  return NULL;
 }
 
 
@@ -359,21 +557,23 @@ GNUNET_GNSRECORD_block_create2 (const struct 
GNUNET_IDENTITY_PrivateKey *pkey,
 enum GNUNET_GenericReturnValue
 GNUNET_GNSRECORD_block_verify (const struct GNUNET_GNSRECORD_Block *block)
 {
-  const struct GNUNET_CRYPTO_EcdsaPublicKey *key;
-  const struct GNUNET_GNSRECORD_EcdsaBlock *ecblock;
-
-  if (GNUNET_GNSRECORD_TYPE_PKEY != ntohl (block->type))
+  switch (ntohl (block->type))
   {
-    GNUNET_break (0);
+  case GNUNET_GNSRECORD_TYPE_PKEY:
+    return GNUNET_CRYPTO_ecdsa_verify_ (
+      GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
+      &block->ecdsa_block.purpose,
+      &block->ecdsa_block.signature,
+      &block->ecdsa_block.derived_key);
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
+    return GNUNET_CRYPTO_eddsa_verify_ (
+      GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
+      &block->eddsa_block.purpose,
+      &block->eddsa_block.signature,
+      &block->eddsa_block.derived_key);
+  default:
     return GNUNET_NO;
   }
-  ecblock = &block->ecdsa_block;
-  key = &ecblock->derived_key;
-
-  return GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
-                                      &ecblock->purpose,
-                                      &ecblock->signature,
-                                      key);
 }
 
 
@@ -505,6 +705,134 @@ block_decrypt_ecdsa (const struct 
GNUNET_GNSRECORD_EcdsaBlock *block,
 }
 
 
+enum GNUNET_GenericReturnValue
+block_decrypt_eddsa (const struct GNUNET_GNSRECORD_EddsaBlock *block,
+                     const struct
+                     GNUNET_CRYPTO_EddsaPublicKey *zone_key,
+                     const char *label,
+                     GNUNET_GNSRECORD_RecordCallback proc,
+                     void *proc_cls)
+{
+  size_t payload_len = ntohl (block->purpose.size)
+                       - sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
+                       - sizeof(struct GNUNET_TIME_AbsoluteNBO);
+  unsigned char nonce[crypto_secretbox_NONCEBYTES];
+  unsigned char key[crypto_secretbox_KEYBYTES];
+
+  if (ntohl (block->purpose.size) <
+      sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
+      + sizeof(struct GNUNET_TIME_AbsoluteNBO))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  derive_block_xsalsa_key (nonce,
+                           key,
+                           label,
+                           block->expiration_time.abs_value_us__,
+                           zone_key);
+  {
+    char payload[payload_len];
+    uint32_t rd_count;
+
+    GNUNET_break (GNUNET_OK ==
+                  eddsa_symmetric_decrypt (&block[1], payload_len,
+                                           key, nonce,
+                                           payload));
+    GNUNET_memcpy (&rd_count,
+                   payload,
+                   sizeof(uint32_t));
+    rd_count = ntohl (rd_count);
+    if (rd_count > 2048)
+    {
+      /* limit to sane value */
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    {
+      struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)];
+      unsigned int j;
+      struct GNUNET_TIME_Absolute now;
+
+      if (GNUNET_OK !=
+          GNUNET_GNSRECORD_records_deserialize (payload_len - sizeof(uint32_t),
+                                                &payload[sizeof(uint32_t)],
+                                                rd_count,
+                                                rd))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      /* hide expired records */
+      now = GNUNET_TIME_absolute_get ();
+      j = 0;
+      for (unsigned int i = 0; i < rd_count; i++)
+      {
+        if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
+        {
+          /* encrypted blocks must never have relative expiration times, skip! 
*/
+          GNUNET_break_op (0);
+          continue;
+        }
+
+        if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD))
+        {
+          int include_record = GNUNET_YES;
+          /* Shadow record, figure out if we have a not expired active record 
*/
+          for (unsigned int k = 0; k < rd_count; k++)
+          {
+            if (k == i)
+              continue;
+            if (rd[i].expiration_time < now.abs_value_us)
+              include_record = GNUNET_NO;       /* Shadow record is expired */
+            if ((rd[k].record_type == rd[i].record_type) &&
+                (rd[k].expiration_time >= now.abs_value_us) &&
+                (0 == (rd[k].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD)))
+            {
+              include_record = GNUNET_NO;         /* We have a non-expired, 
non-shadow record of the same type */
+              GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                          "Ignoring shadow record\n");
+              break;
+            }
+          }
+          if (GNUNET_YES == include_record)
+          {
+            rd[i].flags ^= GNUNET_GNSRECORD_RF_SHADOW_RECORD;       /* Remove 
Flag */
+            if (j != i)
+              rd[j] = rd[i];
+            j++;
+          }
+        }
+        else if (rd[i].expiration_time >= now.abs_value_us)
+        {
+          /* Include this record */
+          if (j != i)
+            rd[j] = rd[i];
+          j++;
+        }
+        else
+        {
+          struct GNUNET_TIME_Absolute at;
+
+          at.abs_value_us = rd[i].expiration_time;
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                      "Excluding record that expired %s (%llu ago)\n",
+                      GNUNET_STRINGS_absolute_time_to_string (at),
+                      (unsigned long long) rd[i].expiration_time
+                      - now.abs_value_us);
+        }
+      }
+      rd_count = j;
+      if (NULL != proc)
+        proc (proc_cls,
+              rd_count,
+              (0 != rd_count) ? rd : NULL);
+    }
+  }
+  return GNUNET_OK;
+}
+
+
 /**
  * Decrypt block.
  *
@@ -524,17 +852,17 @@ GNUNET_GNSRECORD_block_decrypt (const struct 
GNUNET_GNSRECORD_Block *block,
                                 GNUNET_GNSRECORD_RecordCallback proc,
                                 void *proc_cls)
 {
-  const struct GNUNET_CRYPTO_EcdsaPublicKey *key;
-
-  if (GNUNET_IDENTITY_TYPE_ECDSA != ntohl (zone_key->type))
+  switch (ntohl (zone_key->type))
   {
-    return GNUNET_NO;
+  case GNUNET_IDENTITY_TYPE_ECDSA:
+    return block_decrypt_ecdsa (&block->ecdsa_block,
+                                &zone_key->ecdsa_key, label, proc, proc_cls);
+  case GNUNET_IDENTITY_TYPE_EDDSA:
+    return block_decrypt_eddsa (&block->eddsa_block,
+                                &zone_key->eddsa_key, label, proc, proc_cls);
+  default:
+    return GNUNET_SYSERR;
   }
-  key = &zone_key->ecdsa_key;
-
-  return block_decrypt_ecdsa (&block->ecdsa_block,
-                              key, label, proc, proc_cls);
-
 }
 
 
@@ -555,6 +883,7 @@ GNUNET_GNSRECORD_query_from_private_key (const struct
   switch (ntohl (zone->type))
   {
   case GNUNET_GNSRECORD_TYPE_PKEY:
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
 
     GNUNET_IDENTITY_key_get_public (zone,
                                     &pub);
@@ -570,6 +899,7 @@ GNUNET_GNSRECORD_query_from_private_key (const struct
 
 /**
  * Calculate the DHT query for a given @a label in a given @a zone.
+ * FIXME: We may want to plugin-ize this at some point.
  *
  * @param pub public key of the zone
  * @param label label of the record
@@ -595,6 +925,16 @@ GNUNET_GNSRECORD_query_from_public_key (const struct
                         sizeof (pd.ecdsa_key),
                         query);
     break;
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
+    pd.type = pub->type;
+    GNUNET_CRYPTO_eddsa_public_key_derive (&pub->eddsa_key,
+                                           label,
+                                           "gns",
+                                           &pd.eddsa_key);
+    GNUNET_CRYPTO_hash (&pd.eddsa_key,
+                        sizeof (pd.eddsa_key),
+                        query);
+    break;
   default:
     GNUNET_assert (0);
   }
diff --git a/src/gnsrecord/gnsrecord_misc.c b/src/gnsrecord/gnsrecord_misc.c
index b907eed27..2fe315bd8 100644
--- a/src/gnsrecord/gnsrecord_misc.c
+++ b/src/gnsrecord/gnsrecord_misc.c
@@ -338,6 +338,12 @@ GNUNET_GNSRECORD_block_get_size (const struct 
GNUNET_GNSRECORD_Block *block)
            + ntohl (block->ecdsa_block.purpose.size)   /* Length of signed 
data */
            - sizeof (block->ecdsa_block.purpose);   /* Purpose already in 
EcdsaBlock */
     break;
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
+    return sizeof (uint32_t)   /* zone type */
+           + sizeof (block->eddsa_block)   /* EddsaBlock */
+           + ntohl (block->eddsa_block.purpose.size)   /* Length of signed 
data */
+           - sizeof (block->ecdsa_block.purpose);   /* Purpose already in 
EcdsaBlock */
+
   default:
     return 0;
   }
@@ -354,6 +360,8 @@ GNUNET_GNSRECORD_block_get_expiration (const struct
   {
   case GNUNET_GNSRECORD_TYPE_PKEY:
     return GNUNET_TIME_absolute_ntoh (block->ecdsa_block.expiration_time);
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
+    return GNUNET_TIME_absolute_ntoh (block->eddsa_block.expiration_time);
   default:
     GNUNET_break (0); /* Hopefully we never get here, but we might */
   }
@@ -373,6 +381,11 @@ GNUNET_GNSRECORD_query_from_block (const struct 
GNUNET_GNSRECORD_Block *block,
                         sizeof (block->ecdsa_block.derived_key),
                         query);
     return GNUNET_OK;
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
+    GNUNET_CRYPTO_hash (&block->eddsa_block.derived_key,
+                        sizeof (block->eddsa_block.derived_key),
+                        query);
+    return GNUNET_OK;
   default:
     return GNUNET_SYSERR;
   }
@@ -394,6 +407,10 @@ GNUNET_GNSRECORD_record_to_identity_key (const struct 
GNUNET_GNSRECORD_Data *rd,
     key->type = htonl (rd->record_type);
     memcpy (&key->ecdsa_key, rd->data, sizeof (key->ecdsa_key));
     return GNUNET_OK;
+  case GNUNET_GNSRECORD_TYPE_EDKEY:
+    key->type = htonl (rd->record_type);
+    memcpy (&key->eddsa_key, rd->data, sizeof (key->eddsa_key));
+    return GNUNET_OK;
   default:
     return GNUNET_SYSERR;
   }
diff --git a/src/gnsrecord/gnsrecord_serialization.c 
b/src/gnsrecord/gnsrecord_serialization.c
index 42665e662..cb6957605 100644
--- a/src/gnsrecord/gnsrecord_serialization.c
+++ b/src/gnsrecord/gnsrecord_serialization.c
@@ -124,7 +124,8 @@ GNUNET_GNSRECORD_records_get_size (unsigned int rd_count,
     return -1;
   }
   // Do not pad PKEY
-  if (GNUNET_GNSRECORD_TYPE_PKEY == rd->record_type)
+  if ((GNUNET_GNSRECORD_TYPE_PKEY == rd->record_type) ||
+      (GNUNET_GNSRECORD_TYPE_EDKEY == rd->record_type))
     return ret;
   /**
    * Efficiently round up to the next
diff --git a/src/gnsrecord/test_gnsrecord_crypto.c 
b/src/gnsrecord/test_gnsrecord_crypto.c
index d541f3076..9394f562d 100644
--- a/src/gnsrecord/test_gnsrecord_crypto.c
+++ b/src/gnsrecord/test_gnsrecord_crypto.c
@@ -92,29 +92,22 @@ rd_decrypt_cb (void *cls,
   res = 0;
 }
 
-
 static void
-run (void *cls,
-     char *const *args,
-     const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *cfg)
+test_with_type (struct GNUNET_IDENTITY_PrivateKey *privkey)
 {
   struct GNUNET_GNSRECORD_Block *block;
   struct GNUNET_IDENTITY_PublicKey pubkey;
   struct GNUNET_HashCode query_pub;
   struct GNUNET_HashCode query_priv;
   struct GNUNET_TIME_Absolute expire = GNUNET_TIME_absolute_get ();
-  struct GNUNET_IDENTITY_PrivateKey privkey;
 
 
-  privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
-  GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key);
   /* get public key */
-  GNUNET_IDENTITY_key_get_public (&privkey,
+  GNUNET_IDENTITY_key_get_public (privkey,
                                   &pubkey);
 
   /* test query derivation */
-  GNUNET_GNSRECORD_query_from_private_key (&privkey,
+  GNUNET_GNSRECORD_query_from_private_key (privkey,
                                            "testlabel",
                                            &query_priv);
   GNUNET_GNSRECORD_query_from_public_key (&pubkey,
@@ -129,7 +122,7 @@ run (void *cls,
 
   /* Create block */
   GNUNET_assert (NULL != (block =
-                            GNUNET_GNSRECORD_block_create (&privkey,
+                            GNUNET_GNSRECORD_block_create (privkey,
                                                            expire,
                                                            s_name,
                                                            s_rd,
@@ -146,6 +139,27 @@ run (void *cls,
 }
 
 
+
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_IDENTITY_PrivateKey privkey;
+  struct GNUNET_IDENTITY_PrivateKey privkey_ed;
+
+
+  privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
+  GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key);
+  test_with_type (&privkey);
+
+  privkey_ed.type = htonl (GNUNET_GNSRECORD_TYPE_EDKEY);
+  GNUNET_CRYPTO_eddsa_key_create (&privkey_ed.eddsa_key);
+  test_with_type(&privkey_ed);
+}
+
+
 int
 main (int argc, char *argv[])
 {
diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h
index 43cdfdfac..a334b50d0 100644
--- a/src/include/gnunet_crypto_lib.h
+++ b/src/include/gnunet_crypto_lib.h
@@ -274,6 +274,19 @@ struct GNUNET_CRYPTO_EddsaPrivateKey
 };
 
 
+/**
+ * Private ECC scalar encoded for transmission.  To be used only for EdDSA
+ * signatures.
+ */
+struct GNUNET_CRYPTO_EddsaPrivateScalar
+{
+  /**
+   * s is the expandedprivate 512-bit scalar of a private key.
+   */
+  unsigned char s[512 / 8];
+};
+
+
 /**
  * @brief type for session keys
  */
@@ -1906,6 +1919,71 @@ GNUNET_CRYPTO_ecdsa_public_key_derive (
   struct GNUNET_CRYPTO_EcdsaPublicKey *result);
 
 
+/**
+ * @ingroup crypto
+ * Derive a private scalar from a given private key and a label.
+ * Essentially calculates a private key 'h = H(l,P) * d mod n'
+ * where n is the size of the ECC group and P is the public
+ * key associated with the private key 'd'.
+ * The result is the derived private _scalar_, not the private
+ * key as for EdDSA we cannot derive before we hash the
+ * private key.
+ *
+ * @param priv original private key
+ * @param label label to use for key deriviation
+ * @param context additional context to use for HKDF of 'h';
+ *        typically the name of the subsystem/application
+ * @param result derived private scalar
+ */
+void
+GNUNET_CRYPTO_eddsa_private_key_derive (
+  const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
+  const char *label,
+  const char *context,
+  struct GNUNET_CRYPTO_EddsaPrivateScalar *result);
+
+
+/**
+ * @ingroup crypto
+ * Derive a public key from a given public key and a label.
+ * Essentially calculates a public key 'V = H(l,P) * P'.
+ *
+ * @param pub original public key
+ * @param label label to use for key deriviation
+ * @param context additional context to use for HKDF of 'h'.
+ *        typically the name of the subsystem/application
+ * @param result where to write the derived public key
+ */
+void
+GNUNET_CRYPTO_eddsa_public_key_derive (
+  const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
+  const char *label,
+  const char *context,
+  struct GNUNET_CRYPTO_EddsaPublicKey *result);
+
+
+/**
+ * This is a signature function for EdDSA which takes the
+ * secret scalar sk instead of the private seed which is
+ * usually the case for crypto APIs. We require this functionality
+ * in order to use derived private keys for signatures we
+ * cannot calculate the inverse of a sk to find the seed
+ * efficiently.
+ *
+ * The resulting signature is a standard EdDSA signature
+ * which can be verified using the usual APIs.
+ *
+ * @param sk the secret scalar
+ * @param purp the signature purpose
+ * @param sig the resulting signature
+ */
+void
+GNUNET_CRYPTO_eddsa_sign_with_scalar (
+  const struct GNUNET_CRYPTO_EddsaPrivateScalar *priv,
+  const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+  struct GNUNET_CRYPTO_EddsaSignature *sig);
+
+
 /**
  * Output the given MPI value to the given buffer in network
  * byte order.  The MPI @a val may not be negative.
diff --git a/src/include/gnunet_gnsrecord_lib.h 
b/src/include/gnunet_gnsrecord_lib.h
index 61cbac2ca..5afb3f253 100644
--- a/src/include/gnunet_gnsrecord_lib.h
+++ b/src/include/gnunet_gnsrecord_lib.h
@@ -197,6 +197,37 @@ struct GNUNET_GNSRECORD_EcdsaBlock
   /* followed by encrypted data */
 };
 
+
+/**
+ * Information we have in an encrypted block with record data (i.e. in the 
DHT).
+ */
+struct GNUNET_GNSRECORD_EddsaBlock
+{
+  /**
+   * Derived key used for signing; hash of this is the query.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey derived_key;
+
+  /**
+   * Signature of the block.
+   */
+  struct GNUNET_CRYPTO_EddsaSignature signature;
+
+  /**
+   * Number of bytes signed; also specifies the number of bytes
+   * of encrypted data that follow.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Expiration time of the block.
+   */
+  struct GNUNET_TIME_AbsoluteNBO expiration_time;
+
+  /* followed by encrypted data */
+};
+
+
 struct GNUNET_GNSRECORD_Block
 {
   uint32_t type;
@@ -204,7 +235,7 @@ struct GNUNET_GNSRECORD_Block
   union
   {
     struct GNUNET_GNSRECORD_EcdsaBlock ecdsa_block;
-    //struct GNUNET_GNSRECORD_EddsaBlock eddsa_block;
+    struct GNUNET_GNSRECORD_EddsaBlock eddsa_block;
   };
 };
 
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 72fd37bde..09620aee0 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -61,6 +61,7 @@ libgnunetutil_la_SOURCES = \
   crypto_symmetric.c \
   crypto_crc.c \
   crypto_ecc.c \
+  crypto_ecc_gnsrecord.c \
   $(DLOG) \
   crypto_ecc_setup.c \
   crypto_hash.c \
diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c
index efbf2ee17..5b1b579ec 100644
--- a/src/util/crypto_ecc.c
+++ b/src/util/crypto_ecc.c
@@ -714,135 +714,6 @@ GNUNET_CRYPTO_ecc_ecdh (const struct 
GNUNET_CRYPTO_EcdhePrivateKey *priv,
 }
 
 
-/**
- * Derive the 'h' value for key derivation, where
- * 'h = H(l,P)'.
- *
- * @param pub public key for deriviation
- * @param label label for deriviation
- * @param context additional context to use for HKDF of 'h';
- *        typically the name of the subsystem/application
- * @return h value
- */
-static gcry_mpi_t
-derive_h (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
-          const char *label,
-          const char *context)
-{
-  gcry_mpi_t h;
-  struct GNUNET_HashCode hc;
-  static const char *const salt = "key-derivation";
-
-  GNUNET_CRYPTO_kdf (&hc,
-                     sizeof(hc),
-                     salt,
-                     strlen (salt),
-                     pub,
-                     sizeof(*pub),
-                     label,
-                     strlen (label),
-                     context,
-                     strlen (context),
-                     NULL,
-                     0);
-  GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
-  return h;
-}
-
-
-struct GNUNET_CRYPTO_EcdsaPrivateKey *
-GNUNET_CRYPTO_ecdsa_private_key_derive (
-  const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
-  const char *label,
-  const char *context)
-{
-  struct GNUNET_CRYPTO_EcdsaPublicKey pub;
-  struct GNUNET_CRYPTO_EcdsaPrivateKey *ret;
-  uint8_t dc[32];
-  gcry_mpi_t h;
-  gcry_mpi_t x;
-  gcry_mpi_t d;
-  gcry_mpi_t n;
-  gcry_ctx_t ctx;
-
-  GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
-
-  n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
-  GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pub);
-
-  h = derive_h (&pub, label, context);
-  /* Convert to big endian for libgcrypt */
-  for (size_t i = 0; i < 32; i++)
-    dc[i] = priv->d[31 - i];
-  GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc));
-  d = gcry_mpi_new (256);
-  gcry_mpi_mulm (d, h, x, n);
-  gcry_mpi_release (h);
-  gcry_mpi_release (x);
-  gcry_mpi_release (n);
-  gcry_ctx_release (ctx);
-  ret = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
-  GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
-  /* Convert to big endian for libgcrypt */
-  for (size_t i = 0; i < 32; i++)
-    ret->d[i] = dc[31 - i];
-  sodium_memzero (dc, sizeof(dc));
-  gcry_mpi_release (d);
-  return ret;
-}
-
-
-void
-GNUNET_CRYPTO_ecdsa_public_key_derive (
-  const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
-  const char *label,
-  const char *context,
-  struct GNUNET_CRYPTO_EcdsaPublicKey *result)
-{
-  gcry_ctx_t ctx;
-  gcry_mpi_t q_y;
-  gcry_mpi_t h;
-  gcry_mpi_t n;
-  gcry_mpi_t h_mod_n;
-  gcry_mpi_point_t q;
-  gcry_mpi_point_t v;
-
-  GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
-
-  /* obtain point 'q' from original public key.  The provided 'q' is
-     compressed thus we first store it in the context and then get it
-     back as a (decompresssed) point.  */
-  q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8 * sizeof(pub->q_y));
-  GNUNET_assert (NULL != q_y);
-  GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
-  gcry_mpi_release (q_y);
-  q = gcry_mpi_ec_get_point ("q", ctx, 0);
-  GNUNET_assert (q);
-
-  /* calculate h_mod_n = h % n */
-  h = derive_h (pub, label, context);
-  n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
-  h_mod_n = gcry_mpi_new (256);
-  gcry_mpi_mod (h_mod_n, h, n);
-  /* calculate v = h_mod_n * q */
-  v = gcry_mpi_point_new (0);
-  gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
-  gcry_mpi_release (h_mod_n);
-  gcry_mpi_release (h);
-  gcry_mpi_release (n);
-  gcry_mpi_point_release (q);
-
-  /* convert point 'v' to public key that we return */
-  GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
-  gcry_mpi_point_release (v);
-  q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
-  GNUNET_assert (q_y);
-  GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
-  gcry_mpi_release (q_y);
-  gcry_ctx_release (ctx);
-}
-
-
 enum GNUNET_GenericReturnValue
 GNUNET_CRYPTO_eddsa_ecdh (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
                           const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
diff --git a/src/util/crypto_ecc_gnsrecord.c b/src/util/crypto_ecc_gnsrecord.c
new file mode 100644
index 000000000..6689a21f1
--- /dev/null
+++ b/src/util/crypto_ecc_gnsrecord.c
@@ -0,0 +1,351 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013, 2015 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file util/crypto_ecc_gnsrecord.c
+ * @brief public key cryptography (ECC) for GNS records (LSD0001)
+ * @author Christian Grothoff
+ * @author Florian Dold
+ * @author Martin Schanzenbach
+ */
+#include "platform.h"
+#include <gcrypt.h>
+#include <sodium.h>
+#include "gnunet_crypto_lib.h"
+#include "gnunet_strings_lib.h"
+
+#define CURVE "Ed25519"
+
+/**
+ * Derive the 'h' value for key derivation, where
+ * 'h = H(l,P)'.
+ *
+ * @param pub public key for deriviation
+ * @param pubsize the size of the public key
+ * @param label label for deriviation
+ * @param context additional context to use for HKDF of 'h';
+ *        typically the name of the subsystem/application
+ * @return h value
+ */
+static gcry_mpi_t
+derive_h (const void *pub,
+          size_t pubsize,
+          const char *label,
+          const char *context)
+{
+  gcry_mpi_t h;
+  struct GNUNET_HashCode hc;
+  static const char *const salt = "key-derivation";
+
+  GNUNET_CRYPTO_kdf (&hc,
+                     sizeof(hc),
+                     salt,
+                     strlen (salt),
+                     pub,
+                     pubsize,
+                     label,
+                     strlen (label),
+                     context,
+                     strlen (context),
+                     NULL,
+                     0);
+  GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
+  return h;
+}
+
+
+/**
+ * This is a signature function for EdDSA which takes the
+ * secret scalar sk instead of the private seed which is
+ * usually the case for crypto APIs. We require this functionality
+ * in order to use derived private keys for signatures we
+ * cannot calculate the inverse of a sk to find the seed
+ * efficiently.
+ *
+ * The resulting signature is a standard EdDSA signature
+ * which can be verified using the usual APIs.
+ *
+ * @param sk the secret scalar
+ * @param purp the signature purpose
+ * @param sig the resulting signature
+ */
+void
+GNUNET_CRYPTO_eddsa_sign_with_scalar (
+  const struct GNUNET_CRYPTO_EddsaPrivateScalar *priv,
+  const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+  struct GNUNET_CRYPTO_EddsaSignature *sig)
+{
+
+  crypto_hash_sha512_state hs;
+  unsigned char az[64];
+  unsigned char nonce[64];
+  unsigned char hram[64];
+  unsigned char R[32];
+  unsigned char pk[32];
+
+  crypto_hash_sha512_init (&hs);
+
+  // crypto_hash_sha512 (az, sk, 32); DO NOT EXPAND, WE HAVE A KEY
+  memcpy (az, priv->s, 64);
+  crypto_scalarmult_ed25519_base_noclamp (pk,
+                                          priv->s);
+  crypto_hash_sha512_update (&hs, az + 32, 32);
+
+  crypto_hash_sha512_update (&hs, (uint8_t*) purpose, ntohl (purpose->size));
+  crypto_hash_sha512_final (&hs, nonce);
+
+  // This effectively creates R || A in sig
+  memcpy (sig->s, pk, 32);
+
+  unsigned char nonce_mod[64];
+  crypto_core_ed25519_scalar_reduce (nonce_mod, nonce);
+  // nonce == r; r * G == R
+  crypto_scalarmult_ed25519_base_noclamp (R, nonce_mod);
+  memcpy (sig->r, R, sizeof (R));
+
+  // SHA512 (R | A | M) == k
+  crypto_hash_sha512_init (&hs);
+  crypto_hash_sha512_update (&hs, (uint8_t*) sig, 64);
+  crypto_hash_sha512_update (&hs, (uint8_t*) purpose,
+                             ntohl (purpose->size));
+  crypto_hash_sha512_final (&hs, hram);
+
+  unsigned char hram_mod[64];
+  crypto_core_ed25519_scalar_reduce (hram_mod, hram);
+  az[0] &= 248;
+  az[31] &= 127;
+  az[31] |= 64;
+
+  unsigned char tmp[32];
+  // r + k * s mod L == S
+  crypto_core_ed25519_scalar_mul (tmp, hram_mod, az);
+  crypto_core_ed25519_scalar_add (sig->s, tmp, nonce_mod);
+
+  sodium_memzero (az, sizeof az);
+  sodium_memzero (nonce, sizeof nonce);
+}
+
+
+struct GNUNET_CRYPTO_EcdsaPrivateKey *
+GNUNET_CRYPTO_ecdsa_private_key_derive (
+  const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
+  const char *label,
+  const char *context)
+{
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub;
+  struct GNUNET_CRYPTO_EcdsaPrivateKey *ret;
+  uint8_t dc[32];
+  gcry_mpi_t h;
+  gcry_mpi_t x;
+  gcry_mpi_t d;
+  gcry_mpi_t n;
+  gcry_ctx_t ctx;
+
+  GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
+
+  n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
+  GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pub);
+
+  h = derive_h (&pub, sizeof (pub), label, context);
+  /* Convert to big endian for libgcrypt */
+  for (size_t i = 0; i < 32; i++)
+    dc[i] = priv->d[31 - i];
+  GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc));
+  d = gcry_mpi_new (256);
+  gcry_mpi_mulm (d, h, x, n);
+  gcry_mpi_release (h);
+  gcry_mpi_release (x);
+  gcry_mpi_release (n);
+  gcry_ctx_release (ctx);
+  ret = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
+  GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
+  /* Convert to big endian for libgcrypt */
+  for (size_t i = 0; i < 32; i++)
+    ret->d[i] = dc[31 - i];
+  sodium_memzero (dc, sizeof(dc));
+  gcry_mpi_release (d);
+  return ret;
+}
+
+
+void
+GNUNET_CRYPTO_ecdsa_public_key_derive (
+  const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
+  const char *label,
+  const char *context,
+  struct GNUNET_CRYPTO_EcdsaPublicKey *result)
+{
+  gcry_ctx_t ctx;
+  gcry_mpi_t q_y;
+  gcry_mpi_t h;
+  gcry_mpi_t n;
+  gcry_mpi_t h_mod_n;
+  gcry_mpi_point_t q;
+  gcry_mpi_point_t v;
+
+  GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
+
+  /* obtain point 'q' from original public key.  The provided 'q' is
+     compressed thus we first store it in the context and then get it
+     back as a (decompresssed) point.  */
+  q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8 * sizeof(pub->q_y));
+  GNUNET_assert (NULL != q_y);
+  GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
+  gcry_mpi_release (q_y);
+  q = gcry_mpi_ec_get_point ("q", ctx, 0);
+  GNUNET_assert (q);
+
+  /* calculate h_mod_n = h % n */
+  h = derive_h (pub, sizeof (pub), label, context);
+  n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
+  h_mod_n = gcry_mpi_new (256);
+  gcry_mpi_mod (h_mod_n, h, n);
+  /* calculate v = h_mod_n * q */
+  v = gcry_mpi_point_new (0);
+  gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
+  gcry_mpi_release (h_mod_n);
+  gcry_mpi_release (h);
+  gcry_mpi_release (n);
+  gcry_mpi_point_release (q);
+
+  /* convert point 'v' to public key that we return */
+  GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
+  gcry_mpi_point_release (v);
+  q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
+  GNUNET_assert (q_y);
+  GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
+  gcry_mpi_release (q_y);
+  gcry_ctx_release (ctx);
+}
+
+
+void
+GNUNET_CRYPTO_eddsa_private_key_derive (
+  const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
+  const char *label,
+  const char *context,
+  struct GNUNET_CRYPTO_EddsaPrivateScalar *result)
+{
+  struct GNUNET_CRYPTO_EddsaPublicKey pub;
+  uint8_t dc[32];
+  unsigned char sk[64];
+  gcry_mpi_t h;
+  gcry_mpi_t h_mod_n;
+  gcry_mpi_t x;
+  gcry_mpi_t d;
+  gcry_mpi_t n;
+  gcry_mpi_t a1;
+  gcry_mpi_t a2;
+  gcry_ctx_t ctx;
+
+  GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
+
+  n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
+  GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub);
+  crypto_hash_sha512 (sk, priv->d, 32);
+  sk[0] &= 248;
+  sk[31] &= 127;
+  sk[31] |= 64;
+  h = derive_h (&pub, sizeof (pub), label, context);
+  h_mod_n = gcry_mpi_new (256);
+  gcry_mpi_mod (h_mod_n, h, n);
+  /* Convert to big endian for libgcrypt */
+  for (size_t i = 0; i < 32; i++)
+    dc[i] = sk[31 - i];
+  GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc)); // a
+  a1 = gcry_mpi_new (256);
+  gcry_mpi_t eight = gcry_mpi_set_ui (NULL, 8);
+  gcry_mpi_div (a1, NULL, x, eight, 0); // a1 := a / 8
+  a2 = gcry_mpi_new (256);
+  gcry_mpi_mulm (a2, h_mod_n, a1, n); // a2 := h * a1 mod n
+  d = gcry_mpi_new (256);
+  // gcry_mpi_mulm (d, a2, eight, n); // a' := a2 * 8 mod n
+  gcry_mpi_mul (d, a2, eight); // a' := a2 * 8
+  gcry_mpi_release (h);
+  gcry_mpi_release (x);
+  gcry_mpi_release (n);
+  gcry_mpi_release (a1);
+  gcry_mpi_release (a2);
+  gcry_ctx_release (ctx);
+  GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
+  memcpy (result->s, sk, sizeof (sk));
+  /* Convert to little endian for libsodium */
+  for (size_t i = 0; i < 32; i++)
+    result->s[i] = dc[31 - i];
+  result->s[0] &= 248;
+  result->s[31] &= 127;
+  result->s[31] |= 64;
+
+  sodium_memzero (dc, sizeof(dc));
+  gcry_mpi_release (d);
+}
+
+
+void
+GNUNET_CRYPTO_eddsa_public_key_derive (
+  const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
+  const char *label,
+  const char *context,
+  struct GNUNET_CRYPTO_EddsaPublicKey *result)
+{
+  gcry_ctx_t ctx;
+  gcry_mpi_t q_y;
+  gcry_mpi_t h;
+  gcry_mpi_t n;
+  gcry_mpi_t h_mod_n;
+  gcry_mpi_point_t q;
+  gcry_mpi_point_t v;
+
+  GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
+
+  /* obtain point 'q' from original public key.  The provided 'q' is
+     compressed thus we first store it in the context and then get it
+     back as a (decompresssed) point.  */
+  q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8 * sizeof(pub->q_y));
+  GNUNET_assert (NULL != q_y);
+  GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
+  gcry_mpi_release (q_y);
+  q = gcry_mpi_ec_get_point ("q", ctx, 0);
+  GNUNET_assert (q);
+
+  /* calculate h_mod_n = h % n */
+  h = derive_h (pub, sizeof (*pub), label, context);
+  n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
+  h_mod_n = gcry_mpi_new (256);
+  gcry_mpi_mod (h_mod_n, h, n);
+
+  /* calculate v = h_mod_n * q */
+  v = gcry_mpi_point_new (0);
+  gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
+  gcry_mpi_release (h_mod_n);
+  gcry_mpi_release (h);
+  gcry_mpi_release (n);
+  gcry_mpi_point_release (q);
+
+  /* convert point 'v' to public key that we return */
+  GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
+  gcry_mpi_point_release (v);
+  q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
+  GNUNET_assert (q_y);
+  GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
+  gcry_mpi_release (q_y);
+  gcry_ctx_release (ctx);
+
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]