age_restriction.c (22280B)
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 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 GNUNET_assert (NULL != ahash); 50 if (NULL == commitment) 51 { 52 memset (ahash, 0, sizeof(struct TALER_AgeCommitmentHashP)); 53 return; 54 } 55 56 GNUNET_assert (__builtin_popcount (commitment->mask.bits) - 1 == 57 (int) commitment->num); 58 59 hash_context = GNUNET_CRYPTO_hash_context_start (); 60 61 for (size_t i = 0; i < commitment->num; i++) 62 { 63 GNUNET_CRYPTO_hash_context_read (hash_context, 64 &commitment->pubs[i], 65 sizeof(commitment->pubs[i])); 66 } 67 68 GNUNET_CRYPTO_hash_context_finish (hash_context, 69 &hash); 70 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 TALER_get_age_group ( 81 const struct TALER_AgeMask *mask, 82 uint8_t age) 83 { 84 uint32_t m = mask->bits; 85 uint8_t i = 0; 86 87 while (m > 0) 88 { 89 if (0 >= age) 90 break; 91 m = m >> 1; 92 i += m & 1; 93 age--; 94 } 95 return i; 96 } 97 98 99 uint8_t 100 TALER_get_lowest_age ( 101 const struct TALER_AgeMask *mask, 102 uint8_t age) 103 { 104 uint32_t m = mask->bits; 105 uint8_t group = TALER_get_age_group (mask, age); 106 uint8_t lowest = 0; 107 108 while (group > 0) 109 { 110 m = m >> 1; 111 if (m & 1) 112 group--; 113 lowest++; 114 } 115 116 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 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 GNUNET_assert (NULL != mask); 168 GNUNET_assert (NULL != seed); 169 GNUNET_assert (NULL != ncp); 170 GNUNET_assert (mask->bits & 1); /* first bit must have been set */ 171 172 num_pub = __builtin_popcount (mask->bits) - 1; 173 num_priv = TALER_get_age_group (mask, age); 174 175 GNUNET_assert (31 > num_priv); 176 GNUNET_assert (num_priv <= num_pub); 177 178 seed_i = *seed; 179 ncp->commitment.mask.bits = mask->bits; 180 ncp->commitment.num = num_pub; 181 ncp->proof.num = num_priv; 182 ncp->proof.privs = NULL; 183 184 ncp->commitment.pubs = GNUNET_new_array ( 185 num_pub, 186 struct TALER_AgeCommitmentPublicKeyP); 187 188 if (0 < num_priv) 189 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 for (i = 0; i < num_pub; i++) 198 { 199 struct TALER_AgeCommitmentPrivateKeyP key = {0}; 200 struct TALER_AgeCommitmentPrivateKeyP *pkey = &key; 201 202 /* Only save the private keys for age groups less than num_priv */ 203 if (i < num_priv) 204 pkey = &ncp->proof.privs[i]; 205 206 #ifndef AGE_RESTRICTION_WITH_ECDSA 207 GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i, 208 sizeof(seed_i), 209 &pkey->priv); 210 GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv, 211 &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 seed_i.bits[0] += 1; 221 } 222 } 223 224 225 enum GNUNET_GenericReturnValue 226 TALER_age_commitment_derive ( 227 const struct TALER_AgeCommitment *orig, 228 const struct GNUNET_HashCode *salt, 229 struct TALER_AgeCommitment *newac) 230 { 231 GNUNET_assert (NULL != newac); 232 GNUNET_assert (((int) orig->num) == 233 __builtin_popcount (orig->mask.bits) - 1); 234 235 newac->mask = orig->mask; 236 newac->num = orig->num; 237 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 for (size_t i = 0; i < orig->num; i++) 244 { 245 GNUNET_CRYPTO_edx25519_public_key_derive ( 246 &orig->pubs[i].pub, 247 salt, 248 sizeof(*salt), 249 &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 return GNUNET_OK; 268 } 269 270 271 enum GNUNET_GenericReturnValue 272 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 ret = GNUNET_CRYPTO_kdf (&salt, 281 sizeof (salt), 282 "age commitment", 283 strlen ("age commitment"), 284 secret, 285 sizeof(*secret), 286 NULL, 0); 287 if (GNUNET_OK != ret) 288 { 289 GNUNET_break (0); 290 return ret; 291 } 292 293 return TALER_age_commitment_derive ( 294 orig, 295 &salt, 296 newac); 297 } 298 299 300 enum GNUNET_GenericReturnValue 301 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 GNUNET_assert (NULL != newacp); 308 GNUNET_assert (orig->proof.num <= 309 orig->commitment.num); 310 GNUNET_assert (((int) orig->commitment.num) == 311 __builtin_popcount (orig->commitment.mask.bits) - 1); 312 313 ret = TALER_age_commitment_derive ( 314 &orig->commitment, 315 salt, 316 &newacp->commitment); 317 if (GNUNET_OK != ret) 318 { 319 GNUNET_break (0); 320 return ret; 321 } 322 323 newacp->proof.num = orig->proof.num; 324 newacp->proof.privs = NULL; 325 if (0 != newacp->proof.num) 326 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 for (size_t i = 0; i < orig->proof.num; i++) 333 { 334 GNUNET_CRYPTO_edx25519_private_key_derive ( 335 &orig->proof.privs[i].priv, 336 salt, 337 sizeof(*salt), 338 &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 return GNUNET_OK; 359 } 360 361 362 enum GNUNET_GenericReturnValue 363 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 ret = GNUNET_CRYPTO_kdf (&salt, 372 sizeof (salt), 373 "age commitment", 374 strlen ("age commitment"), 375 secret, 376 sizeof(*secret), 377 NULL, 0); 378 if (GNUNET_OK != ret) 379 { 380 GNUNET_break (0); 381 return ret; 382 } 383 384 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_AgeAttestationPPS 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_SignaturePurpose 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 TALER_age_commitment_attest ( 429 const struct TALER_AgeCommitmentProof *cp, 430 uint8_t age, 431 struct TALER_AgeAttestationP *attest) 432 { 433 uint8_t group; 434 435 GNUNET_assert (NULL != attest); 436 GNUNET_assert (NULL != cp); 437 438 group = TALER_get_age_group (&cp->commitment.mask, 439 age); 440 441 GNUNET_assert (group < 32); 442 443 if (0 == group) 444 { 445 /* Age group 0 means: no attestation necessary. 446 * We set the signature to zero and communicate success. */ 447 memset (attest, 448 0, 449 sizeof(struct TALER_AgeAttestationP)); 450 return GNUNET_OK; 451 } 452 453 if (group > cp->proof.num) 454 return GNUNET_NO; 455 456 { 457 struct TALER_AgeAttestationPPS at = { 458 .purpose.size = htonl (sizeof(at)), 459 .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION), 460 .mask.bits_nbo = htonl (cp->commitment.mask.bits), 461 .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 sign (&cp->proof.privs[group - 1].priv, 470 &at, 471 &attest->signature); 472 } 473 #undef sign 474 475 return GNUNET_OK; 476 } 477 478 479 enum GNUNET_GenericReturnValue 480 TALER_age_commitment_verify ( 481 const struct TALER_AgeCommitment *comm, 482 uint8_t age, 483 const struct TALER_AgeAttestationP *attest) 484 { 485 uint8_t group; 486 487 GNUNET_assert (NULL != attest); 488 GNUNET_assert (NULL != comm); 489 490 group = TALER_get_age_group (&comm->mask, 491 age); 492 493 GNUNET_assert (group < 32); 494 495 /* Age group 0 means: no attestation necessary. */ 496 if (0 == group) 497 return GNUNET_OK; 498 499 if (group > comm->num) 500 { 501 GNUNET_break_op (0); 502 return GNUNET_NO; 503 } 504 505 { 506 struct TALER_AgeAttestationPPS at = { 507 .purpose.size = htonl (sizeof(at)), 508 .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION), 509 .mask.bits_nbo = htonl (comm->mask.bits), 510 .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 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 TALER_age_commitment_free ( 529 struct TALER_AgeCommitment *commitment) 530 { 531 if (NULL == commitment) 532 return; 533 534 if (NULL != commitment->pubs) 535 { 536 GNUNET_free (commitment->pubs); 537 commitment->pubs = NULL; 538 } 539 } 540 541 542 void 543 TALER_age_proof_free ( 544 struct TALER_AgeProof *proof) 545 { 546 if (NULL == proof) 547 return; 548 549 if (NULL != proof->privs) 550 { 551 GNUNET_CRYPTO_zero_keys ( 552 proof->privs, 553 sizeof(*proof->privs) * proof->num); 554 555 GNUNET_free (proof->privs); 556 proof->privs = NULL; 557 } 558 } 559 560 561 void 562 TALER_age_commitment_proof_free ( 563 struct TALER_AgeCommitmentProof *acp) 564 { 565 if (NULL == acp) 566 return; 567 568 if (NULL != acp->proof.privs) 569 { 570 GNUNET_CRYPTO_zero_keys ( 571 acp->proof.privs, 572 sizeof(*acp->proof.privs) * acp->proof.num); 573 574 GNUNET_free (acp->proof.privs); 575 acp->proof.privs = NULL; 576 } 577 578 if (NULL != acp->commitment.pubs) 579 { 580 GNUNET_free (acp->commitment.pubs); 581 acp->commitment.pubs = NULL; 582 } 583 } 584 585 586 struct TALER_AgeCommitmentProof * 587 TALER_age_commitment_proof_duplicate ( 588 const struct TALER_AgeCommitmentProof *acp) 589 { 590 struct TALER_AgeCommitmentProof *nacp; 591 592 GNUNET_assert (NULL != acp); 593 GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 == 594 (int) acp->commitment.num); 595 596 nacp = GNUNET_new (struct TALER_AgeCommitmentProof); 597 598 TALER_age_commitment_proof_deep_copy (nacp, acp); 599 return nacp; 600 } 601 602 603 struct TALER_AgeCommitment * 604 TALER_age_commitment_duplicate ( 605 const struct TALER_AgeCommitment *ac) 606 { 607 struct TALER_AgeCommitment *nac; 608 609 GNUNET_assert (NULL != ac); 610 GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 == 611 (int) ac->num); 612 613 nac = GNUNET_new (struct TALER_AgeCommitment); 614 TALER_age_commitment_deep_copy (nac, ac); 615 return nac; 616 } 617 618 619 void 620 TALER_age_commitment_proof_deep_copy ( 621 struct TALER_AgeCommitmentProof *nacp, 622 const struct TALER_AgeCommitmentProof *acp) 623 { 624 GNUNET_assert (NULL != acp); 625 GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 == 626 (int) acp->commitment.num); 627 628 *nacp = *acp; 629 nacp->commitment.pubs = 630 GNUNET_new_array (acp->commitment.num, 631 struct TALER_AgeCommitmentPublicKeyP); 632 nacp->proof.privs = 633 GNUNET_new_array (acp->proof.num, 634 struct TALER_AgeCommitmentPrivateKeyP); 635 636 for (size_t i = 0; i < acp->commitment.num; i++) 637 nacp->commitment.pubs[i] = acp->commitment.pubs[i]; 638 639 for (size_t i = 0; i < acp->proof.num; i++) 640 nacp->proof.privs[i] = acp->proof.privs[i]; 641 } 642 643 644 void 645 TALER_age_commitment_deep_copy ( 646 struct TALER_AgeCommitment *nac, 647 const struct TALER_AgeCommitment *ac) 648 { 649 GNUNET_assert (NULL != ac); 650 GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 == 651 (int) ac->num); 652 653 *nac = *ac; 654 nac->pubs = 655 GNUNET_new_array (ac->num, 656 struct TALER_AgeCommitmentPublicKeyP); 657 658 for (size_t i = 0; i < ac->num; i++) 659 nac->pubs[i] = ac->pubs[i]; 660 661 } 662 663 664 enum GNUNET_GenericReturnValue 665 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 GNUNET_JSON_spec_string ("age_groups", 672 &str), 673 GNUNET_JSON_spec_end () 674 }; 675 676 ret = GNUNET_JSON_parse (root, 677 spec, 678 NULL, 679 NULL); 680 if (GNUNET_OK == ret) 681 TALER_parse_age_group_string (str, mask); 682 683 GNUNET_JSON_parse_free (spec); 684 685 return ret; 686 } 687 688 689 enum GNUNET_GenericReturnValue 690 TALER_parse_age_group_string ( 691 const char *groups, 692 struct TALER_AgeMask *mask) 693 { 694 695 const char *pos = groups; 696 unsigned int prev = 0; 697 unsigned int val = 0; 698 char c; 699 700 /* reset mask */ 701 mask->bits = 0; 702 703 while (*pos) 704 { 705 c = *pos++; 706 if (':' == c) 707 { 708 if (prev >= val) 709 return GNUNET_SYSERR; 710 711 mask->bits |= 1 << val; 712 prev = val; 713 val = 0; 714 continue; 715 } 716 717 if ('0'>c || '9'<c) 718 return GNUNET_SYSERR; 719 720 val = 10 * val + c - '0'; 721 722 if (0>=val || 32<=val) 723 return GNUNET_SYSERR; 724 } 725 726 if (32<=val || prev>=val) 727 return GNUNET_SYSERR; 728 729 mask->bits |= (1 << val); 730 mask->bits |= 1; // mark zeroth group, too 731 732 return GNUNET_OK; 733 } 734 735 736 const char * 737 TALER_age_mask_to_string ( 738 const struct TALER_AgeMask *mask) 739 { 740 static char buf[256] = {0}; 741 uint32_t bits = mask->bits; 742 unsigned int n = 0; 743 char *pos = buf; 744 745 memset (buf, 0, sizeof(buf)); 746 747 while (bits != 0) 748 { 749 bits >>= 1; 750 n++; 751 if (0 == (bits & 1)) 752 { 753 continue; 754 } 755 756 if (n > 9) 757 { 758 *(pos++) = '0' + n / 10; 759 } 760 *(pos++) = '0' + n % 10; 761 762 if (0 != (bits >> 1)) 763 { 764 *(pos++) = ':'; 765 } 766 } 767 return buf; 768 } 769 770 771 void 772 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 struct GNUNET_HashCode seed_i = {0}; 779 uint8_t num_pub; 780 uint8_t num_priv; 781 782 GNUNET_assert (NULL != mask); 783 GNUNET_assert (NULL != secret); 784 GNUNET_assert (NULL != ncp); 785 GNUNET_assert (mask->bits & 1); /* fist bit must have been set */ 786 787 num_pub = __builtin_popcount (mask->bits) - 1; 788 num_priv = TALER_get_age_group (mask, max_age); 789 790 GNUNET_assert (31 > num_priv); 791 GNUNET_assert (num_priv <= num_pub); 792 793 ncp->commitment.mask.bits = mask->bits; 794 ncp->commitment.num = num_pub; 795 ncp->proof.num = num_priv; 796 ncp->proof.privs = NULL; 797 ncp->commitment.pubs = GNUNET_new_array ( 798 num_pub, 799 struct TALER_AgeCommitmentPublicKeyP); 800 if (0 < num_priv) 801 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 for (size_t i = 0; i < num_pub; i++) 809 { 810 enum GNUNET_GenericReturnValue ret; 811 const char *label = i < num_priv ? "age-commitment" : "age-factor"; 812 813 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 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 if (i < num_priv) 823 { 824 struct TALER_AgeCommitmentPrivateKeyP *pkey = &ncp->proof.privs[i]; 825 826 #ifndef AGE_RESTRICTION_WITH_ECDSA 827 GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i, 828 sizeof(seed_i), 829 &pkey->priv); 830 GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv, 831 &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 GNUNET_CRYPTO_edx25519_public_key_derive ( 846 &TALER_age_commitment_base_public_key, 847 &seed_i, 848 sizeof(seed_i), 849 &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 } 861 862 863 enum GNUNET_GenericReturnValue 864 TALER_parse_coarse_date ( 865 const char *in, 866 const struct TALER_AgeMask *mask, 867 uint32_t *out) 868 { 869 struct tm date = {0}; 870 struct tm limit = {0}; 871 time_t seconds; 872 873 if (NULL == in) 874 { 875 /* FIXME[oec]: correct behaviour? */ 876 *out = 0; 877 return GNUNET_OK; 878 } 879 880 GNUNET_assert (NULL !=mask); 881 GNUNET_assert (NULL !=out); 882 883 if (NULL == strptime (in, "%Y-%m-%d", &date)) 884 { 885 if (NULL == strptime (in, "%Y-%m-00", &date)) 886 if (NULL == strptime (in, "%Y-00-00", &date)) 887 return GNUNET_SYSERR; 888 /* turns out that the day is off by one in the last two cases */ 889 date.tm_mday += 1; 890 } 891 892 seconds = timegm (&date); 893 if (-1 == seconds) 894 return GNUNET_SYSERR; 895 896 /* calculate the limit date for the largest age group */ 897 { 898 time_t l = time (NULL); 899 localtime_r (&l, &limit); 900 } 901 limit.tm_year -= TALER_adult_age (mask); 902 GNUNET_assert (-1 != timegm (&limit)); 903 904 if ((limit.tm_year < date.tm_year) 905 || ((limit.tm_year == date.tm_year) 906 && (limit.tm_mon < date.tm_mon)) 907 || ((limit.tm_year == date.tm_year) 908 && (limit.tm_mon == date.tm_mon) 909 && (limit.tm_mday < date.tm_mday))) 910 *out = seconds / 60 / 60 / 24; 911 else 912 *out = 0; 913 914 return GNUNET_OK; 915 } 916 917 918 /* end util/age_restriction.c */