aboutsummaryrefslogtreecommitdiff
path: root/src/util/crypto_edx25519.c
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/util/crypto_edx25519.c
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/util/crypto_edx25519.c')
-rw-r--r--src/util/crypto_edx25519.c423
1 files changed, 423 insertions, 0 deletions
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}