aboutsummaryrefslogtreecommitdiff
path: root/src/util/crypto_ecc_gnsrecord.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/crypto_ecc_gnsrecord.c')
-rw-r--r--src/util/crypto_ecc_gnsrecord.c351
1 files changed, 351 insertions, 0 deletions
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 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 2015 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_ecc_gnsrecord.c
23 * @brief public key cryptography (ECC) for GNS records (LSD0001)
24 * @author Christian Grothoff
25 * @author Florian Dold
26 * @author Martin Schanzenbach
27 */
28#include "platform.h"
29#include <gcrypt.h>
30#include <sodium.h>
31#include "gnunet_crypto_lib.h"
32#include "gnunet_strings_lib.h"
33
34#define CURVE "Ed25519"
35
36/**
37 * Derive the 'h' value for key derivation, where
38 * 'h = H(l,P)'.
39 *
40 * @param pub public key for deriviation
41 * @param pubsize the size of the public key
42 * @param label label for deriviation
43 * @param context additional context to use for HKDF of 'h';
44 * typically the name of the subsystem/application
45 * @return h value
46 */
47static gcry_mpi_t
48derive_h (const void *pub,
49 size_t pubsize,
50 const char *label,
51 const char *context)
52{
53 gcry_mpi_t h;
54 struct GNUNET_HashCode hc;
55 static const char *const salt = "key-derivation";
56
57 GNUNET_CRYPTO_kdf (&hc,
58 sizeof(hc),
59 salt,
60 strlen (salt),
61 pub,
62 pubsize,
63 label,
64 strlen (label),
65 context,
66 strlen (context),
67 NULL,
68 0);
69 GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
70 return h;
71}
72
73
74/**
75 * This is a signature function for EdDSA which takes the
76 * secret scalar sk instead of the private seed which is
77 * usually the case for crypto APIs. We require this functionality
78 * in order to use derived private keys for signatures we
79 * cannot calculate the inverse of a sk to find the seed
80 * efficiently.
81 *
82 * The resulting signature is a standard EdDSA signature
83 * which can be verified using the usual APIs.
84 *
85 * @param sk the secret scalar
86 * @param purp the signature purpose
87 * @param sig the resulting signature
88 */
89void
90GNUNET_CRYPTO_eddsa_sign_with_scalar (
91 const struct GNUNET_CRYPTO_EddsaPrivateScalar *priv,
92 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
93 struct GNUNET_CRYPTO_EddsaSignature *sig)
94{
95
96 crypto_hash_sha512_state hs;
97 unsigned char az[64];
98 unsigned char nonce[64];
99 unsigned char hram[64];
100 unsigned char R[32];
101 unsigned char pk[32];
102
103 crypto_hash_sha512_init (&hs);
104
105 // crypto_hash_sha512 (az, sk, 32); DO NOT EXPAND, WE HAVE A KEY
106 memcpy (az, priv->s, 64);
107 crypto_scalarmult_ed25519_base_noclamp (pk,
108 priv->s);
109 crypto_hash_sha512_update (&hs, az + 32, 32);
110
111 crypto_hash_sha512_update (&hs, (uint8_t*) purpose, ntohl (purpose->size));
112 crypto_hash_sha512_final (&hs, nonce);
113
114 // This effectively creates R || A in sig
115 memcpy (sig->s, pk, 32);
116
117 unsigned char nonce_mod[64];
118 crypto_core_ed25519_scalar_reduce (nonce_mod, nonce);
119 // nonce == r; r * G == R
120 crypto_scalarmult_ed25519_base_noclamp (R, nonce_mod);
121 memcpy (sig->r, R, sizeof (R));
122
123 // SHA512 (R | A | M) == k
124 crypto_hash_sha512_init (&hs);
125 crypto_hash_sha512_update (&hs, (uint8_t*) sig, 64);
126 crypto_hash_sha512_update (&hs, (uint8_t*) purpose,
127 ntohl (purpose->size));
128 crypto_hash_sha512_final (&hs, hram);
129
130 unsigned char hram_mod[64];
131 crypto_core_ed25519_scalar_reduce (hram_mod, hram);
132 az[0] &= 248;
133 az[31] &= 127;
134 az[31] |= 64;
135
136 unsigned char tmp[32];
137 // r + k * s mod L == S
138 crypto_core_ed25519_scalar_mul (tmp, hram_mod, az);
139 crypto_core_ed25519_scalar_add (sig->s, tmp, nonce_mod);
140
141 sodium_memzero (az, sizeof az);
142 sodium_memzero (nonce, sizeof nonce);
143}
144
145
146struct GNUNET_CRYPTO_EcdsaPrivateKey *
147GNUNET_CRYPTO_ecdsa_private_key_derive (
148 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
149 const char *label,
150 const char *context)
151{
152 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
153 struct GNUNET_CRYPTO_EcdsaPrivateKey *ret;
154 uint8_t dc[32];
155 gcry_mpi_t h;
156 gcry_mpi_t x;
157 gcry_mpi_t d;
158 gcry_mpi_t n;
159 gcry_ctx_t ctx;
160
161 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
162
163 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
164 GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pub);
165
166 h = derive_h (&pub, sizeof (pub), label, context);
167 /* Convert to big endian for libgcrypt */
168 for (size_t i = 0; i < 32; i++)
169 dc[i] = priv->d[31 - i];
170 GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc));
171 d = gcry_mpi_new (256);
172 gcry_mpi_mulm (d, h, x, n);
173 gcry_mpi_release (h);
174 gcry_mpi_release (x);
175 gcry_mpi_release (n);
176 gcry_ctx_release (ctx);
177 ret = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
178 GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
179 /* Convert to big endian for libgcrypt */
180 for (size_t i = 0; i < 32; i++)
181 ret->d[i] = dc[31 - i];
182 sodium_memzero (dc, sizeof(dc));
183 gcry_mpi_release (d);
184 return ret;
185}
186
187
188void
189GNUNET_CRYPTO_ecdsa_public_key_derive (
190 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
191 const char *label,
192 const char *context,
193 struct GNUNET_CRYPTO_EcdsaPublicKey *result)
194{
195 gcry_ctx_t ctx;
196 gcry_mpi_t q_y;
197 gcry_mpi_t h;
198 gcry_mpi_t n;
199 gcry_mpi_t h_mod_n;
200 gcry_mpi_point_t q;
201 gcry_mpi_point_t v;
202
203 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
204
205 /* obtain point 'q' from original public key. The provided 'q' is
206 compressed thus we first store it in the context and then get it
207 back as a (decompresssed) point. */
208 q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8 * sizeof(pub->q_y));
209 GNUNET_assert (NULL != q_y);
210 GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
211 gcry_mpi_release (q_y);
212 q = gcry_mpi_ec_get_point ("q", ctx, 0);
213 GNUNET_assert (q);
214
215 /* calculate h_mod_n = h % n */
216 h = derive_h (pub, sizeof (pub), label, context);
217 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
218 h_mod_n = gcry_mpi_new (256);
219 gcry_mpi_mod (h_mod_n, h, n);
220 /* calculate v = h_mod_n * q */
221 v = gcry_mpi_point_new (0);
222 gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
223 gcry_mpi_release (h_mod_n);
224 gcry_mpi_release (h);
225 gcry_mpi_release (n);
226 gcry_mpi_point_release (q);
227
228 /* convert point 'v' to public key that we return */
229 GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
230 gcry_mpi_point_release (v);
231 q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
232 GNUNET_assert (q_y);
233 GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
234 gcry_mpi_release (q_y);
235 gcry_ctx_release (ctx);
236}
237
238
239void
240GNUNET_CRYPTO_eddsa_private_key_derive (
241 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
242 const char *label,
243 const char *context,
244 struct GNUNET_CRYPTO_EddsaPrivateScalar *result)
245{
246 struct GNUNET_CRYPTO_EddsaPublicKey pub;
247 uint8_t dc[32];
248 unsigned char sk[64];
249 gcry_mpi_t h;
250 gcry_mpi_t h_mod_n;
251 gcry_mpi_t x;
252 gcry_mpi_t d;
253 gcry_mpi_t n;
254 gcry_mpi_t a1;
255 gcry_mpi_t a2;
256 gcry_ctx_t ctx;
257
258 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
259
260 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
261 GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub);
262 crypto_hash_sha512 (sk, priv->d, 32);
263 sk[0] &= 248;
264 sk[31] &= 127;
265 sk[31] |= 64;
266 h = derive_h (&pub, sizeof (pub), label, context);
267 h_mod_n = gcry_mpi_new (256);
268 gcry_mpi_mod (h_mod_n, h, n);
269 /* Convert to big endian for libgcrypt */
270 for (size_t i = 0; i < 32; i++)
271 dc[i] = sk[31 - i];
272 GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc)); // a
273 a1 = gcry_mpi_new (256);
274 gcry_mpi_t eight = gcry_mpi_set_ui (NULL, 8);
275 gcry_mpi_div (a1, NULL, x, eight, 0); // a1 := a / 8
276 a2 = gcry_mpi_new (256);
277 gcry_mpi_mulm (a2, h_mod_n, a1, n); // a2 := h * a1 mod n
278 d = gcry_mpi_new (256);
279 // gcry_mpi_mulm (d, a2, eight, n); // a' := a2 * 8 mod n
280 gcry_mpi_mul (d, a2, eight); // a' := a2 * 8
281 gcry_mpi_release (h);
282 gcry_mpi_release (x);
283 gcry_mpi_release (n);
284 gcry_mpi_release (a1);
285 gcry_mpi_release (a2);
286 gcry_ctx_release (ctx);
287 GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
288 memcpy (result->s, sk, sizeof (sk));
289 /* Convert to little endian for libsodium */
290 for (size_t i = 0; i < 32; i++)
291 result->s[i] = dc[31 - i];
292 result->s[0] &= 248;
293 result->s[31] &= 127;
294 result->s[31] |= 64;
295
296 sodium_memzero (dc, sizeof(dc));
297 gcry_mpi_release (d);
298}
299
300
301void
302GNUNET_CRYPTO_eddsa_public_key_derive (
303 const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
304 const char *label,
305 const char *context,
306 struct GNUNET_CRYPTO_EddsaPublicKey *result)
307{
308 gcry_ctx_t ctx;
309 gcry_mpi_t q_y;
310 gcry_mpi_t h;
311 gcry_mpi_t n;
312 gcry_mpi_t h_mod_n;
313 gcry_mpi_point_t q;
314 gcry_mpi_point_t v;
315
316 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
317
318 /* obtain point 'q' from original public key. The provided 'q' is
319 compressed thus we first store it in the context and then get it
320 back as a (decompresssed) point. */
321 q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8 * sizeof(pub->q_y));
322 GNUNET_assert (NULL != q_y);
323 GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
324 gcry_mpi_release (q_y);
325 q = gcry_mpi_ec_get_point ("q", ctx, 0);
326 GNUNET_assert (q);
327
328 /* calculate h_mod_n = h % n */
329 h = derive_h (pub, sizeof (*pub), label, context);
330 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
331 h_mod_n = gcry_mpi_new (256);
332 gcry_mpi_mod (h_mod_n, h, n);
333
334 /* calculate v = h_mod_n * q */
335 v = gcry_mpi_point_new (0);
336 gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
337 gcry_mpi_release (h_mod_n);
338 gcry_mpi_release (h);
339 gcry_mpi_release (n);
340 gcry_mpi_point_release (q);
341
342 /* convert point 'v' to public key that we return */
343 GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
344 gcry_mpi_point_release (v);
345 q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
346 GNUNET_assert (q_y);
347 GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
348 gcry_mpi_release (q_y);
349 gcry_ctx_release (ctx);
350
351}