LCOV - code coverage report
Current view: top level - util - age_restriction.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 86.3 % 299 258
Test Date: 2026-03-27 15:46:29 Functions: 86.4 % 22 19

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

Generated by: LCOV version 2.0-1