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 (21572B)


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