aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorLucien Heuzeveldt <lucienclaude.heuzeveldt@students.bfh.ch>2021-12-06 22:13:35 +0100
committerLucien Heuzeveldt <lucienclaude.heuzeveldt@students.bfh.ch>2021-12-07 20:44:33 +0100
commit2bcdfc2f958f5fd2512103c2240f1c24c07524d7 (patch)
tree3e96bd0ccf4e21ca44e0e82beaf7f604213e38c4 /src/util
parentfa483f6cbce4873defe81234f44808bb8042fa68 (diff)
downloadgnunet-2bcdfc2f958f5fd2512103c2240f1c24c07524d7.tar.gz
gnunet-2bcdfc2f958f5fd2512103c2240f1c24c07524d7.zip
add crypto_cs implementation
Diffstat (limited to 'src/util')
-rw-r--r--src/util/Makefile.am1
-rw-r--r--src/util/crypto_cs.c425
2 files changed, 426 insertions, 0 deletions
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index d21ac5e86..771be1ee3 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -61,6 +61,7 @@ libgnunetutil_la_SOURCES = \
61 container_multihashmap32.c \ 61 container_multihashmap32.c \
62 crypto_symmetric.c \ 62 crypto_symmetric.c \
63 crypto_crc.c \ 63 crypto_crc.c \
64 crypto_cs.c \
64 crypto_ecc.c \ 65 crypto_ecc.c \
65 crypto_ecc_gnsrecord.c \ 66 crypto_ecc_gnsrecord.c \
66 $(DLOG) \ 67 $(DLOG) \
diff --git a/src/util/crypto_cs.c b/src/util/crypto_cs.c
new file mode 100644
index 000000000..5c441b669
--- /dev/null
+++ b/src/util/crypto_cs.c
@@ -0,0 +1,425 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014,2016,2019 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_cs.c
23 * @brief Clause Blind Schnorr signatures using Curve25519
24 * @author Lucien Heuzeveldt <lucienclaude.heuzeveldt@students.bfh.ch>
25 * @author Gian Demarmels <gian@demarmels.org>
26 */
27#include "platform.h"
28#include "gnunet_crypto_lib.h"
29#include <sodium.h>
30#include <gcrypt.h>
31
32/**
33 * IMPLEMENTATION NOTICE:
34 *
35 * This is an implementation of the Clause Blind Schnorr Signature Scheme using Curve25519.
36 * Further details about the Clause Blind Schnorr Signature Scheme can be found here:
37 * https://eprint.iacr.org/2019/877.pdf
38 *
39 * We use libsodium wherever possible.
40 */
41
42
43/**
44 * Create a new random private key.
45 *
46 * @param[out] priv where to write the fresh private key
47 */
48void
49GNUNET_CRYPTO_cs_private_key_generate (struct GNUNET_CRYPTO_CsPrivateKey *priv)
50{
51 crypto_core_ed25519_scalar_random (priv->scalar.d);
52}
53
54
55/**
56 * Extract the public key of the given private key.
57 *
58 * @param priv the private key
59 * @param[out] pub where to write the public key
60 */
61void
62GNUNET_CRYPTO_cs_private_key_get_public (const struct
63 GNUNET_CRYPTO_CsPrivateKey *priv,
64 struct GNUNET_CRYPTO_CsPublicKey *pub)
65{
66 GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (pub->point.y,
67 priv->scalar.d));
68}
69
70
71/**
72 * maps 32 random bytes to a scalar
73 * this is necessary because libsodium expects scalar to be in the prime order subgroup
74 * @param[out] scalar containing 32 byte char array, is modified to be in prime order subgroup
75 */
76static void
77map_to_scalar_subgroup (struct GNUNET_CRYPTO_Cs25519Scalar *scalar)
78{
79 // perform clamping as described in RFC7748
80 scalar->d[0] &= 248;
81 scalar->d[31] &= 127;
82 scalar->d[31] |= 64;
83}
84
85
86/**
87 * Derive a new secret r pair r0 and r1.
88 * In original papers r is generated randomly
89 * To provide abort-idempotency, r needs to be derived but still needs to be UNPREDICTABLE
90 * To ensure unpredictability a new nonce should be used when a new r needs to be derived.
91 * Uses HKDF internally.
92 * Comment: Can be done in one HKDF shot and split output.
93 *
94 * @param nonce is a random nonce
95 * @param lts is a long-term-secret in form of a private key
96 * @param[out] r array containing derived secrets r0 and r1
97 */
98void
99GNUNET_CRYPTO_cs_r_derive (const struct GNUNET_CRYPTO_CsNonce *nonce,
100 const struct GNUNET_CRYPTO_CsPrivateKey *lts,
101 struct GNUNET_CRYPTO_CsRSecret r[2])
102{
103 GNUNET_assert (GNUNET_YES ==
104 GNUNET_CRYPTO_hkdf (r,
105 sizeof (struct GNUNET_CRYPTO_CsRSecret)
106 * 2,
107 GCRY_MD_SHA512,
108 GCRY_MD_SHA256,
109 "r",
110 strlen ("r"),
111 lts,
112 sizeof (*lts),
113 nonce,
114 sizeof (*nonce),
115 NULL,
116 0));
117
118 map_to_scalar_subgroup (&r[0].scalar);
119 map_to_scalar_subgroup (&r[1].scalar);
120}
121
122
123/**
124 * Extract the public R of the given secret r.
125 *
126 * @param r_priv the private key
127 * @param[out] r_pub where to write the public key
128 */
129void
130GNUNET_CRYPTO_cs_r_get_public (const struct GNUNET_CRYPTO_CsRSecret *r_priv,
131 struct GNUNET_CRYPTO_CsRPublic *r_pub)
132{
133 GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (r_pub->point.y,
134 r_priv->scalar.d));
135}
136
137
138/**
139 * Derives new random blinding factors.
140 * In original papers blinding factors are generated randomly
141 * To provide abort-idempotency, blinding factors need to be derived but still need to be UNPREDICTABLE
142 * To ensure unpredictability a new nonce has to be used.
143 * Uses HKDF internally
144 *
145 * @param secret is secret to derive blinding factors
146 * @param secret_len secret length
147 * @param[out] bs array containing the two derived blinding secrets
148 */
149void
150GNUNET_CRYPTO_cs_blinding_secrets_derive (const void *secret,
151 size_t secret_len,
152 struct GNUNET_CRYPTO_CsBlindingSecret
153 bs[2])
154{
155 GNUNET_assert (GNUNET_YES ==
156 GNUNET_CRYPTO_hkdf (bs,
157 sizeof (struct
158 GNUNET_CRYPTO_CsBlindingSecret)
159 * 2,
160 GCRY_MD_SHA512,
161 GCRY_MD_SHA256,
162 "alphabeta",
163 strlen ("alphabeta"),
164 secret,
165 secret_len,
166 NULL,
167 0));
168 map_to_scalar_subgroup (&bs[0].alpha);
169 map_to_scalar_subgroup (&bs[0].beta);
170 map_to_scalar_subgroup (&bs[1].alpha);
171 map_to_scalar_subgroup (&bs[1].beta);
172}
173
174
175/*
176order of subgroup required for scalars by libsodium
1772^252 + 27742317777372353535851937790883648493
178copied from https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c
179and converted to big endian
180*/
181static const unsigned char L_BIG_ENDIAN[32] = {
182 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7,
184 0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed
185};
186
187
188/**
189 * Computes a Hash of (R', m) mapped to a Curve25519 scalar
190 *
191 * @param hash initial hash of the message to be signed
192 * @param pub denomination public key (used as salt)
193 * @param[out] c C containing scalar
194 */
195static void
196cs_full_domain_hash (const struct GNUNET_CRYPTO_CsRPublic *r_dash,
197 const void *msg,
198 size_t msg_len,
199 const struct GNUNET_CRYPTO_CsPublicKey *pub,
200 struct GNUNET_CRYPTO_CsC *c)
201{
202 // SHA-512 hash of R' and message
203 size_t r_m_concat_len = sizeof(struct GNUNET_CRYPTO_CsRPublic) + msg_len;
204 char r_m_concat[r_m_concat_len];
205 memcpy (r_m_concat, r_dash, sizeof(struct GNUNET_CRYPTO_CsRPublic));
206 memcpy (r_m_concat + sizeof(struct GNUNET_CRYPTO_CsRPublic), msg, msg_len);
207 struct GNUNET_HashCode prehash;
208 GNUNET_CRYPTO_hash (r_m_concat, r_m_concat_len, &prehash);
209
210 // modulus converted to MPI representation
211 gcry_mpi_t l_mpi;
212 GNUNET_CRYPTO_mpi_scan_unsigned (&l_mpi, L_BIG_ENDIAN, sizeof(L_BIG_ENDIAN));
213
214 // calculate full domain hash
215 gcry_mpi_t c_mpi;
216 GNUNET_CRYPTO_kdf_mod_mpi (&c_mpi,
217 l_mpi,
218 pub,
219 sizeof(struct GNUNET_CRYPTO_CsPublicKey),
220 &prehash,
221 sizeof(struct GNUNET_HashCode),
222 "Curve25519FDH");
223 gcry_mpi_release (l_mpi);
224
225 // convert c from mpi
226 unsigned char c_big_endian[256 / 8];
227 GNUNET_CRYPTO_mpi_print_unsigned (c_big_endian, sizeof(c_big_endian), c_mpi);
228 gcry_mpi_release (c_mpi);
229 for (size_t i = 0; i<32; i++)
230 c->scalar.d[i] = c_big_endian[31 - i];
231}
232
233
234/**
235 * calculate R'
236 *
237 * @param bs blinding secret
238 * @param r_pub R
239 * @param pub public key
240 * @param[out] blinded_r_pub R'
241 */
242static void
243calc_r_dash (const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
244 const struct GNUNET_CRYPTO_CsRPublic *r_pub,
245 const struct GNUNET_CRYPTO_CsPublicKey *pub,
246 struct GNUNET_CRYPTO_CsRPublic *blinded_r_pub)
247{
248 // R'i = Ri + alpha i*G + beta i*pub
249 struct GNUNET_CRYPTO_Cs25519Point alpha_mul_base;
250 GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (
251 alpha_mul_base.y,
252 bs->alpha.d));
253 struct GNUNET_CRYPTO_Cs25519Point beta_mul_pub;
254 GNUNET_assert (0 == crypto_scalarmult_ed25519_noclamp (beta_mul_pub.y,
255 bs->beta.d,
256 pub->point.y));
257 struct GNUNET_CRYPTO_Cs25519Point alpha_mul_base_plus_beta_mul_pub;
258 GNUNET_assert (0 == crypto_core_ed25519_add (
259 alpha_mul_base_plus_beta_mul_pub.y,
260 alpha_mul_base.y,
261 beta_mul_pub.y));
262 GNUNET_assert (0 == crypto_core_ed25519_add (blinded_r_pub->point.y,
263 r_pub->point.y,
264 alpha_mul_base_plus_beta_mul_pub.
265 y));
266}
267
268
269/**
270 * Calculate two blinded c's
271 * Comment: One would be insecure due to Wagner's algorithm solving ROS
272 *
273 * @param bs array of the two blinding factor structs each containing alpha and beta
274 * @param r_pub array of the two signer's nonce R
275 * @param pub the public key of the signer
276 * @param msg the message to blind in preparation for signing
277 * @param msg_len length of message msg
278 * @param[out] blinded_c array of the two blinded c's
279 * @param[out] blinded_r_pub array of the two blinded R
280 */
281void
282GNUNET_CRYPTO_cs_calc_blinded_c (const struct GNUNET_CRYPTO_CsBlindingSecret
283 bs[2],
284 const struct GNUNET_CRYPTO_CsRPublic r_pub[2],
285 const struct GNUNET_CRYPTO_CsPublicKey *pub,
286 const void *msg,
287 size_t msg_len,
288 struct GNUNET_CRYPTO_CsC blinded_c[2],
289 struct GNUNET_CRYPTO_CsRPublic
290 blinded_r_pub[2])
291{
292 // for i 0/1: R'i = Ri + alpha i*G + beta i*pub
293 calc_r_dash (&bs[0], &r_pub[0], pub, &blinded_r_pub[0]);
294 calc_r_dash (&bs[1], &r_pub[1], pub, &blinded_r_pub[1]);
295
296 // for i 0/1: c'i = H(R'i, msg)
297 struct GNUNET_CRYPTO_CsC c_dash_0;
298 struct GNUNET_CRYPTO_CsC c_dash_1;
299 cs_full_domain_hash (&blinded_r_pub[0], msg, msg_len, pub, &c_dash_0);
300 cs_full_domain_hash (&blinded_r_pub[1], msg, msg_len, pub, &c_dash_1);
301
302 // for i 0/1: ci = c'i + beta i mod p
303 crypto_core_ed25519_scalar_add (blinded_c[0].scalar.d,
304 c_dash_0.scalar.d,
305 bs[0].beta.d);
306 crypto_core_ed25519_scalar_add (blinded_c[1].scalar.d,
307 c_dash_1.scalar.d,
308 bs[1].beta.d);
309}
310
311
312/**
313 * Sign a blinded c
314 * This function derives b from a nonce and a longterm secret
315 * In original papers b is generated randomly
316 * To provide abort-idempotency, b needs to be derived but still need to be UNPREDICTABLE.
317 * To ensure unpredictability a new nonce has to be used for every signature
318 * HKDF is used internally for derivation
319 * r0 and r1 can be derived prior by using GNUNET_CRYPTO_cs_r_derive
320 *
321 * @param priv private key to use for the signing and as LTS in HKDF
322 * @param r array of the two secret nonce from the signer
323 * @param c array of the two blinded c to sign c_b
324 * @param nonce is a random nonce
325 * @param[out] blinded_signature_scalar where to write the signature
326 * @return 0 or 1 for b (see Clause Blind Signature Scheme)
327 */
328unsigned int
329GNUNET_CRYPTO_cs_sign_derive (const struct GNUNET_CRYPTO_CsPrivateKey *priv,
330 const struct GNUNET_CRYPTO_CsRSecret r[2],
331 const struct GNUNET_CRYPTO_CsC c[2],
332 const struct GNUNET_CRYPTO_CsNonce *nonce,
333 struct GNUNET_CRYPTO_CsBlindS *
334 blinded_signature_scalar
335 )
336{
337 uint32_t hkdf_out;
338
339 // derive clause session identifier b (random bit)
340 GNUNET_assert (GNUNET_YES ==
341 GNUNET_CRYPTO_hkdf (&hkdf_out,
342 sizeof (hkdf_out),
343 GCRY_MD_SHA512,
344 GCRY_MD_SHA256,
345 "b",
346 strlen ("b"),
347 priv,
348 sizeof (*priv),
349 nonce,
350 sizeof (*nonce),
351 NULL,
352 0));
353 unsigned int b = hkdf_out % 2;
354
355 // s = r_b + c_b priv
356 struct GNUNET_CRYPTO_Cs25519Scalar c_b_mul_priv;
357 crypto_core_ed25519_scalar_mul (c_b_mul_priv.d,
358 c[b].scalar.d,
359 priv->scalar.d);
360 crypto_core_ed25519_scalar_add (blinded_signature_scalar->scalar.d,
361 r[b].scalar.d,
362 c_b_mul_priv.d);
363
364 return b;
365}
366
367
368/**
369 * Unblind a blind-signed signature using a c that was blinded
370 *
371 * @param blinded_signature_scalar the signature made on the blinded c
372 * @param bs the blinding factors used in the blinding
373 * @param[out] signature_scalar where to write the unblinded signature
374 */
375void
376GNUNET_CRYPTO_cs_unblind (const struct
377 GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar,
378 const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
379 struct GNUNET_CRYPTO_CsS *signature_scalar)
380{
381 crypto_core_ed25519_scalar_add (signature_scalar->scalar.d,
382 blinded_signature_scalar->scalar.d,
383 bs->alpha.d);
384}
385
386
387/**
388 * Verify whether the given message corresponds to the given signature and the
389 * signature is valid with respect to the given public key.
390 *
391 * @param sig signature that is being validated
392 * @param pub public key of the signer
393 * @param msg is the message that should be signed by @a sig (message is used to calculate c)
394 * @param msg_len is the message length
395 * @returns #GNUNET_YES on success, #GNUNET_SYSERR if signature invalid
396 */
397enum GNUNET_GenericReturnValue
398GNUNET_CRYPTO_cs_verify (const struct GNUNET_CRYPTO_CsSignature *sig,
399 const struct GNUNET_CRYPTO_CsPublicKey *pub,
400 const void *msg,
401 size_t msg_len)
402{
403 // calculate c' = H(R, m)
404 struct GNUNET_CRYPTO_CsC c_dash;
405 cs_full_domain_hash (&sig->r_point, msg, msg_len, pub, &c_dash);
406
407 // s'G ?= R' + c' pub
408 struct GNUNET_CRYPTO_Cs25519Point sig_scal_mul_base;
409 GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (
410 sig_scal_mul_base.y,
411 sig->s_scalar.scalar.d));
412 struct GNUNET_CRYPTO_Cs25519Point c_dash_mul_pub;
413 GNUNET_assert (0 == crypto_scalarmult_ed25519_noclamp (c_dash_mul_pub.y,
414 c_dash.scalar.d,
415 pub->point.y));
416 struct GNUNET_CRYPTO_Cs25519Point R_add_c_dash_mul_pub;
417 GNUNET_assert (0 == crypto_core_ed25519_add (R_add_c_dash_mul_pub.y,
418 sig->r_point.point.y,
419 c_dash_mul_pub.y));
420
421 return 0 == GNUNET_memcmp (&sig_scal_mul_base,
422 &R_add_c_dash_mul_pub)
423 ? GNUNET_OK
424 : GNUNET_SYSERR;
425}