aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2022-03-27 17:12:52 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2022-03-27 17:12:52 +0200
commitce38d1f6c9bd7857a1c3bc2094a0ee9752b86c32 (patch)
tree984ac3c3018e218acd74c5866a3df2169bfd0557 /src
parent1e4d6256731d69f1309ff8439569c65d2e1384a0 (diff)
downloadgnunet-ce38d1f6c9bd7857a1c3bc2094a0ee9752b86c32.tar.gz
gnunet-ce38d1f6c9bd7857a1c3bc2094a0ee9752b86c32.zip
Edx25519 implemented
Edx25519 is a variant of EdDSA on curve25519 which allows for repeated derivation of private and public keys, independently. The private keys in Edx25519 initially correspond to the data after expansion and clamping in EdDSA. However, this correspondence is lost after deriving further keys from existing ones. The public keys and signature verification are compatible with EdDSA. The ability to repeatedly derive key material is used for example in the context of age restriction in GNU Taler. The scheme that has been implemented is as follows: /* Private keys in Edx25519 are pairs (a, b) of 32 byte each. * Initially they correspond to the result of the expansion * and clamping in EdDSA. */ Edx25519_generate_private(seed) { /* EdDSA expand and clamp */ dh := SHA-512(seed) a := dh[0..31] b := dh[32..64] a[0] &= 0b11111000 a[31] &= 0b01111111 a[31] |= 0b01000000 return (a, b) } Edx25519_public_from_private(private) { /* Public keys are the same as in EdDSA */ (a, _) := private return [a] * G } Edx25519_blinding_factor(P, seed) { /* This is a helper function used in the derivation of * private/public keys from existing ones. */ h1 := HKDF_32(P, seed) /* Ensure that h == h % L */ h := h1 % L /* Optionally: Make sure that we don't create weak keys. */ P' := [h] * P if !( (h!=1) && (h!=0) && (P'!=E) ) { return Edx25519_blinding_factor(P, seed+1) } return h } Edx25519_derive_private(private, seed) { /* This is based on the definition in * GNUNET_CRYPTO_eddsa_private_key_derive. But it accepts * and returns a private pair (a, b) and allows for iteration. */ (a, b) := private P := Edx25519_public_key_from_private(private) h := Edx25519_blinding_factor(P, seed) /* Carefully calculate the new value for a */ a1 := a / 8; a2 := (h * a1) % L a' := (a2 * 8) % L /* Update b as well, binding it to h. This is an additional step compared to GNS. */ b' := SHA256(b ∥ h) return (a', b') } Edx25519_derive_public(P, seed) { h := Edx25519_blinding_factor(P, seed) return [h]*P } Edx25519_sign(private, message) { /* As in Ed25519, except for the origin of b */ (d, b) := private P := Edx25519_public_from_private(private) r := SHA-512(b ∥ message) R := [r] * G s := r + SHA-512(R ∥ P ∥ message) * d % L return (R,s) } Edx25519_verify(P, message, signature) { /* Identical to Ed25519 */ (R, s) := signature return [s] * G == R + [SHA-512(R ∥ P ∥ message)] * P }
Diffstat (limited to 'src')
-rw-r--r--src/include/gnunet_crypto_lib.h235
-rw-r--r--src/util/.gitignore1
-rw-r--r--src/util/Makefile.am8
-rw-r--r--src/util/crypto_edx25519.c423
-rw-r--r--src/util/test_crypto_edx25519.c326
5 files changed, 991 insertions, 2 deletions
diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h
index 77abab45d..582a58861 100644
--- a/src/include/gnunet_crypto_lib.h
+++ b/src/include/gnunet_crypto_lib.h
@@ -287,6 +287,59 @@ struct GNUNET_CRYPTO_EddsaPrivateScalar
287 unsigned char s[512 / 8]; 287 unsigned char s[512 / 8];
288}; 288};
289 289
290/**
291 * Private ECC key material encoded for transmission. To be used only for
292 * Edx25519 signatures. An inital key corresponds to data from the key
293 * expansion and clamping in the EdDSA key generation.
294 */
295struct GNUNET_CRYPTO_Edx25519PrivateKey
296{
297 /**
298 * a is a value mod n, where n has at most 256 bits. It is the first half of
299 * the seed-expansion of EdDSA and will be clamped.
300 */
301 unsigned char a[256 / 8];
302
303 /**
304 * b consists of 32 bytes which where originally the lower 32bytes of the key
305 * expansion. Subsequent calls to derive_private will change this value, too.
306 */
307 unsigned char b[256 / 8];
308};
309
310
311/**
312 * Public ECC key (always for curve Ed25519) encoded in a format suitable for
313 * network transmission and Edx25519 (same as EdDSA) signatures. Refer to
314 * section 5.1.3 of rfc8032, for a thorough explanation of how this value maps
315 * to the x- and y-coordinates.
316 */
317struct GNUNET_CRYPTO_Edx25519PublicKey
318{
319 /**
320 * Point Q consists of a y-value mod p (256 bits); the x-value is
321 * always positive. The point is stored in Ed25519 standard
322 * compact format.
323 */
324 unsigned char q_y[256 / 8];
325};
326
327/**
328 * @brief an ECC signature using Edx25519 (same as in EdDSA).
329 */
330struct GNUNET_CRYPTO_Edx25519Signature
331{
332 /**
333 * R value.
334 */
335 unsigned char r[256 / 8];
336
337 /**
338 * S value.
339 */
340 unsigned char s[256 / 8];
341};
342
290 343
291/** 344/**
292 * @brief type for session keys 345 * @brief type for session keys
@@ -1279,6 +1332,17 @@ GNUNET_CRYPTO_eddsa_key_get_public (
1279 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, 1332 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
1280 struct GNUNET_CRYPTO_EddsaPublicKey *pub); 1333 struct GNUNET_CRYPTO_EddsaPublicKey *pub);
1281 1334
1335/**
1336 * @ingroup crypto
1337 * Extract the public key for the given private key.
1338 *
1339 * @param priv the private key
1340 * @param pub where to write the public key
1341 */
1342void
1343GNUNET_CRYPTO_edx25519_key_get_public (
1344 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
1345 struct GNUNET_CRYPTO_Edx25519PublicKey *pub);
1282 1346
1283/** 1347/**
1284 * @ingroup crypto 1348 * @ingroup crypto
@@ -1465,6 +1529,30 @@ GNUNET_CRYPTO_eddsa_key_create (struct GNUNET_CRYPTO_EddsaPrivateKey *pk);
1465 1529
1466/** 1530/**
1467 * @ingroup crypto 1531 * @ingroup crypto
1532 * Create a new private key.
1533 *
1534 * @param[out] pk private key to initialize
1535 */
1536void
1537GNUNET_CRYPTO_edx25519_key_create (struct GNUNET_CRYPTO_Edx25519PrivateKey *pk);
1538
1539/**
1540 * @ingroup crypto
1541 * Create a new private key for Edx25519 from a given seed. After expanding
1542 * the seed, the first half of the key will be clamped according to EdDSA.
1543 *
1544 * @param seed seed input
1545 * @param seedsize size of the seed in bytes
1546 * @param[out] pk private key to initialize
1547 */
1548void
1549GNUNET_CRYPTO_edx25519_key_create_from_seed (
1550 const void *seed,
1551 size_t seedsize,
1552 struct GNUNET_CRYPTO_Edx25519PrivateKey *pk);
1553
1554/**
1555 * @ingroup crypto
1468 * Create a new private key. Clear with #GNUNET_CRYPTO_ecdhe_key_clear(). 1556 * Create a new private key. Clear with #GNUNET_CRYPTO_ecdhe_key_clear().
1469 * 1557 *
1470 * @param[out] pk set to fresh private key; 1558 * @param[out] pk set to fresh private key;
@@ -1492,6 +1580,14 @@ GNUNET_CRYPTO_eddsa_key_clear (struct GNUNET_CRYPTO_EddsaPrivateKey *pk);
1492void 1580void
1493GNUNET_CRYPTO_ecdsa_key_clear (struct GNUNET_CRYPTO_EcdsaPrivateKey *pk); 1581GNUNET_CRYPTO_ecdsa_key_clear (struct GNUNET_CRYPTO_EcdsaPrivateKey *pk);
1494 1582
1583/**
1584 * @ingroup crypto
1585 * Clear memory that was used to store a private key.
1586 *
1587 * @param pk location of the key
1588 */
1589void
1590GNUNET_CRYPTO_edx25519_key_clear (struct GNUNET_CRYPTO_Edx25519PrivateKey *pk);
1495 1591
1496/** 1592/**
1497 * @ingroup crypto 1593 * @ingroup crypto
@@ -1874,6 +1970,53 @@ GNUNET_CRYPTO_ecdsa_sign_ (
1874 sig)); \ 1970 sig)); \
1875} while (0) 1971} while (0)
1876 1972
1973/**
1974 * @ingroup crypto
1975 * @brief Edx25519 sign a given block.
1976 *
1977 * The @a purpose data is the beginning of the data of which the signature is
1978 * to be created. The `size` field in @a purpose must correctly indicate the
1979 * number of bytes of the data structure, including its header. If possible,
1980 * use #GNUNET_CRYPTO_edx25519_sign() instead of this function (only if @a
1981 * validate is not fixed-size, you must use this function directly).
1982 *
1983 * @param priv private key to use for the signing
1984 * @param purpose what to sign (size, purpose)
1985 * @param[out] sig where to write the signature
1986 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
1987 */
1988enum GNUNET_GenericReturnValue
1989GNUNET_CRYPTO_edx25519_sign_ (
1990 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
1991 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
1992 struct GNUNET_CRYPTO_Edx25519Signature *sig);
1993
1994
1995/**
1996 * @ingroup crypto
1997 * @brief Edx25519 sign a given block. The resulting signature is compatible
1998 * with EdDSA.
1999 *
2000 * The @a ps data must be a fixed-size struct for which the signature is to be
2001 * created. The `size` field in @a ps->purpose must correctly indicate the
2002 * number of bytes of the data structure, including its header.
2003 *
2004 * @param priv private key to use for the signing
2005 * @param ps packed struct with what to sign, MUST begin with a purpose
2006 * @param[out] sig where to write the signature
2007 */
2008#define GNUNET_CRYPTO_edx25519_sign(priv,ps,sig) do { \
2009 /* check size is set correctly */ \
2010 GNUNET_assert (ntohl ((ps)->purpose.size) == sizeof (*(ps))); \
2011 /* check 'ps' begins with the purpose */ \
2012 GNUNET_static_assert (((void*) (ps)) == \
2013 ((void*) &(ps)->purpose)); \
2014 GNUNET_assert (GNUNET_OK == \
2015 GNUNET_CRYPTO_edx25519_sign_ (priv, \
2016 &(ps)->purpose, \
2017 sig)); \
2018} while (0)
2019
1877 2020
1878/** 2021/**
1879 * @ingroup crypto 2022 * @ingroup crypto
@@ -1917,7 +2060,7 @@ GNUNET_CRYPTO_eddsa_verify_ (
1917 */ 2060 */
1918#define GNUNET_CRYPTO_eddsa_verify(purp,ps,sig,pub) ({ \ 2061#define GNUNET_CRYPTO_eddsa_verify(purp,ps,sig,pub) ({ \
1919 /* check size is set correctly */ \ 2062 /* check size is set correctly */ \
1920 GNUNET_assert (ntohl ((ps)->purpose.size) == sizeof (*(ps))); \ 2063 GNUNET_assert (ntohl ((ps)->purpose.size) == sizeof (*(ps))); \
1921 /* check 'ps' begins with the purpose */ \ 2064 /* check 'ps' begins with the purpose */ \
1922 GNUNET_static_assert (((void*) (ps)) == \ 2065 GNUNET_static_assert (((void*) (ps)) == \
1923 ((void*) &(ps)->purpose)); \ 2066 ((void*) &(ps)->purpose)); \
@@ -1927,7 +2070,6 @@ GNUNET_CRYPTO_eddsa_verify_ (
1927 pub); \ 2070 pub); \
1928 }) 2071 })
1929 2072
1930
1931/** 2073/**
1932 * @ingroup crypto 2074 * @ingroup crypto
1933 * @brief Verify ECDSA signature. 2075 * @brief Verify ECDSA signature.
@@ -1982,6 +2124,58 @@ GNUNET_CRYPTO_ecdsa_verify_ (
1982 2124
1983/** 2125/**
1984 * @ingroup crypto 2126 * @ingroup crypto
2127 * @brief Verify Edx25519 signature.
2128 *
2129 * The @a validate data is the beginning of the data of which the signature
2130 * is to be verified. The `size` field in @a validate must correctly indicate
2131 * the number of bytes of the data structure, including its header. If @a
2132 * purpose does not match the purpose given in @a validate (the latter must be
2133 * in big endian), signature verification fails. If possible, use
2134 * #GNUNET_CRYPTO_edx25519_verify() instead of this function (only if @a
2135 * validate is not fixed-size, you must use this function directly).
2136 *
2137 * @param purpose what is the purpose that the signature should have?
2138 * @param validate block to validate (size, purpose, data)
2139 * @param sig signature that is being validated
2140 * @param pub public key of the signer
2141 * @returns #GNUNET_OK if ok, #GNUNET_SYSERR if invalid
2142 */
2143enum GNUNET_GenericReturnValue
2144GNUNET_CRYPTO_edx25519_verify_ (
2145 uint32_t purpose,
2146 const struct GNUNET_CRYPTO_EccSignaturePurpose *validate,
2147 const struct GNUNET_CRYPTO_Edx25519Signature *sig,
2148 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub);
2149
2150
2151/**
2152 * @ingroup crypto
2153 * @brief Verify Edx25519 signature.
2154 *
2155 * The @a ps data must be a fixed-size struct for which the signature is to be
2156 * created. The `size` field in @a ps->purpose must correctly indicate the
2157 * number of bytes of the data structure, including its header.
2158 *
2159 * @param purp purpose of the signature, must match 'ps->purpose.purpose'
2160 * (except in host byte order)
2161 * @param priv private key to use for the signing
2162 * @param ps packed struct with what to sign, MUST begin with a purpose
2163 * @param sig where to write the signature
2164 */
2165#define GNUNET_CRYPTO_edx25519_verify(purp,ps,sig,pub) ({ \
2166 /* check size is set correctly */ \
2167 GNUNET_assert (ntohl ((ps)->purpose.size) == sizeof (*(ps))); \
2168 /* check 'ps' begins with the purpose */ \
2169 GNUNET_static_assert (((void*) (ps)) == \
2170 ((void*) &(ps)->purpose)); \
2171 GNUNET_CRYPTO_edx25519_verify_ (purp, \
2172 &(ps)->purpose, \
2173 sig, \
2174 pub); \
2175 })
2176
2177/**
2178 * @ingroup crypto
1985 * Derive a private key from a given private key and a label. 2179 * Derive a private key from a given private key and a label.
1986 * Essentially calculates a private key 'h = H(l,P) * d mod n' 2180 * Essentially calculates a private key 'h = H(l,P) * d mod n'
1987 * where n is the size of the ECC group and P is the public 2181 * where n is the size of the ECC group and P is the public
@@ -2115,6 +2309,43 @@ GNUNET_CRYPTO_eddsa_key_get_public_from_scalar (
2115 const struct GNUNET_CRYPTO_EddsaPrivateScalar *s, 2309 const struct GNUNET_CRYPTO_EddsaPrivateScalar *s,
2116 struct GNUNET_CRYPTO_EddsaPublicKey *pkey); 2310 struct GNUNET_CRYPTO_EddsaPublicKey *pkey);
2117 2311
2312/**
2313 * @ingroup crypto
2314 * Derive a private scalar from a given private key and a label.
2315 * Essentially calculates a private key 'h = H(l,P) * d mod n'
2316 * where n is the size of the ECC group and P is the public
2317 * key associated with the private key 'd'.
2318 *
2319 * @param priv original private key
2320 * @param seed input seed
2321 * @param seedsize size of the seed
2322 * @param result derived private key
2323 */
2324void
2325GNUNET_CRYPTO_edx25519_private_key_derive (
2326 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
2327 const void *seed,
2328 size_t seedsize,
2329 struct GNUNET_CRYPTO_Edx25519PrivateKey *result);
2330
2331
2332/**
2333 * @ingroup crypto
2334 * Derive a public key from a given public key and a label.
2335 * Essentially calculates a public key 'V = H(l,P) * P'.
2336 *
2337 * @param pub original public key
2338 * @param seed input seed
2339 * @param seedsize size of the seed
2340 * @param result where to write the derived public key
2341 */
2342void
2343GNUNET_CRYPTO_edx25519_public_key_derive (
2344 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub,
2345 const void *seed,
2346 size_t seedsize,
2347 struct GNUNET_CRYPTO_Edx25519PublicKey *result);
2348
2118 2349
2119/** 2350/**
2120 * Output the given MPI value to the given buffer in network 2351 * Output the given MPI value to the given buffer in network
diff --git a/src/util/.gitignore b/src/util/.gitignore
index 51eab71db..0e3449fed 100644
--- a/src/util/.gitignore
+++ b/src/util/.gitignore
@@ -36,6 +36,7 @@ test_crypto_ecdh_eddsa
36test_crypto_ecdhe 36test_crypto_ecdhe
37test_crypto_ecdsa 37test_crypto_ecdsa
38test_crypto_eddsa 38test_crypto_eddsa
39test_crypto_edx25519
39test_crypto_hash 40test_crypto_hash
40test_crypto_hash_context 41test_crypto_hash_context
41test_crypto_hkdf 42test_crypto_hkdf
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 406d42b1e..9cb7da15b 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -66,6 +66,7 @@ libgnunetutil_la_SOURCES = \
66 crypto_ecc_gnsrecord.c \ 66 crypto_ecc_gnsrecord.c \
67 $(DLOG) \ 67 $(DLOG) \
68 crypto_ecc_setup.c \ 68 crypto_ecc_setup.c \
69 crypto_edx25519.c \
69 crypto_hash.c \ 70 crypto_hash.c \
70 crypto_hash_file.c \ 71 crypto_hash_file.c \
71 crypto_hkdf.c \ 72 crypto_hkdf.c \
@@ -297,6 +298,7 @@ check_PROGRAMS = \
297 test_crypto_ecdhe \ 298 test_crypto_ecdhe \
298 test_crypto_ecdh_eddsa \ 299 test_crypto_ecdh_eddsa \
299 test_crypto_ecdh_ecdsa \ 300 test_crypto_ecdh_ecdsa \
301 test_crypto_edx25519 \
300 $(DLOG_TEST) \ 302 $(DLOG_TEST) \
301 test_crypto_hash \ 303 test_crypto_hash \
302 test_crypto_hash_context \ 304 test_crypto_hash_context \
@@ -470,6 +472,12 @@ test_crypto_eddsa_LDADD = \
470 libgnunetutil.la \ 472 libgnunetutil.la \
471 $(LIBGCRYPT_LIBS) 473 $(LIBGCRYPT_LIBS)
472 474
475test_crypto_edx25519_SOURCES = \
476 test_crypto_edx25519.c
477test_crypto_edx25519_LDADD = \
478 libgnunetutil.la \
479 $(LIBGCRYPT_LIBS)
480
473test_crypto_ecc_dlog_SOURCES = \ 481test_crypto_ecc_dlog_SOURCES = \
474 test_crypto_ecc_dlog.c 482 test_crypto_ecc_dlog.c
475test_crypto_ecc_dlog_LDADD = \ 483test_crypto_ecc_dlog_LDADD = \
diff --git a/src/util/crypto_edx25519.c b/src/util/crypto_edx25519.c
new file mode 100644
index 000000000..bb5c6d177
--- /dev/null
+++ b/src/util/crypto_edx25519.c
@@ -0,0 +1,423 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file util/crypto_edx25519.c
23 * @brief An variant of EdDSA which allows for iterative derivation of key pairs.
24 * @author Özgür Kesim
25 * @author Christian Grothoff
26 * @author Florian Dold
27 * @author Martin Schanzenbach
28 */
29#include "platform.h"
30#include <gcrypt.h>
31#include <sodium.h>
32#include "gnunet_crypto_lib.h"
33#include "gnunet_strings_lib.h"
34
35#define CURVE "Ed25519"
36
37void
38GNUNET_CRYPTO_edx25519_key_clear (struct GNUNET_CRYPTO_Edx25519PrivateKey *pk)
39{
40 memset (pk, 0, sizeof(struct GNUNET_CRYPTO_Edx25519PrivateKey));
41}
42
43
44void
45GNUNET_CRYPTO_edx25519_key_create_from_seed (
46 const void *seed,
47 size_t seedsize,
48 struct GNUNET_CRYPTO_Edx25519PrivateKey *pk)
49{
50
51 GNUNET_static_assert (sizeof(*pk) == sizeof(struct GNUNET_HashCode));
52 GNUNET_CRYPTO_hash (seed,
53 seedsize,
54 (struct GNUNET_HashCode *) pk);
55
56 /* Clamp the first half of the key. The second half is used in the signature
57 * process. */
58 pk->a[0] &= 248;
59 pk->a[31] &= 127;
60 pk->a[31] |= 64;
61}
62
63
64void
65GNUNET_CRYPTO_edx25519_key_create (
66 struct GNUNET_CRYPTO_Edx25519PrivateKey *pk)
67{
68 char seed[256 / 8];
69 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
70 seed,
71 sizeof (seed));
72 GNUNET_CRYPTO_edx25519_key_create_from_seed (seed,
73 sizeof(seed),
74 pk);
75}
76
77
78void
79GNUNET_CRYPTO_edx25519_key_get_public (
80 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
81 struct GNUNET_CRYPTO_Edx25519PublicKey *pub)
82{
83 crypto_scalarmult_ed25519_base_noclamp (pub->q_y,
84 priv->a);
85}
86
87
88/**
89 * This function operates the basically same way as the signature function for
90 * EdDSA. But instead of expanding a private seed (which is usually the case
91 * for crypto APIs) and using the resulting scalars, it takes the scalars
92 * directly from Edx25519PrivateKey. We require this functionality in order to
93 * use derived private keys for signatures.
94 *
95 * The resulting signature is a standard EdDSA signature
96 * which can be verified using the usual APIs.
97 *
98 * @param priv the private key (containing two scalars .a and .b)
99 * @param purp the signature purpose
100 * @param sig the resulting signature
101 */
102enum GNUNET_GenericReturnValue
103GNUNET_CRYPTO_edx25519_sign_ (
104 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
105 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
106 struct GNUNET_CRYPTO_Edx25519Signature *sig)
107{
108
109 crypto_hash_sha512_state hs;
110 unsigned char r[64];
111 unsigned char hram[64];
112 unsigned char P[32];
113 unsigned char R[32];
114 unsigned char tmp[32];
115
116 crypto_hash_sha512_init (&hs);
117
118 /**
119 * Calculate the public key P from the private scalar in the key.
120 */
121 crypto_scalarmult_ed25519_base_noclamp (P,
122 priv->a);
123
124 /**
125 * Calculate r:
126 * r = SHA512 (b ∥ M)
127 * where M is our message (purpose).
128 */
129 crypto_hash_sha512_update (&hs,
130 priv->b,
131 sizeof(priv->b));
132 crypto_hash_sha512_update (&hs,
133 (uint8_t*) purpose,
134 ntohl (purpose->size));
135 crypto_hash_sha512_final (&hs,
136 r);
137
138 /**
139 * Temporarily put P into S
140 */
141 memcpy (sig->s, P, 32);
142
143 /**
144 * Reduce the scalar value r
145 */
146 unsigned char r_mod[64];
147 crypto_core_ed25519_scalar_reduce (r_mod, r);
148
149 /**
150 * Calculate R := r * G of the signature
151 */
152 crypto_scalarmult_ed25519_base_noclamp (R, r_mod);
153 memcpy (sig->r, R, sizeof (R));
154
155 /**
156 * Calculate
157 * hram := SHA512 (R ∥ P ∥ M)
158 */
159 crypto_hash_sha512_init (&hs);
160 crypto_hash_sha512_update (&hs, (uint8_t*) sig, 64);
161 crypto_hash_sha512_update (&hs, (uint8_t*) purpose,
162 ntohl (purpose->size));
163 crypto_hash_sha512_final (&hs, hram);
164
165 /**
166 * Reduce the resulting scalar value
167 */
168 unsigned char hram_mod[64];
169 crypto_core_ed25519_scalar_reduce (hram_mod, hram);
170
171 /**
172 * Calculate
173 * S := r + hram * s mod L
174 */
175 crypto_core_ed25519_scalar_mul (tmp, hram_mod, priv->a);
176 crypto_core_ed25519_scalar_add (sig->s, tmp, r_mod);
177
178 sodium_memzero (r, sizeof (r));
179 sodium_memzero (r_mod, sizeof (r_mod));
180
181 return GNUNET_OK;
182}
183
184
185enum GNUNET_GenericReturnValue
186GNUNET_CRYPTO_edx25519_verify_ (
187 uint32_t purpose,
188 const struct GNUNET_CRYPTO_EccSignaturePurpose *validate,
189 const struct GNUNET_CRYPTO_Edx25519Signature *sig,
190 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub)
191{
192 const unsigned char *m = (const void *) validate;
193 size_t mlen = ntohl (validate->size);
194 const unsigned char *s = (const void *) sig;
195
196 int res;
197
198 if (purpose != ntohl (validate->purpose))
199 return GNUNET_SYSERR; /* purpose mismatch */
200
201 res = crypto_sign_verify_detached (s, m, mlen, pub->q_y);
202 return (res == 0) ? GNUNET_OK : GNUNET_SYSERR;
203}
204
205
206/**
207 * Derive the 'h' value for key derivation, where
208 * 'h = H(P ∥ seed) mod n' and 'n' is the size of the cyclic subroup.
209 *
210 * @param pub public key for deriviation
211 * @param seed seed for key the deriviation
212 * @param seedsize the size of the seed
213 * @param n The value for the modulus 'n'
214 * @param[out] phc if not NULL, the output of H() will be written into
215 * return h_mod_n (allocated by this function)
216 */
217static gcry_mpi_t
218derive_h_mod_n (
219 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub,
220 const void *seed,
221 size_t seedsize,
222 const gcry_mpi_t n,
223 struct GNUNET_HashCode *phc)
224{
225 static const char *const salt = "edx2559-derivation";
226 struct GNUNET_HashCode hc;
227 gcry_mpi_t h;
228 gcry_mpi_t h_mod_n;
229
230 if (NULL == phc)
231 phc = &hc;
232
233 GNUNET_CRYPTO_kdf (phc, sizeof(*phc),
234 salt, strlen (salt),
235 pub, sizeof(*pub),
236 seed, seedsize,
237 NULL, 0);
238
239 /* calculate h_mod_n = h % n */
240 GNUNET_CRYPTO_mpi_scan_unsigned (&h,
241 (unsigned char *) phc,
242 sizeof(*phc));
243 h_mod_n = gcry_mpi_new (256);
244 gcry_mpi_mod (h_mod_n, h, n);
245
246#ifdef CHECK_RARE_CASES
247 /**
248 * Note that the following cases would be problematic:
249 * 1.) h == 0 mod n
250 * 2.) h == 1 mod n
251 * 3.) [h] * P == E
252 * We assume that the probalities for these cases to occur are neglegible.
253 */
254 GNUNET_assert (! gcry_mpi_cmp_ui (h_mod_n, 0));
255 GNUNET_assert (! gcry_mpi_cmp_ui (h_mod_n, 1));
256#endif
257
258 return h_mod_n;
259}
260
261
262void
263GNUNET_CRYPTO_edx25519_private_key_derive (
264 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
265 const void *seed,
266 size_t seedsize,
267 struct GNUNET_CRYPTO_Edx25519PrivateKey *result)
268{
269 struct GNUNET_CRYPTO_Edx25519PublicKey pub;
270 struct GNUNET_HashCode hc;
271 uint8_t a[32];
272 unsigned char sk[64];
273 gcry_ctx_t ctx;
274 gcry_mpi_t h;
275 gcry_mpi_t h_mod_n;
276 gcry_mpi_t x;
277 gcry_mpi_t n;
278 gcry_mpi_t a1;
279 gcry_mpi_t a2;
280 gcry_mpi_t ap; // a'
281
282 GNUNET_CRYPTO_edx25519_key_get_public (priv, &pub);
283
284 /**
285 * Libsodium does not offer an API with arbitrary arithmetic.
286 * Hence we have to use libgcrypt here.
287 */
288 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
289
290 /**
291 * Get our modulo
292 */
293 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
294 GNUNET_assert (NULL != n);
295
296 /**
297 * Get h mod n
298 */
299 h_mod_n = derive_h_mod_n (&pub,
300 seed,
301 seedsize,
302 n,
303 &hc);
304
305 /* Convert priv->a scalar to big endian for libgcrypt */
306 for (size_t i = 0; i < 32; i++)
307 a[i] = priv->a[31 - i];
308
309 /**
310 * dc now contains the private scalar "a".
311 * We carefully remove the clamping and derive a'.
312 * Calculate:
313 * a1 := a / 8
314 * a2 := h * a1 mod n
315 * a' := a2 * 8 mod n
316 */
317 GNUNET_CRYPTO_mpi_scan_unsigned (&x, a, sizeof(a)); // a
318 a1 = gcry_mpi_new (256);
319 gcry_mpi_t eight = gcry_mpi_set_ui (NULL, 8);
320 gcry_mpi_div (a1, NULL, x, eight, 0); // a1 := a / 8
321 a2 = gcry_mpi_new (256);
322 gcry_mpi_mulm (a2, h_mod_n, a1, n); // a2 := h * a1 mod n
323 ap = gcry_mpi_new (256);
324 gcry_mpi_mul (ap, a2, eight); // a' := a2 * 8
325
326#ifdef CHECK_RARE_CASES
327 /* The likelihood for a' == 0 or a' == 1 is neglegible */
328 GNUNET_assert (! gcry_mpi_cmp_ui (ap, 0));
329 GNUNET_assert (! gcry_mpi_cmp_ui (ap, 1));
330#endif
331
332 gcry_mpi_release (h_mod_n);
333 gcry_mpi_release (h);
334 gcry_mpi_release (x);
335 gcry_mpi_release (n);
336 gcry_mpi_release (a1);
337 gcry_mpi_release (a2);
338 gcry_ctx_release (ctx);
339 GNUNET_CRYPTO_mpi_print_unsigned (a, sizeof(a), ap);
340 gcry_mpi_release (ap);
341
342 /**
343 * We hash the derived "h" parameter with the other half of the expanded
344 * private key (that is: priv->b). This ensures that for signature
345 * generation, the "R" is derived from the same derivation path as "h" and is
346 * not reused.
347 */
348 {
349 crypto_hash_sha256_state hs;
350 crypto_hash_sha256_init (&hs);
351 crypto_hash_sha256_update (&hs, priv->b, sizeof(priv->b));
352 crypto_hash_sha256_update (&hs, (unsigned char*) &hc, sizeof (hc));
353 crypto_hash_sha256_final (&hs, result->b);
354 }
355
356 /* Convert to little endian for libsodium */
357 for (size_t i = 0; i < 32; i++)
358 result->a[i] = a[31 - i];
359
360 sodium_memzero (a, sizeof(a));
361}
362
363
364void
365GNUNET_CRYPTO_edx25519_public_key_derive (
366 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub,
367 const void *seed,
368 size_t seedsize,
369 struct GNUNET_CRYPTO_Edx25519PublicKey *result)
370{
371 struct GNUNET_HashCode hc;
372 gcry_ctx_t ctx;
373 gcry_mpi_t q_y;
374 gcry_mpi_t h;
375 gcry_mpi_t n;
376 gcry_mpi_t h_mod_n;
377 gcry_mpi_point_t q;
378 gcry_mpi_point_t v;
379
380 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
381
382 /* obtain point 'q' from original public key. The provided 'q' is
383 compressed thus we first store it in the context and then get it
384 back as a (decompresssed) point. */
385 q_y = gcry_mpi_set_opaque_copy (NULL,
386 pub->q_y,
387 8 * sizeof(pub->q_y));
388 GNUNET_assert (NULL != q_y);
389 GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
390 gcry_mpi_release (q_y);
391 q = gcry_mpi_ec_get_point ("q", ctx, 0);
392 GNUNET_assert (q);
393
394 /**
395 * Get h mod n
396 */
397 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
398 GNUNET_assert (NULL != n);
399 GNUNET_assert (NULL != pub);
400 h_mod_n = derive_h_mod_n (pub,
401 seed,
402 seedsize,
403 n,
404 NULL /* We don't need hc here */);
405
406 /* calculate v = h_mod_n * q */
407 v = gcry_mpi_point_new (0);
408 gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
409 gcry_mpi_release (h_mod_n);
410 gcry_mpi_release (h);
411 gcry_mpi_release (n);
412 gcry_mpi_point_release (q);
413
414 /* convert point 'v' to public key that we return */
415 GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
416 gcry_mpi_point_release (v);
417 q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
418 GNUNET_assert (q_y);
419 GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
420 gcry_mpi_release (q_y);
421 gcry_ctx_release (ctx);
422
423}
diff --git a/src/util/test_crypto_edx25519.c b/src/util/test_crypto_edx25519.c
new file mode 100644
index 000000000..ead6f0bb9
--- /dev/null
+++ b/src/util/test_crypto_edx25519.c
@@ -0,0 +1,326 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19
20 */
21/**
22 * @file util/test_crypto_edx25519.c
23 * @brief testcase for ECC public key crypto for edx25519
24 * @author Özgür Kesim
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_signatures.h"
29#include <gcrypt.h>
30
31#define ITER 25
32
33#define KEYFILE "/tmp/test-gnunet-crypto-edx25519.key"
34
35#define PERF GNUNET_YES
36
37
38static struct GNUNET_CRYPTO_Edx25519PrivateKey key;
39
40
41static int
42testSignVerify (void)
43{
44 struct GNUNET_CRYPTO_Edx25519Signature sig;
45 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
46 struct GNUNET_CRYPTO_Edx25519PublicKey pkey;
47 struct GNUNET_TIME_Absolute start;
48 int ok = GNUNET_OK;
49
50 fprintf (stderr, "%s", "W");
51 GNUNET_CRYPTO_edx25519_key_get_public (&key,
52 &pkey);
53 start = GNUNET_TIME_absolute_get ();
54 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
55 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
56
57 for (unsigned int i = 0; i < ITER; i++)
58 {
59 fprintf (stderr, "%s", "."); fflush (stderr);
60 if (GNUNET_SYSERR == GNUNET_CRYPTO_edx25519_sign_ (&key,
61 &purp,
62 &sig))
63 {
64 fprintf (stderr,
65 "GNUNET_CRYPTO_edx25519_sign returned SYSERR\n");
66 ok = GNUNET_SYSERR;
67 continue;
68 }
69 if (GNUNET_SYSERR ==
70 GNUNET_CRYPTO_edx25519_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
71 &purp,
72 &sig,
73 &pkey))
74 {
75 fprintf (stderr,
76 "GNUNET_CRYPTO_edx25519_verify failed!\n");
77 ok = GNUNET_SYSERR;
78 continue;
79 }
80 if (GNUNET_SYSERR !=
81 GNUNET_CRYPTO_edx25519_verify_ (
82 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
83 &purp,
84 &sig,
85 &pkey))
86 {
87 fprintf (stderr,
88 "GNUNET_CRYPTO_edx25519_verify failed to fail!\n");
89 ok = GNUNET_SYSERR;
90 continue;
91 }
92 }
93 fprintf (stderr, "\n");
94 printf ("%d EdDSA sign/verify operations %s\n",
95 ITER,
96 GNUNET_STRINGS_relative_time_to_string (
97 GNUNET_TIME_absolute_get_duration (start),
98 GNUNET_YES));
99 return ok;
100}
101
102
103static int
104testDeriveSignVerify (void)
105{
106 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
107 struct GNUNET_CRYPTO_Edx25519Signature sig;
108 struct GNUNET_CRYPTO_Edx25519PrivateKey dkey;
109 struct GNUNET_CRYPTO_Edx25519PublicKey pub;
110 struct GNUNET_CRYPTO_Edx25519PublicKey dpub;
111 struct GNUNET_CRYPTO_Edx25519PublicKey dpub2;
112
113 GNUNET_CRYPTO_edx25519_key_get_public (&key, &pub);
114 GNUNET_CRYPTO_edx25519_private_key_derive (&key,
115 "test-derive",
116 sizeof("test-derive"),
117 &dkey);
118 GNUNET_CRYPTO_edx25519_public_key_derive (&pub,
119 "test-derive",
120 sizeof("test-derive"),
121 &dpub);
122 GNUNET_CRYPTO_edx25519_key_get_public (&dkey, &dpub2);
123
124 if (0 != GNUNET_memcmp (&dpub.q_y, &dpub2.q_y))
125 {
126 fprintf (stderr, "key deriviation failed\n");
127 return GNUNET_SYSERR;
128 }
129
130 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
131 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
132
133 GNUNET_CRYPTO_edx25519_sign_ (&dkey,
134 &purp,
135 &sig);
136
137 if (GNUNET_SYSERR ==
138 GNUNET_CRYPTO_edx25519_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
139 &purp,
140 &sig,
141 &dpub))
142 {
143 fprintf (stderr,
144 "GNUNET_CRYPTO_edx25519_verify failed after derivation!\n");
145 return GNUNET_SYSERR;
146 }
147
148 if (GNUNET_SYSERR !=
149 GNUNET_CRYPTO_edx25519_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
150 &purp,
151 &sig,
152 &pub))
153 {
154 fprintf (stderr,
155 "GNUNET_CRYPTO_edx25519_verify failed to fail after derivation!\n");
156 return GNUNET_SYSERR;
157 }
158
159 if (GNUNET_SYSERR !=
160 GNUNET_CRYPTO_edx25519_verify_ (
161 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
162 &purp,
163 &sig,
164 &dpub))
165 {
166 fprintf (stderr,
167 "GNUNET_CRYPTO_edx25519_verify failed to fail after derivation!\n");
168 return GNUNET_SYSERR;
169 }
170 return GNUNET_OK;
171}
172
173
174#if PERF
175static int
176testSignPerformance ()
177{
178 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
179 struct GNUNET_CRYPTO_Edx25519Signature sig;
180 struct GNUNET_CRYPTO_Edx25519PublicKey pkey;
181 struct GNUNET_TIME_Absolute start;
182 int ok = GNUNET_OK;
183
184 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
185 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
186 fprintf (stderr, "%s", "W");
187 GNUNET_CRYPTO_edx25519_key_get_public (&key,
188 &pkey);
189 start = GNUNET_TIME_absolute_get ();
190 for (unsigned int i = 0; i < ITER; i++)
191 {
192 fprintf (stderr, "%s", ".");
193 fflush (stderr);
194 if (GNUNET_SYSERR ==
195 GNUNET_CRYPTO_edx25519_sign_ (&key,
196 &purp,
197 &sig))
198 {
199 fprintf (stderr, "%s", "GNUNET_CRYPTO_edx25519_sign returned SYSERR\n");
200 ok = GNUNET_SYSERR;
201 continue;
202 }
203 }
204 fprintf (stderr, "\n");
205 printf ("%d EdDSA sign operations %s\n",
206 ITER,
207 GNUNET_STRINGS_relative_time_to_string (
208 GNUNET_TIME_absolute_get_duration (start),
209 GNUNET_YES));
210 return ok;
211}
212
213
214#endif
215
216
217#if 0 /* not implemented */
218static int
219testCreateFromFile (void)
220{
221 struct GNUNET_CRYPTO_Edx25519PublicKey p1;
222 struct GNUNET_CRYPTO_Edx25519PublicKey p2;
223
224 /* do_create == GNUNET_YES and non-existing file MUST return GNUNET_YES */
225 GNUNET_assert (0 == unlink (KEYFILE) || ENOENT == errno);
226 GNUNET_assert (GNUNET_YES ==
227 GNUNET_CRYPTO_edx25519_key_from_file (KEYFILE,
228 GNUNET_YES,
229 &key));
230 GNUNET_CRYPTO_edx25519_key_get_public (&key,
231 &p1);
232
233 /* do_create == GNUNET_YES and _existing_ file MUST return GNUNET_NO */
234 GNUNET_assert (GNUNET_NO ==
235 GNUNET_CRYPTO_edx25519_key_from_file (KEYFILE,
236 GNUNET_YES,
237 &key));
238 GNUNET_CRYPTO_edx25519_key_get_public (&key,
239 &p2);
240 GNUNET_assert (0 ==
241 GNUNET_memcmp (&p1,
242 &p2));
243
244 /* do_create == GNUNET_NO and non-existing file MUST return GNUNET_SYSERR */
245 GNUNET_assert (0 == unlink (KEYFILE));
246 GNUNET_assert (GNUNET_SYSERR ==
247 GNUNET_CRYPTO_edx25519_key_from_file (KEYFILE,
248 GNUNET_NO,
249 &key));
250 return GNUNET_OK;
251}
252
253
254#endif
255
256
257static void
258perf_keygen (void)
259{
260 struct GNUNET_TIME_Absolute start;
261 struct GNUNET_CRYPTO_Edx25519PrivateKey pk;
262
263 fprintf (stderr, "%s", "W");
264 start = GNUNET_TIME_absolute_get ();
265 for (unsigned int i = 0; i < 10; i++)
266 {
267 fprintf (stderr, ".");
268 fflush (stderr);
269 GNUNET_CRYPTO_edx25519_key_create (&pk);
270 }
271 fprintf (stderr, "\n");
272 printf ("10 EdDSA keys created in %s\n",
273 GNUNET_STRINGS_relative_time_to_string (
274 GNUNET_TIME_absolute_get_duration (start), GNUNET_YES));
275}
276
277
278int
279main (int argc, char *argv[])
280{
281 int failure_count = 0;
282
283 if (! gcry_check_version ("1.6.0"))
284 {
285 fprintf (stderr,
286 "libgcrypt has not the expected version (version %s is required).\n",
287 "1.6.0");
288 return 0;
289 }
290 if (getenv ("GNUNET_GCRYPT_DEBUG"))
291 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
292 GNUNET_log_setup ("test-crypto-edx25519",
293 "WARNING",
294 NULL);
295 GNUNET_CRYPTO_edx25519_key_create (&key);
296 if (GNUNET_OK != testDeriveSignVerify ())
297 {
298 failure_count++;
299 fprintf (stderr,
300 "\n\n%d TESTS FAILED!\n\n", failure_count);
301 return -1;
302 }
303#if PERF
304 if (GNUNET_OK != testSignPerformance ())
305 failure_count++;
306#endif
307 if (GNUNET_OK != testSignVerify ())
308 failure_count++;
309#if 0 /* not implemented */
310 if (GNUNET_OK != testCreateFromFile ())
311 failure_count++;
312#endif
313 perf_keygen ();
314
315 if (0 != failure_count)
316 {
317 fprintf (stderr,
318 "\n\n%d TESTS FAILED!\n\n",
319 failure_count);
320 return -1;
321 }
322 return 0;
323}
324
325
326/* end of test_crypto_edx25519.c */