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

Generated by: LCOV version 2.0-1