exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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 */