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