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 "platform.h"
22 : #include "taler_util.h"
23 : #include "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_AgeCommitmentHash *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_AgeCommitmentHash));
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_kdf (key,
139 : sizeof (*key),
140 : &seed,
141 : seed_size,
142 : "age commitment",
143 : sizeof ("age commitment") - 1,
144 : NULL, 0));
145 : /* See GNUNET_CRYPTO_ecdsa_key_create */
146 : key->d[0] &= 248;
147 : key->d[31] &= 127;
148 : key->d[31] |= 64;
149 : }
150 :
151 :
152 : #endif
153 :
154 :
155 : void
156 35 : TALER_age_restriction_commit (
157 : const struct TALER_AgeMask *mask,
158 : uint8_t age,
159 : const struct GNUNET_HashCode *seed,
160 : struct TALER_AgeCommitmentProof *ncp)
161 : {
162 : struct GNUNET_HashCode seed_i;
163 : uint8_t num_pub;
164 : uint8_t num_priv;
165 : size_t i;
166 :
167 35 : GNUNET_assert (NULL != mask);
168 35 : GNUNET_assert (NULL != seed);
169 35 : GNUNET_assert (NULL != ncp);
170 35 : GNUNET_assert (mask->bits & 1); /* first bit must have been set */
171 :
172 35 : num_pub = __builtin_popcount (mask->bits) - 1;
173 35 : num_priv = TALER_get_age_group (mask, age);
174 :
175 35 : GNUNET_assert (31 > num_priv);
176 35 : GNUNET_assert (num_priv <= num_pub);
177 :
178 35 : seed_i = *seed;
179 35 : ncp->commitment.mask.bits = mask->bits;
180 35 : ncp->commitment.num = num_pub;
181 35 : ncp->proof.num = num_priv;
182 35 : ncp->proof.privs = NULL;
183 :
184 35 : ncp->commitment.pubs = GNUNET_new_array (
185 : num_pub,
186 : struct TALER_AgeCommitmentPublicKeyP);
187 :
188 35 : if (0 < num_priv)
189 27 : ncp->proof.privs = GNUNET_new_array (
190 : num_priv,
191 : struct TALER_AgeCommitmentPrivateKeyP);
192 :
193 : /* Create as many private keys as we need and fill the rest of the
194 : * public keys with valid curve points.
195 : * We need to make sure that the public keys are proper points on the
196 : * elliptic curve, so we can't simply fill the struct with random values. */
197 280 : for (i = 0; i < num_pub; i++)
198 : {
199 245 : struct TALER_AgeCommitmentPrivateKeyP key = {0};
200 245 : struct TALER_AgeCommitmentPrivateKeyP *pkey = &key;
201 :
202 : /* Only save the private keys for age groups less than num_priv */
203 245 : if (i < num_priv)
204 138 : pkey = &ncp->proof.privs[i];
205 :
206 : #ifndef AGE_RESTRICTION_WITH_ECDSA
207 245 : GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
208 : sizeof(seed_i),
209 : &pkey->priv);
210 245 : GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
211 245 : &ncp->commitment.pubs[i].pub);
212 : #else
213 : ecdsa_create_from_seed (&seed_i,
214 : sizeof(seed_i),
215 : &pkey->priv);
216 : GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
217 : &ncp->commitment.pubs[i].pub);
218 : #endif
219 :
220 245 : seed_i.bits[0] += 1;
221 : }
222 35 : }
223 :
224 :
225 : enum GNUNET_GenericReturnValue
226 418 : TALER_age_commitment_derive (
227 : const struct TALER_AgeCommitment *orig,
228 : const struct GNUNET_HashCode *salt,
229 : struct TALER_AgeCommitment *newac)
230 : {
231 418 : GNUNET_assert (NULL != newac);
232 418 : GNUNET_assert (((int) orig->num) ==
233 : __builtin_popcount (orig->mask.bits) - 1);
234 :
235 418 : newac->mask = orig->mask;
236 418 : newac->num = orig->num;
237 418 : newac->pubs = GNUNET_new_array (
238 : newac->num,
239 : struct TALER_AgeCommitmentPublicKeyP);
240 :
241 : #ifndef AGE_RESTRICTION_WITH_ECDSA
242 : /* Derive the public keys */
243 3344 : for (size_t i = 0; i < orig->num; i++)
244 : {
245 2926 : GNUNET_CRYPTO_edx25519_public_key_derive (
246 2926 : &orig->pubs[i].pub,
247 : salt,
248 : sizeof(*salt),
249 2926 : &newac->pubs[i].pub);
250 : }
251 : #else
252 : {
253 : const char *label = GNUNET_h2s (salt);
254 :
255 : /* Derive the public keys */
256 : for (size_t i = 0; i < orig->num; i++)
257 : {
258 : GNUNET_CRYPTO_ecdsa_public_key_derive (
259 : &orig->pubs[i].pub,
260 : label,
261 : "age commitment derive",
262 : &newac->pubs[i].pub);
263 : }
264 : }
265 : #endif
266 :
267 418 : return GNUNET_OK;
268 : }
269 :
270 :
271 : enum GNUNET_GenericReturnValue
272 64 : TALER_age_commitment_derive_from_secret (
273 : const struct TALER_AgeCommitment *orig,
274 : const struct TALER_PlanchetMasterSecretP *secret,
275 : struct TALER_AgeCommitment *newac)
276 : {
277 : struct GNUNET_HashCode salt;
278 : enum GNUNET_GenericReturnValue ret;
279 :
280 64 : ret = GNUNET_CRYPTO_kdf (&salt,
281 : sizeof (salt),
282 : "age commitment",
283 : strlen ("age commitment"),
284 : secret,
285 : sizeof(*secret),
286 : NULL, 0);
287 64 : if (GNUNET_OK != ret)
288 : {
289 0 : GNUNET_break (0);
290 0 : return ret;
291 : }
292 :
293 64 : return TALER_age_commitment_derive (
294 : orig,
295 : &salt,
296 : newac);
297 : }
298 :
299 :
300 : enum GNUNET_GenericReturnValue
301 354 : TALER_age_commitment_proof_derive (
302 : const struct TALER_AgeCommitmentProof *orig,
303 : const struct GNUNET_HashCode *salt,
304 : struct TALER_AgeCommitmentProof *newacp)
305 : {
306 : enum GNUNET_GenericReturnValue ret;
307 354 : GNUNET_assert (NULL != newacp);
308 354 : GNUNET_assert (orig->proof.num <=
309 : orig->commitment.num);
310 354 : GNUNET_assert (((int) orig->commitment.num) ==
311 : __builtin_popcount (orig->commitment.mask.bits) - 1);
312 :
313 354 : ret = TALER_age_commitment_derive (
314 : &orig->commitment,
315 : salt,
316 : &newacp->commitment);
317 354 : if (GNUNET_OK != ret)
318 : {
319 0 : GNUNET_break (0);
320 0 : return ret;
321 : }
322 :
323 354 : newacp->proof.num = orig->proof.num;
324 354 : newacp->proof.privs = NULL;
325 354 : if (0 != newacp->proof.num)
326 338 : newacp->proof.privs = GNUNET_new_array (
327 : newacp->proof.num,
328 : struct TALER_AgeCommitmentPrivateKeyP);
329 :
330 : #ifndef AGE_RESTRICTION_WITH_ECDSA
331 : /* Derive the private keys */
332 1482 : for (size_t i = 0; i < orig->proof.num; i++)
333 : {
334 1128 : GNUNET_CRYPTO_edx25519_private_key_derive (
335 1128 : &orig->proof.privs[i].priv,
336 : salt,
337 : sizeof(*salt),
338 1128 : &newacp->proof.privs[i].priv);
339 : }
340 : #else
341 : {
342 : const char *label = GNUNET_h2s (salt);
343 :
344 : /* Derive the private keys */
345 : for (size_t i = 0; i < orig->proof.num; i++)
346 : {
347 : struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
348 : priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
349 : &orig->proof.privs[i].priv,
350 : label,
351 : "age commitment derive");
352 : newacp->proof.privs[i].priv = *priv;
353 : GNUNET_free (priv);
354 : }
355 : }
356 : #endif
357 :
358 354 : return GNUNET_OK;
359 : }
360 :
361 :
362 : enum GNUNET_GenericReturnValue
363 288 : TALER_age_commitment_proof_derive_from_secret (
364 : const struct TALER_AgeCommitmentProof *orig,
365 : const struct TALER_PlanchetMasterSecretP *secret,
366 : struct TALER_AgeCommitmentProof *newacp)
367 : {
368 : struct GNUNET_HashCode salt;
369 : enum GNUNET_GenericReturnValue ret;
370 :
371 288 : ret = GNUNET_CRYPTO_kdf (&salt,
372 : sizeof (salt),
373 : "age commitment",
374 : strlen ("age commitment"),
375 : secret,
376 : sizeof(*secret),
377 : NULL, 0);
378 288 : if (GNUNET_OK != ret)
379 : {
380 0 : GNUNET_break (0);
381 0 : return ret;
382 : }
383 :
384 288 : return TALER_age_commitment_proof_derive (
385 : orig,
386 : &salt,
387 : newacp);
388 : }
389 :
390 :
391 : GNUNET_NETWORK_STRUCT_BEGIN
392 :
393 : /**
394 : * Age group mask in network byte order.
395 : */
396 : struct TALER_AgeMaskNBO
397 : {
398 : uint32_t bits_nbo;
399 : };
400 :
401 : /**
402 : * Used for attestation of a particular age
403 : */
404 : struct TALER_AgeAttestationPS
405 : {
406 : /**
407 : * Purpose must be #TALER_SIGNATURE_WALLET_AGE_ATTESTATION.
408 : * (no GNUNET_PACKED here because the struct is already packed)
409 : */
410 : struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
411 :
412 : /**
413 : * Age mask that defines the underlying age groups
414 : */
415 : struct TALER_AgeMaskNBO mask GNUNET_PACKED;
416 :
417 : /**
418 : * The particular age that this attestation is for.
419 : * We use uint32_t here for alignment.
420 : */
421 : uint32_t age GNUNET_PACKED;
422 : };
423 :
424 : GNUNET_NETWORK_STRUCT_END
425 :
426 :
427 : enum GNUNET_GenericReturnValue
428 2178 : TALER_age_commitment_attest (
429 : const struct TALER_AgeCommitmentProof *cp,
430 : uint8_t age,
431 : struct TALER_AgeAttestation *attest)
432 : {
433 : uint8_t group;
434 :
435 2178 : GNUNET_assert (NULL != attest);
436 2178 : GNUNET_assert (NULL != cp);
437 :
438 2178 : group = TALER_get_age_group (&cp->commitment.mask,
439 : age);
440 :
441 2178 : GNUNET_assert (group < 32);
442 :
443 2178 : if (0 == group)
444 : {
445 : /* Age group 0 means: no attestation necessary.
446 : * We set the signature to zero and communicate success. */
447 792 : memset (attest,
448 : 0,
449 : sizeof(struct TALER_AgeAttestation));
450 792 : return GNUNET_OK;
451 : }
452 :
453 1386 : if (group > cp->proof.num)
454 585 : return GNUNET_NO;
455 :
456 : {
457 801 : struct TALER_AgeAttestationPS at = {
458 801 : .purpose.size = htonl (sizeof(at)),
459 801 : .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
460 801 : .mask.bits_nbo = htonl (cp->commitment.mask.bits),
461 801 : .age = htonl (age),
462 : };
463 :
464 : #ifndef AGE_RESTRICTION_WITH_ECDSA
465 : #define sign(a,b,c) GNUNET_CRYPTO_edx25519_sign (a,b,c)
466 : #else
467 : #define sign(a,b,c) GNUNET_CRYPTO_ecdsa_sign (a,b,c)
468 : #endif
469 801 : sign (&cp->proof.privs[group - 1].priv,
470 : &at,
471 : &attest->signature);
472 : }
473 : #undef sign
474 :
475 801 : return GNUNET_OK;
476 : }
477 :
478 :
479 : enum GNUNET_GenericReturnValue
480 1593 : TALER_age_commitment_verify (
481 : const struct TALER_AgeCommitment *comm,
482 : uint8_t age,
483 : const struct TALER_AgeAttestation *attest)
484 : {
485 : uint8_t group;
486 :
487 1593 : GNUNET_assert (NULL != attest);
488 1593 : GNUNET_assert (NULL != comm);
489 :
490 1593 : group = TALER_get_age_group (&comm->mask,
491 : age);
492 :
493 1593 : GNUNET_assert (group < 32);
494 :
495 : /* Age group 0 means: no attestation necessary. */
496 1593 : if (0 == group)
497 792 : return GNUNET_OK;
498 :
499 801 : if (group > comm->num)
500 : {
501 0 : GNUNET_break_op (0);
502 0 : return GNUNET_NO;
503 : }
504 :
505 : {
506 801 : struct TALER_AgeAttestationPS at = {
507 801 : .purpose.size = htonl (sizeof(at)),
508 801 : .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
509 801 : .mask.bits_nbo = htonl (comm->mask.bits),
510 801 : .age = htonl (age),
511 : };
512 :
513 : #ifndef AGE_RESTRICTION_WITH_ECDSA
514 : #define verify(a,b,c,d) GNUNET_CRYPTO_edx25519_verify ((a),(b),(c),(d))
515 : #else
516 : #define verify(a,b,c,d) GNUNET_CRYPTO_ecdsa_verify ((a),(b),(c),(d))
517 : #endif
518 801 : return verify (TALER_SIGNATURE_WALLET_AGE_ATTESTATION,
519 : &at,
520 : &attest->signature,
521 : &comm->pubs[group - 1].pub);
522 : }
523 : #undef verify
524 : }
525 :
526 :
527 : void
528 64 : TALER_age_commitment_free (
529 : struct TALER_AgeCommitment *commitment)
530 : {
531 64 : if (NULL == commitment)
532 0 : return;
533 :
534 64 : if (NULL != commitment->pubs)
535 : {
536 64 : GNUNET_free (commitment->pubs);
537 64 : commitment->pubs = NULL;
538 : }
539 : }
540 :
541 :
542 : void
543 0 : TALER_age_proof_free (
544 : struct TALER_AgeProof *proof)
545 : {
546 0 : if (NULL == proof)
547 0 : return;
548 :
549 0 : if (NULL != proof->privs)
550 : {
551 0 : GNUNET_CRYPTO_zero_keys (
552 0 : proof->privs,
553 0 : sizeof(*proof->privs) * proof->num);
554 :
555 0 : GNUNET_free (proof->privs);
556 0 : proof->privs = NULL;
557 : }
558 : }
559 :
560 :
561 : void
562 817 : TALER_age_commitment_proof_free (
563 : struct TALER_AgeCommitmentProof *acp)
564 : {
565 817 : if (NULL == acp)
566 264 : return;
567 :
568 553 : if (NULL != acp->proof.privs)
569 : {
570 460 : GNUNET_CRYPTO_zero_keys (
571 460 : acp->proof.privs,
572 460 : sizeof(*acp->proof.privs) * acp->proof.num);
573 :
574 460 : GNUNET_free (acp->proof.privs);
575 460 : acp->proof.privs = NULL;
576 : }
577 :
578 553 : if (NULL != acp->commitment.pubs)
579 : {
580 489 : GNUNET_free (acp->commitment.pubs);
581 489 : acp->commitment.pubs = NULL;
582 : }
583 : }
584 :
585 :
586 : struct TALER_AgeCommitmentProof *
587 32 : TALER_age_commitment_proof_duplicate (
588 : const struct TALER_AgeCommitmentProof *acp)
589 : {
590 : struct TALER_AgeCommitmentProof *nacp;
591 :
592 32 : GNUNET_assert (NULL != acp);
593 32 : GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 ==
594 : (int) acp->commitment.num);
595 :
596 32 : nacp = GNUNET_new (struct TALER_AgeCommitmentProof);
597 :
598 32 : TALER_age_commitment_proof_deep_copy (nacp, acp);
599 32 : return nacp;
600 : }
601 :
602 :
603 : struct TALER_AgeCommitment *
604 0 : TALER_age_commitment_duplicate (
605 : const struct TALER_AgeCommitment *ac)
606 : {
607 : struct TALER_AgeCommitment *nac;
608 :
609 0 : GNUNET_assert (NULL != ac);
610 0 : GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 ==
611 : (int) ac->num);
612 :
613 0 : nac = GNUNET_new (struct TALER_AgeCommitment);
614 0 : TALER_age_commitment_deep_copy (nac, ac);
615 0 : return nac;
616 : }
617 :
618 :
619 : void
620 49 : TALER_age_commitment_proof_deep_copy (
621 : struct TALER_AgeCommitmentProof *nacp,
622 : const struct TALER_AgeCommitmentProof *acp)
623 : {
624 49 : GNUNET_assert (NULL != acp);
625 49 : GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 ==
626 : (int) acp->commitment.num);
627 :
628 49 : *nacp = *acp;
629 49 : nacp->commitment.pubs =
630 49 : GNUNET_new_array (acp->commitment.num,
631 : struct TALER_AgeCommitmentPublicKeyP);
632 49 : nacp->proof.privs =
633 49 : GNUNET_new_array (acp->proof.num,
634 : struct TALER_AgeCommitmentPrivateKeyP);
635 :
636 392 : for (size_t i = 0; i < acp->commitment.num; i++)
637 343 : nacp->commitment.pubs[i] = acp->commitment.pubs[i];
638 :
639 181 : for (size_t i = 0; i < acp->proof.num; i++)
640 132 : nacp->proof.privs[i] = acp->proof.privs[i];
641 49 : }
642 :
643 :
644 : void
645 0 : TALER_age_commitment_deep_copy (
646 : struct TALER_AgeCommitment *nac,
647 : const struct TALER_AgeCommitment *ac)
648 : {
649 0 : GNUNET_assert (NULL != ac);
650 0 : GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 ==
651 : (int) ac->num);
652 :
653 0 : *nac = *ac;
654 0 : nac->pubs =
655 0 : GNUNET_new_array (ac->num,
656 : struct TALER_AgeCommitmentPublicKeyP);
657 :
658 0 : for (size_t i = 0; i < ac->num; i++)
659 0 : nac->pubs[i] = ac->pubs[i];
660 :
661 0 : }
662 :
663 :
664 : enum GNUNET_GenericReturnValue
665 11 : TALER_JSON_parse_age_groups (const json_t *root,
666 : struct TALER_AgeMask *mask)
667 : {
668 : enum GNUNET_GenericReturnValue ret;
669 : const char *str;
670 : struct GNUNET_JSON_Specification spec[] = {
671 11 : GNUNET_JSON_spec_string ("age_groups",
672 : &str),
673 11 : GNUNET_JSON_spec_end ()
674 : };
675 :
676 11 : ret = GNUNET_JSON_parse (root,
677 : spec,
678 : NULL,
679 : NULL);
680 11 : if (GNUNET_OK == ret)
681 11 : TALER_parse_age_group_string (str, mask);
682 :
683 11 : GNUNET_JSON_parse_free (spec);
684 :
685 11 : return ret;
686 : }
687 :
688 :
689 : enum GNUNET_GenericReturnValue
690 61 : TALER_parse_age_group_string (
691 : const char *groups,
692 : struct TALER_AgeMask *mask)
693 : {
694 :
695 61 : const char *pos = groups;
696 61 : unsigned int prev = 0;
697 61 : unsigned int val = 0;
698 : char c;
699 :
700 : /* reset mask */
701 61 : mask->bits = 0;
702 :
703 1220 : while (*pos)
704 : {
705 1159 : c = *pos++;
706 1159 : if (':' == c)
707 : {
708 366 : if (prev >= val)
709 0 : return GNUNET_SYSERR;
710 :
711 366 : mask->bits |= 1 << val;
712 366 : prev = val;
713 366 : val = 0;
714 366 : continue;
715 : }
716 :
717 793 : if ('0'>c || '9'<c)
718 0 : return GNUNET_SYSERR;
719 :
720 793 : val = 10 * val + c - '0';
721 :
722 793 : if (0>=val || 32<=val)
723 0 : return GNUNET_SYSERR;
724 : }
725 :
726 61 : if (32<=val || prev>=val)
727 0 : return GNUNET_SYSERR;
728 :
729 61 : mask->bits |= (1 << val);
730 61 : mask->bits |= 1; // mark zeroth group, too
731 :
732 61 : return GNUNET_OK;
733 : }
734 :
735 :
736 : const char *
737 702 : TALER_age_mask_to_string (
738 : const struct TALER_AgeMask *mask)
739 : {
740 : static char buf[256] = {0};
741 702 : uint32_t bits = mask->bits;
742 702 : unsigned int n = 0;
743 702 : char *pos = buf;
744 :
745 702 : memset (buf, 0, sizeof(buf));
746 :
747 16093 : while (bits != 0)
748 : {
749 15391 : bits >>= 1;
750 15391 : n++;
751 15391 : if (0 == (bits & 1))
752 : {
753 10504 : continue;
754 : }
755 :
756 4887 : if (n > 9)
757 : {
758 4186 : *(pos++) = '0' + n / 10;
759 : }
760 4887 : *(pos++) = '0' + n % 10;
761 :
762 4887 : if (0 != (bits >> 1))
763 : {
764 4185 : *(pos++) = ':';
765 : }
766 : }
767 702 : return buf;
768 : }
769 :
770 :
771 : void
772 51 : TALER_age_restriction_from_secret (
773 : const struct TALER_PlanchetMasterSecretP *secret,
774 : const struct TALER_AgeMask *mask,
775 : const uint8_t max_age,
776 : struct TALER_AgeCommitmentProof *ncp)
777 : {
778 51 : struct GNUNET_HashCode seed_i = {0};
779 : uint8_t num_pub;
780 : uint8_t num_priv;
781 :
782 51 : GNUNET_assert (NULL != mask);
783 51 : GNUNET_assert (NULL != secret);
784 51 : GNUNET_assert (NULL != ncp);
785 51 : GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
786 :
787 51 : num_pub = __builtin_popcount (mask->bits) - 1;
788 51 : num_priv = TALER_get_age_group (mask, max_age);
789 :
790 51 : GNUNET_assert (31 > num_priv);
791 51 : GNUNET_assert (num_priv <= num_pub);
792 :
793 51 : ncp->commitment.mask.bits = mask->bits;
794 51 : ncp->commitment.num = num_pub;
795 51 : ncp->proof.num = num_priv;
796 51 : ncp->proof.privs = NULL;
797 51 : ncp->commitment.pubs = GNUNET_new_array (
798 : num_pub,
799 : struct TALER_AgeCommitmentPublicKeyP);
800 51 : if (0 < num_priv)
801 46 : ncp->proof.privs = GNUNET_new_array (
802 : num_priv,
803 : struct TALER_AgeCommitmentPrivateKeyP);
804 :
805 : /* Create as many private keys as allow with max_age and derive the
806 : * corresponding public keys. The rest of the needed public keys are created
807 : * by scalar multiplication with the TALER_age_commitment_base_public_key. */
808 408 : for (size_t i = 0; i < num_pub; i++)
809 : {
810 : enum GNUNET_GenericReturnValue ret;
811 357 : const char *label = i < num_priv ? "age-commitment" : "age-factor";
812 :
813 357 : ret = GNUNET_CRYPTO_kdf (&seed_i, sizeof(seed_i),
814 : secret, sizeof(*secret),
815 : label, strlen (label),
816 : &i, sizeof(i),
817 : NULL, 0);
818 357 : GNUNET_assert (GNUNET_OK == ret);
819 :
820 : /* Only generate and save the private keys and public keys for age groups
821 : * less than num_priv */
822 357 : if (i < num_priv)
823 : {
824 96 : struct TALER_AgeCommitmentPrivateKeyP *pkey = &ncp->proof.privs[i];
825 :
826 : #ifndef AGE_RESTRICTION_WITH_ECDSA
827 96 : GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
828 : sizeof(seed_i),
829 : &pkey->priv);
830 96 : GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
831 96 : &ncp->commitment.pubs[i].pub);
832 : #else
833 : ecdsa_create_from_seed (&seed_i,
834 : sizeof(seed_i),
835 : &pkey->priv);
836 : GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
837 : &ncp->commitment.pubs[i].pub);
838 : #endif
839 : }
840 : else
841 : {
842 : /* For all indices larger than num_priv, derive a public key from
843 : * TALER_age_commitment_base_public_key by scalar multiplication */
844 : #ifndef AGE_RESTRICTION_WITH_ECDSA
845 261 : GNUNET_CRYPTO_edx25519_public_key_derive (
846 : &TALER_age_commitment_base_public_key,
847 : &seed_i,
848 : sizeof(seed_i),
849 261 : &ncp->commitment.pubs[i].pub);
850 : #else
851 :
852 : GNUNET_CRYPTO_ecdsa_public_key_derive (
853 : &TALER_age_commitment_base_public_key,
854 : GNUNET_h2s (&seed_i),
855 : "age withdraw",
856 : &ncp->commitment.pubs[i].pub);
857 : #endif
858 : }
859 : }
860 51 : }
861 :
862 :
863 : enum GNUNET_GenericReturnValue
864 27 : TALER_parse_coarse_date (
865 : const char *in,
866 : const struct TALER_AgeMask *mask,
867 : uint32_t *out)
868 : {
869 27 : struct tm date = {0};
870 27 : struct tm limit = {0};
871 : time_t seconds;
872 :
873 27 : if (NULL == in)
874 : {
875 : /* FIXME[oec]: correct behaviour? */
876 0 : *out = 0;
877 0 : return GNUNET_OK;
878 : }
879 :
880 27 : GNUNET_assert (NULL !=mask);
881 27 : GNUNET_assert (NULL !=out);
882 :
883 27 : if (NULL == strptime (in, "%Y-%m-%d", &date))
884 : {
885 20 : if (NULL == strptime (in, "%Y-%m-00", &date))
886 18 : if (NULL == strptime (in, "%Y-00-00", &date))
887 5 : return GNUNET_SYSERR;
888 : /* turns out that the day is off by one in the last two cases */
889 15 : date.tm_mday += 1;
890 : }
891 :
892 22 : seconds = timegm (&date);
893 22 : if (-1 == seconds)
894 0 : return GNUNET_SYSERR;
895 :
896 : /* calculate the limit date for the largest age group */
897 : {
898 22 : time_t l = time (NULL);
899 22 : localtime_r (&l, &limit);
900 : }
901 22 : limit.tm_year -= TALER_adult_age (mask);
902 22 : GNUNET_assert (-1 != timegm (&limit));
903 :
904 22 : if ((limit.tm_year < date.tm_year)
905 5 : || ((limit.tm_year == date.tm_year)
906 0 : && (limit.tm_mon < date.tm_mon))
907 5 : || ((limit.tm_year == date.tm_year)
908 0 : && (limit.tm_mon == date.tm_mon)
909 0 : && (limit.tm_mday < date.tm_mday)))
910 17 : *out = seconds / 60 / 60 / 24;
911 : else
912 5 : *out = 0;
913 :
914 22 : return GNUNET_OK;
915 : }
916 :
917 :
918 : /* end util/age_restriction.c */
|