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