Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2022-2023 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file util/age_restriction.c
18 : * @brief Functions that are used for age restriction
19 : * @author Özgür Kesim
20 : */
21 : #include "taler/platform.h"
22 : #include "taler/taler_util.h"
23 : #include "taler/taler_signatures.h"
24 : #include <gnunet/gnunet_json_lib.h>
25 : #include <gcrypt.h>
26 : #include <stdint.h>
27 :
28 : struct
29 : #ifndef AGE_RESTRICTION_WITH_ECDSA
30 : GNUNET_CRYPTO_Edx25519PublicKey
31 : #else
32 : GNUNET_CRYPTO_EcdsaPublicKey
33 : #endif
34 : TALER_age_commitment_base_public_key = {
35 : .q_y = { 0x64, 0x41, 0xb9, 0xbd, 0xbf, 0x14, 0x39, 0x8e,
36 : 0x46, 0xeb, 0x5c, 0x1d, 0x34, 0xd3, 0x9b, 0x2f,
37 : 0x9b, 0x7d, 0xc8, 0x18, 0xeb, 0x9c, 0x09, 0xfb,
38 : 0x43, 0xad, 0x16, 0x64, 0xbc, 0x18, 0x49, 0xb5},
39 : };
40 :
41 : void
42 463 : TALER_age_commitment_hash (
43 : const struct TALER_AgeCommitment *commitment,
44 : struct TALER_AgeCommitmentHashP *ahash)
45 : {
46 : struct GNUNET_HashContext *hash_context;
47 : struct GNUNET_HashCode hash;
48 :
49 463 : GNUNET_assert (NULL != ahash);
50 463 : if (NULL == commitment)
51 : {
52 18 : memset (ahash, 0, sizeof(struct TALER_AgeCommitmentHashP));
53 18 : return;
54 : }
55 :
56 445 : GNUNET_assert (__builtin_popcount (commitment->mask.bits) - 1 ==
57 : (int) commitment->num);
58 :
59 445 : hash_context = GNUNET_CRYPTO_hash_context_start ();
60 :
61 3560 : for (size_t i = 0; i < commitment->num; i++)
62 : {
63 3115 : GNUNET_CRYPTO_hash_context_read (hash_context,
64 3115 : &commitment->pubs[i],
65 : sizeof(commitment->pubs[i]));
66 : }
67 :
68 445 : GNUNET_CRYPTO_hash_context_finish (hash_context,
69 : &hash);
70 445 : GNUNET_memcpy (&ahash->shash.bits,
71 : &hash.bits,
72 : sizeof(ahash->shash.bits));
73 : }
74 :
75 :
76 : /* To a given age value between 0 and 31, returns the index of the age group
77 : * defined by the given mask.
78 : */
79 : uint8_t
80 6174 : TALER_get_age_group (
81 : const struct TALER_AgeMask *mask,
82 : uint8_t age)
83 : {
84 6174 : uint32_t m = mask->bits;
85 6174 : uint8_t i = 0;
86 :
87 68471 : while (m > 0)
88 : {
89 68430 : if (0 >= age)
90 6133 : break;
91 62297 : m = m >> 1;
92 62297 : i += m & 1;
93 62297 : age--;
94 : }
95 6174 : return i;
96 : }
97 :
98 :
99 : uint8_t
100 32 : TALER_get_lowest_age (
101 : const struct TALER_AgeMask *mask,
102 : uint8_t age)
103 : {
104 32 : uint32_t m = mask->bits;
105 32 : uint8_t group = TALER_get_age_group (mask, age);
106 32 : uint8_t lowest = 0;
107 :
108 354 : while (group > 0)
109 : {
110 322 : m = m >> 1;
111 322 : if (m & 1)
112 78 : group--;
113 322 : lowest++;
114 : }
115 :
116 32 : return lowest;
117 : }
118 :
119 :
120 : #ifdef AGE_RESTRICTION_WITH_ECDSA
121 : /**
122 : * @brief Helper function to generate a ECDSA private key
123 : *
124 : * @param seed Input seed
125 : * @param size Size of the seed in bytes
126 : * @param[out] pkey ECDSA private key
127 : */
128 : static void
129 : ecdsa_create_from_seed (
130 : const void *seed,
131 : size_t seed_size,
132 : struct GNUNET_CRYPTO_EcdsaPrivateKey *key)
133 : {
134 : enum GNUNET_GenericReturnValue ret;
135 :
136 : GNUNET_assert (
137 : GNUNET_OK ==
138 : GNUNET_CRYPTO_hkdf_gnunet (key,
139 : sizeof (*key),
140 : &seed,
141 : seed_size,
142 : "age commitment",
143 : sizeof ("age commitment") - 1));
144 : /* See GNUNET_CRYPTO_ecdsa_key_create */
145 : key->d[0] &= 248;
146 : key->d[31] &= 127;
147 : key->d[31] |= 64;
148 : }
149 :
150 :
151 : #endif
152 :
153 :
154 : void
155 35 : TALER_age_restriction_commit (
156 : const struct TALER_AgeMask *mask,
157 : uint8_t age,
158 : const struct GNUNET_HashCode *seed,
159 : struct TALER_AgeCommitmentProof *ncp)
160 : {
161 : struct GNUNET_HashCode seed_i;
162 : uint8_t num_pub;
163 : uint8_t num_priv;
164 : size_t i;
165 :
166 35 : GNUNET_assert (NULL != mask);
167 35 : GNUNET_assert (NULL != seed);
168 35 : GNUNET_assert (NULL != ncp);
169 35 : GNUNET_assert (mask->bits & 1); /* first bit must have been set */
170 :
171 35 : num_pub = __builtin_popcount (mask->bits) - 1;
172 35 : num_priv = TALER_get_age_group (mask, age);
173 :
174 35 : GNUNET_assert (31 > num_priv);
175 35 : GNUNET_assert (num_priv <= num_pub);
176 :
177 35 : seed_i = *seed;
178 35 : ncp->commitment.mask.bits = mask->bits;
179 35 : ncp->commitment.num = num_pub;
180 35 : ncp->proof.num = num_priv;
181 35 : ncp->proof.privs = NULL;
182 :
183 35 : ncp->commitment.pubs = GNUNET_new_array (
184 : num_pub,
185 : struct TALER_AgeCommitmentPublicKeyP);
186 :
187 35 : if (0 < num_priv)
188 27 : ncp->proof.privs = GNUNET_new_array (
189 : num_priv,
190 : struct TALER_AgeCommitmentPrivateKeyP);
191 :
192 : /* Create as many private keys as we need and fill the rest of the
193 : * public keys with valid curve points.
194 : * We need to make sure that the public keys are proper points on the
195 : * elliptic curve, so we can't simply fill the struct with random values. */
196 280 : for (i = 0; i < num_pub; i++)
197 : {
198 245 : struct TALER_AgeCommitmentPrivateKeyP key = {0};
199 245 : struct TALER_AgeCommitmentPrivateKeyP *pkey = &key;
200 :
201 : /* Only save the private keys for age groups less than num_priv */
202 245 : if (i < num_priv)
203 138 : pkey = &ncp->proof.privs[i];
204 :
205 : #ifndef AGE_RESTRICTION_WITH_ECDSA
206 245 : GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
207 : sizeof(seed_i),
208 : &pkey->priv);
209 245 : GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
210 245 : &ncp->commitment.pubs[i].pub);
211 : #else
212 : ecdsa_create_from_seed (&seed_i,
213 : sizeof(seed_i),
214 : &pkey->priv);
215 : GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
216 : &ncp->commitment.pubs[i].pub);
217 : #endif
218 :
219 245 : seed_i.bits[0] += 1;
220 : }
221 35 : }
222 :
223 :
224 : enum GNUNET_GenericReturnValue
225 418 : TALER_age_commitment_derive (
226 : const struct TALER_AgeCommitment *orig,
227 : const struct GNUNET_HashCode *salt,
228 : struct TALER_AgeCommitment *newac)
229 : {
230 418 : GNUNET_assert (NULL != newac);
231 418 : GNUNET_assert (((int) orig->num) ==
232 : __builtin_popcount (orig->mask.bits) - 1);
233 :
234 418 : newac->mask = orig->mask;
235 418 : newac->num = orig->num;
236 418 : newac->pubs = GNUNET_new_array (
237 : newac->num,
238 : struct TALER_AgeCommitmentPublicKeyP);
239 :
240 : #ifndef AGE_RESTRICTION_WITH_ECDSA
241 : /* Derive the public keys */
242 3344 : for (size_t i = 0; i < orig->num; i++)
243 : {
244 2926 : GNUNET_CRYPTO_edx25519_public_key_derive (
245 2926 : &orig->pubs[i].pub,
246 : salt,
247 : sizeof(*salt),
248 2926 : &newac->pubs[i].pub);
249 : }
250 : #else
251 : {
252 : const char *label = GNUNET_h2s (salt);
253 :
254 : /* Derive the public keys */
255 : for (size_t i = 0; i < orig->num; i++)
256 : {
257 : GNUNET_CRYPTO_ecdsa_public_key_derive (
258 : &orig->pubs[i].pub,
259 : label,
260 : "age commitment derive",
261 : &newac->pubs[i].pub);
262 : }
263 : }
264 : #endif
265 :
266 418 : return GNUNET_OK;
267 : }
268 :
269 :
270 : enum GNUNET_GenericReturnValue
271 64 : TALER_age_commitment_derive_from_secret (
272 : const struct TALER_AgeCommitment *orig,
273 : const struct TALER_PlanchetMasterSecretP *secret,
274 : struct TALER_AgeCommitment *newac)
275 : {
276 : struct GNUNET_HashCode salt;
277 : enum GNUNET_GenericReturnValue ret;
278 :
279 64 : ret = GNUNET_CRYPTO_hkdf_gnunet (&salt,
280 : sizeof (salt),
281 : "age commitment",
282 : strlen ("age commitment"),
283 : secret,
284 : sizeof(*secret));
285 64 : if (GNUNET_OK != ret)
286 : {
287 0 : GNUNET_break (0);
288 0 : return ret;
289 : }
290 :
291 64 : return TALER_age_commitment_derive (
292 : orig,
293 : &salt,
294 : newac);
295 : }
296 :
297 :
298 : enum GNUNET_GenericReturnValue
299 354 : TALER_age_commitment_proof_derive (
300 : const struct TALER_AgeCommitmentProof *orig,
301 : const struct GNUNET_HashCode *salt,
302 : struct TALER_AgeCommitmentProof *newacp)
303 : {
304 : enum GNUNET_GenericReturnValue ret;
305 354 : GNUNET_assert (NULL != newacp);
306 354 : GNUNET_assert (orig->proof.num <=
307 : orig->commitment.num);
308 354 : GNUNET_assert (((int) orig->commitment.num) ==
309 : __builtin_popcount (orig->commitment.mask.bits) - 1);
310 :
311 354 : ret = TALER_age_commitment_derive (
312 : &orig->commitment,
313 : salt,
314 : &newacp->commitment);
315 354 : if (GNUNET_OK != ret)
316 : {
317 0 : GNUNET_break (0);
318 0 : return ret;
319 : }
320 :
321 354 : newacp->proof.num = orig->proof.num;
322 354 : newacp->proof.privs = NULL;
323 354 : if (0 != newacp->proof.num)
324 338 : newacp->proof.privs = GNUNET_new_array (
325 : newacp->proof.num,
326 : struct TALER_AgeCommitmentPrivateKeyP);
327 :
328 : #ifndef AGE_RESTRICTION_WITH_ECDSA
329 : /* Derive the private keys */
330 1482 : for (size_t i = 0; i < orig->proof.num; i++)
331 : {
332 1128 : GNUNET_CRYPTO_edx25519_private_key_derive (
333 1128 : &orig->proof.privs[i].priv,
334 : salt,
335 : sizeof(*salt),
336 1128 : &newacp->proof.privs[i].priv);
337 : }
338 : #else
339 : {
340 : const char *label = GNUNET_h2s (salt);
341 :
342 : /* Derive the private keys */
343 : for (size_t i = 0; i < orig->proof.num; i++)
344 : {
345 : struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
346 : priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
347 : &orig->proof.privs[i].priv,
348 : label,
349 : "age commitment derive");
350 : newacp->proof.privs[i].priv = *priv;
351 : GNUNET_free (priv);
352 : }
353 : }
354 : #endif
355 :
356 354 : return GNUNET_OK;
357 : }
358 :
359 :
360 : enum GNUNET_GenericReturnValue
361 288 : TALER_age_commitment_proof_derive_from_secret (
362 : const struct TALER_AgeCommitmentProof *orig,
363 : const struct TALER_PlanchetMasterSecretP *secret,
364 : struct TALER_AgeCommitmentProof *newacp)
365 : {
366 : struct GNUNET_HashCode salt;
367 : enum GNUNET_GenericReturnValue ret;
368 :
369 288 : ret = GNUNET_CRYPTO_hkdf_gnunet (&salt,
370 : sizeof (salt),
371 : "age commitment",
372 : strlen ("age commitment"),
373 : secret,
374 : sizeof(*secret));
375 288 : if (GNUNET_OK != ret)
376 : {
377 0 : GNUNET_break (0);
378 0 : return ret;
379 : }
380 :
381 288 : return TALER_age_commitment_proof_derive (
382 : orig,
383 : &salt,
384 : newacp);
385 : }
386 :
387 :
388 : GNUNET_NETWORK_STRUCT_BEGIN
389 :
390 : /**
391 : * Age group mask in network byte order.
392 : */
393 : struct TALER_AgeMaskNBO
394 : {
395 : uint32_t bits_nbo;
396 : };
397 :
398 : /**
399 : * Used for attestation of a particular age
400 : */
401 : struct TALER_AgeAttestationPPS
402 : {
403 : /**
404 : * Purpose must be #TALER_SIGNATURE_WALLET_AGE_ATTESTATION.
405 : * (no GNUNET_PACKED here because the struct is already packed)
406 : */
407 : struct GNUNET_CRYPTO_SignaturePurpose purpose;
408 :
409 : /**
410 : * Age mask that defines the underlying age groups
411 : */
412 : struct TALER_AgeMaskNBO mask GNUNET_PACKED;
413 :
414 : /**
415 : * The particular age that this attestation is for.
416 : * We use uint32_t here for alignment.
417 : */
418 : uint32_t age GNUNET_PACKED;
419 : };
420 :
421 : GNUNET_NETWORK_STRUCT_END
422 :
423 :
424 : enum GNUNET_GenericReturnValue
425 2178 : TALER_age_commitment_attest (
426 : const struct TALER_AgeCommitmentProof *cp,
427 : uint8_t age,
428 : struct TALER_AgeAttestationP *attest)
429 : {
430 : uint8_t group;
431 :
432 2178 : GNUNET_assert (NULL != attest);
433 2178 : GNUNET_assert (NULL != cp);
434 :
435 2178 : group = TALER_get_age_group (&cp->commitment.mask,
436 : age);
437 :
438 2178 : GNUNET_assert (group < 32);
439 :
440 2178 : if (0 == group)
441 : {
442 : /* Age group 0 means: no attestation necessary.
443 : * We set the signature to zero and communicate success. */
444 792 : memset (attest,
445 : 0,
446 : sizeof(struct TALER_AgeAttestationP));
447 792 : return GNUNET_OK;
448 : }
449 :
450 1386 : if (group > cp->proof.num)
451 585 : return GNUNET_NO;
452 :
453 : {
454 801 : struct TALER_AgeAttestationPPS at = {
455 801 : .purpose.size = htonl (sizeof(at)),
456 801 : .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
457 801 : .mask.bits_nbo = htonl (cp->commitment.mask.bits),
458 801 : .age = htonl (age),
459 : };
460 :
461 : #ifndef AGE_RESTRICTION_WITH_ECDSA
462 : #define sign(a,b,c) GNUNET_CRYPTO_edx25519_sign (a,b,c)
463 : #else
464 : #define sign(a,b,c) GNUNET_CRYPTO_ecdsa_sign (a,b,c)
465 : #endif
466 801 : sign (&cp->proof.privs[group - 1].priv,
467 : &at,
468 : &attest->signature);
469 : }
470 : #undef sign
471 :
472 801 : return GNUNET_OK;
473 : }
474 :
475 :
476 : enum GNUNET_GenericReturnValue
477 1593 : TALER_age_commitment_verify (
478 : const struct TALER_AgeCommitment *comm,
479 : uint8_t age,
480 : const struct TALER_AgeAttestationP *attest)
481 : {
482 : uint8_t group;
483 :
484 1593 : GNUNET_assert (NULL != attest);
485 1593 : GNUNET_assert (NULL != comm);
486 :
487 1593 : group = TALER_get_age_group (&comm->mask,
488 : age);
489 :
490 1593 : GNUNET_assert (group < 32);
491 :
492 : /* Age group 0 means: no attestation necessary. */
493 1593 : if (0 == group)
494 792 : return GNUNET_OK;
495 :
496 801 : if (group > comm->num)
497 : {
498 0 : GNUNET_break_op (0);
499 0 : return GNUNET_NO;
500 : }
501 :
502 : {
503 801 : struct TALER_AgeAttestationPPS at = {
504 801 : .purpose.size = htonl (sizeof(at)),
505 801 : .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
506 801 : .mask.bits_nbo = htonl (comm->mask.bits),
507 801 : .age = htonl (age),
508 : };
509 :
510 : #ifndef AGE_RESTRICTION_WITH_ECDSA
511 : #define verify(a,b,c,d) GNUNET_CRYPTO_edx25519_verify ((a),(b),(c),(d))
512 : #else
513 : #define verify(a,b,c,d) GNUNET_CRYPTO_ecdsa_verify ((a),(b),(c),(d))
514 : #endif
515 801 : return verify (TALER_SIGNATURE_WALLET_AGE_ATTESTATION,
516 : &at,
517 : &attest->signature,
518 : &comm->pubs[group - 1].pub);
519 : }
520 : #undef verify
521 : }
522 :
523 :
524 : void
525 64 : TALER_age_commitment_free (
526 : struct TALER_AgeCommitment *commitment)
527 : {
528 64 : if (NULL == commitment)
529 0 : return;
530 :
531 64 : if (NULL != commitment->pubs)
532 : {
533 64 : GNUNET_free (commitment->pubs);
534 64 : commitment->pubs = NULL;
535 : }
536 : }
537 :
538 :
539 : void
540 0 : TALER_age_proof_free (
541 : struct TALER_AgeProof *proof)
542 : {
543 0 : if (NULL == proof)
544 0 : return;
545 :
546 0 : if (NULL != proof->privs)
547 : {
548 0 : GNUNET_CRYPTO_zero_keys (
549 0 : proof->privs,
550 0 : sizeof(*proof->privs) * proof->num);
551 :
552 0 : GNUNET_free (proof->privs);
553 0 : proof->privs = NULL;
554 : }
555 : }
556 :
557 :
558 : void
559 817 : TALER_age_commitment_proof_free (
560 : struct TALER_AgeCommitmentProof *acp)
561 : {
562 817 : if (NULL == acp)
563 264 : return;
564 :
565 553 : if (NULL != acp->proof.privs)
566 : {
567 460 : GNUNET_CRYPTO_zero_keys (
568 460 : acp->proof.privs,
569 460 : sizeof(*acp->proof.privs) * acp->proof.num);
570 :
571 460 : GNUNET_free (acp->proof.privs);
572 460 : acp->proof.privs = NULL;
573 : }
574 :
575 553 : if (NULL != acp->commitment.pubs)
576 : {
577 489 : GNUNET_free (acp->commitment.pubs);
578 489 : acp->commitment.pubs = NULL;
579 : }
580 : }
581 :
582 :
583 : struct TALER_AgeCommitmentProof *
584 32 : TALER_age_commitment_proof_duplicate (
585 : const struct TALER_AgeCommitmentProof *acp)
586 : {
587 : struct TALER_AgeCommitmentProof *nacp;
588 :
589 32 : GNUNET_assert (NULL != acp);
590 32 : GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 ==
591 : (int) acp->commitment.num);
592 :
593 32 : nacp = GNUNET_new (struct TALER_AgeCommitmentProof);
594 :
595 32 : TALER_age_commitment_proof_deep_copy (nacp, acp);
596 32 : return nacp;
597 : }
598 :
599 :
600 : struct TALER_AgeCommitment *
601 0 : TALER_age_commitment_duplicate (
602 : const struct TALER_AgeCommitment *ac)
603 : {
604 : struct TALER_AgeCommitment *nac;
605 :
606 0 : GNUNET_assert (NULL != ac);
607 0 : GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 ==
608 : (int) ac->num);
609 :
610 0 : nac = GNUNET_new (struct TALER_AgeCommitment);
611 0 : TALER_age_commitment_deep_copy (nac, ac);
612 0 : return nac;
613 : }
614 :
615 :
616 : void
617 49 : TALER_age_commitment_proof_deep_copy (
618 : struct TALER_AgeCommitmentProof *nacp,
619 : const struct TALER_AgeCommitmentProof *acp)
620 : {
621 49 : GNUNET_assert (NULL != acp);
622 49 : GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 ==
623 : (int) acp->commitment.num);
624 :
625 49 : *nacp = *acp;
626 49 : nacp->commitment.pubs =
627 49 : GNUNET_new_array (acp->commitment.num,
628 : struct TALER_AgeCommitmentPublicKeyP);
629 49 : nacp->proof.privs =
630 49 : GNUNET_new_array (acp->proof.num,
631 : struct TALER_AgeCommitmentPrivateKeyP);
632 :
633 392 : for (size_t i = 0; i < acp->commitment.num; i++)
634 343 : nacp->commitment.pubs[i] = acp->commitment.pubs[i];
635 :
636 181 : for (size_t i = 0; i < acp->proof.num; i++)
637 132 : nacp->proof.privs[i] = acp->proof.privs[i];
638 49 : }
639 :
640 :
641 : void
642 0 : TALER_age_commitment_deep_copy (
643 : struct TALER_AgeCommitment *nac,
644 : const struct TALER_AgeCommitment *ac)
645 : {
646 0 : GNUNET_assert (NULL != ac);
647 0 : GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 ==
648 : (int) ac->num);
649 :
650 0 : *nac = *ac;
651 0 : nac->pubs =
652 0 : GNUNET_new_array (ac->num,
653 : struct TALER_AgeCommitmentPublicKeyP);
654 :
655 0 : for (size_t i = 0; i < ac->num; i++)
656 0 : nac->pubs[i] = ac->pubs[i];
657 :
658 0 : }
659 :
660 :
661 : enum GNUNET_GenericReturnValue
662 11 : TALER_JSON_parse_age_groups (const json_t *root,
663 : struct TALER_AgeMask *mask)
664 : {
665 : enum GNUNET_GenericReturnValue ret;
666 : const char *str;
667 : struct GNUNET_JSON_Specification spec[] = {
668 11 : GNUNET_JSON_spec_string ("age_groups",
669 : &str),
670 11 : GNUNET_JSON_spec_end ()
671 : };
672 :
673 11 : ret = GNUNET_JSON_parse (root,
674 : spec,
675 : NULL,
676 : NULL);
677 11 : if (GNUNET_OK == ret)
678 11 : TALER_parse_age_group_string (str, mask);
679 :
680 11 : GNUNET_JSON_parse_free (spec);
681 :
682 11 : return ret;
683 : }
684 :
685 :
686 : enum GNUNET_GenericReturnValue
687 61 : TALER_parse_age_group_string (
688 : const char *groups,
689 : struct TALER_AgeMask *mask)
690 : {
691 :
692 61 : const char *pos = groups;
693 61 : unsigned int prev = 0;
694 61 : unsigned int val = 0;
695 : char c;
696 :
697 : /* reset mask */
698 61 : mask->bits = 0;
699 :
700 1220 : while (*pos)
701 : {
702 1159 : c = *pos++;
703 1159 : if (':' == c)
704 : {
705 366 : if (prev >= val)
706 0 : return GNUNET_SYSERR;
707 :
708 366 : mask->bits |= 1 << val;
709 366 : prev = val;
710 366 : val = 0;
711 366 : continue;
712 : }
713 :
714 793 : if ('0'>c || '9'<c)
715 0 : return GNUNET_SYSERR;
716 :
717 793 : val = 10 * val + c - '0';
718 :
719 793 : if (0>=val || 32<=val)
720 0 : return GNUNET_SYSERR;
721 : }
722 :
723 61 : if (32<=val || prev>=val)
724 0 : return GNUNET_SYSERR;
725 :
726 61 : mask->bits |= (1 << val);
727 61 : mask->bits |= 1; // mark zeroth group, too
728 :
729 61 : return GNUNET_OK;
730 : }
731 :
732 :
733 : const char *
734 101 : TALER_age_mask_to_string (
735 : const struct TALER_AgeMask *mask)
736 : {
737 : static char buf[256] = {0};
738 101 : uint32_t bits = mask->bits;
739 101 : unsigned int n = 0;
740 101 : char *pos = buf;
741 :
742 101 : memset (buf, 0, sizeof(buf));
743 :
744 2270 : while (bits != 0)
745 : {
746 2169 : bits >>= 1;
747 2169 : n++;
748 2169 : if (0 == (bits & 1))
749 : {
750 1489 : continue;
751 : }
752 :
753 680 : if (n > 9)
754 : {
755 580 : *(pos++) = '0' + n / 10;
756 : }
757 680 : *(pos++) = '0' + n % 10;
758 :
759 680 : if (0 != (bits >> 1))
760 : {
761 579 : *(pos++) = ':';
762 : }
763 : }
764 101 : return buf;
765 : }
766 :
767 :
768 : void
769 51 : TALER_age_restriction_from_secret (
770 : const struct TALER_PlanchetMasterSecretP *secret,
771 : const struct TALER_AgeMask *mask,
772 : const uint8_t max_age,
773 : struct TALER_AgeCommitmentProof *ncp)
774 : {
775 51 : struct GNUNET_HashCode seed_i = {0};
776 : uint8_t num_pub;
777 : uint8_t num_priv;
778 :
779 51 : GNUNET_assert (NULL != mask);
780 51 : GNUNET_assert (NULL != secret);
781 51 : GNUNET_assert (NULL != ncp);
782 51 : GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
783 :
784 51 : num_pub = __builtin_popcount (mask->bits) - 1;
785 51 : num_priv = TALER_get_age_group (mask, max_age);
786 :
787 51 : GNUNET_assert (31 > num_priv);
788 51 : GNUNET_assert (num_priv <= num_pub);
789 :
790 51 : ncp->commitment.mask.bits = mask->bits;
791 51 : ncp->commitment.num = num_pub;
792 51 : ncp->proof.num = num_priv;
793 51 : ncp->proof.privs = NULL;
794 51 : ncp->commitment.pubs = GNUNET_new_array (
795 : num_pub,
796 : struct TALER_AgeCommitmentPublicKeyP);
797 51 : if (0 < num_priv)
798 46 : ncp->proof.privs = GNUNET_new_array (
799 : num_priv,
800 : struct TALER_AgeCommitmentPrivateKeyP);
801 :
802 : /* Create as many private keys as allow with max_age and derive the
803 : * corresponding public keys. The rest of the needed public keys are created
804 : * by scalar multiplication with the TALER_age_commitment_base_public_key. */
805 408 : for (size_t i = 0; i < num_pub; i++)
806 : {
807 : enum GNUNET_GenericReturnValue ret;
808 357 : const char *label = i < num_priv ? "age-commitment" : "age-factor";
809 :
810 357 : ret = GNUNET_CRYPTO_hkdf_gnunet (&seed_i, sizeof(seed_i),
811 : secret, sizeof(*secret),
812 : label, strlen (label),
813 : GNUNET_CRYPTO_kdf_arg_auto (&i));
814 357 : GNUNET_assert (GNUNET_OK == ret);
815 :
816 : /* Only generate and save the private keys and public keys for age groups
817 : * less than num_priv */
818 357 : if (i < num_priv)
819 : {
820 96 : struct TALER_AgeCommitmentPrivateKeyP *pkey = &ncp->proof.privs[i];
821 :
822 : #ifndef AGE_RESTRICTION_WITH_ECDSA
823 96 : GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
824 : sizeof(seed_i),
825 : &pkey->priv);
826 96 : GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
827 96 : &ncp->commitment.pubs[i].pub);
828 : #else
829 : ecdsa_create_from_seed (&seed_i,
830 : sizeof(seed_i),
831 : &pkey->priv);
832 : GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
833 : &ncp->commitment.pubs[i].pub);
834 : #endif
835 : }
836 : else
837 : {
838 : /* For all indices larger than num_priv, derive a public key from
839 : * TALER_age_commitment_base_public_key by scalar multiplication */
840 : #ifndef AGE_RESTRICTION_WITH_ECDSA
841 261 : GNUNET_CRYPTO_edx25519_public_key_derive (
842 : &TALER_age_commitment_base_public_key,
843 : &seed_i,
844 : sizeof(seed_i),
845 261 : &ncp->commitment.pubs[i].pub);
846 : #else
847 :
848 : GNUNET_CRYPTO_ecdsa_public_key_derive (
849 : &TALER_age_commitment_base_public_key,
850 : GNUNET_h2s (&seed_i),
851 : "age withdraw",
852 : &ncp->commitment.pubs[i].pub);
853 : #endif
854 : }
855 : }
856 51 : }
857 :
858 :
859 : enum GNUNET_GenericReturnValue
860 27 : TALER_parse_coarse_date (
861 : const char *in,
862 : const struct TALER_AgeMask *mask,
863 : uint32_t *out)
864 : {
865 27 : struct tm date = {0};
866 27 : struct tm limit = {0};
867 : time_t seconds;
868 :
869 27 : if (NULL == in)
870 : {
871 : /* FIXME[oec]: correct behaviour? */
872 0 : *out = 0;
873 0 : return GNUNET_OK;
874 : }
875 :
876 27 : GNUNET_assert (NULL !=mask);
877 27 : GNUNET_assert (NULL !=out);
878 :
879 27 : if (NULL == strptime (in, "%Y-%m-%d", &date))
880 : {
881 20 : if (NULL == strptime (in, "%Y-%m-00", &date))
882 18 : if (NULL == strptime (in, "%Y-00-00", &date))
883 5 : return GNUNET_SYSERR;
884 : /* turns out that the day is off by one in the last two cases */
885 15 : date.tm_mday += 1;
886 : }
887 :
888 22 : seconds = timegm (&date);
889 22 : if (-1 == seconds)
890 0 : return GNUNET_SYSERR;
891 :
892 : /* calculate the limit date for the largest age group */
893 : {
894 22 : time_t l = time (NULL);
895 22 : localtime_r (&l, &limit);
896 : }
897 22 : limit.tm_year -= TALER_adult_age (mask);
898 22 : GNUNET_assert (-1 != timegm (&limit));
899 :
900 22 : if ((limit.tm_year < date.tm_year)
901 10 : || ((limit.tm_year == date.tm_year)
902 5 : && (limit.tm_mon < date.tm_mon))
903 10 : || ((limit.tm_year == date.tm_year)
904 5 : && (limit.tm_mon == date.tm_mon)
905 0 : && (limit.tm_mday < date.tm_mday)))
906 12 : *out = seconds / 60 / 60 / 24;
907 : else
908 10 : *out = 0;
909 :
910 22 : return GNUNET_OK;
911 : }
912 :
913 :
914 : /* end util/age_restriction.c */
|