LCOV - code coverage report
Current view: top level - util - age_restriction.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 86.0 % 292 251
Test Date: 2026-09-11 18:55:36 Functions: 85.7 % 21 18

            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 "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          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              : uint8_t
      77         6174 : TALER_get_age_group (
      78              :   const struct TALER_AgeMask *mask,
      79              :   uint8_t age)
      80              : {
      81         6174 :   uint32_t m = mask->bits;
      82         6174 :   uint8_t i = 0;
      83              : 
      84        68471 :   while (m > 0)
      85              :   {
      86        68430 :     if (0 >= age)
      87         6133 :       break;
      88        62297 :     m = m >> 1;
      89        62297 :     i += m & 1;
      90        62297 :     age--;
      91              :   }
      92         6174 :   return i;
      93              : }
      94              : 
      95              : 
      96              : uint8_t
      97           32 : TALER_get_lowest_age (
      98              :   const struct TALER_AgeMask *mask,
      99              :   uint8_t age)
     100              : {
     101           32 :   uint32_t m = mask->bits;
     102           32 :   uint8_t group = TALER_get_age_group (mask, age);
     103           32 :   uint8_t lowest = 0;
     104              : 
     105          354 :   while (group > 0)
     106              :   {
     107          322 :     m = m >> 1;
     108          322 :     if (m & 1)
     109           78 :       group--;
     110          322 :     lowest++;
     111              :   }
     112              : 
     113           32 :   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           35 : 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           35 :   GNUNET_assert (NULL != mask);
     164           35 :   GNUNET_assert (NULL != seed);
     165           35 :   GNUNET_assert (NULL != ncp);
     166           35 :   GNUNET_assert (mask->bits & 1); /* first bit must have been set */
     167              : 
     168           35 :   num_pub = __builtin_popcount (mask->bits) - 1;
     169           35 :   num_priv = TALER_get_age_group (mask, age);
     170              : 
     171           35 :   GNUNET_assert (31 > num_priv);
     172           35 :   GNUNET_assert (num_priv <= num_pub);
     173              : 
     174           35 :   seed_i = *seed;
     175           35 :   ncp->commitment.mask.bits = mask->bits;
     176           35 :   ncp->commitment.num = num_pub;
     177           35 :   ncp->proof.num = num_priv;
     178           35 :   ncp->proof.privs = NULL;
     179              : 
     180           35 :   ncp->commitment.pubs = GNUNET_new_array (
     181              :     num_pub,
     182              :     struct TALER_AgeCommitmentPublicKeyP);
     183              : 
     184           35 :   if (0 < num_priv)
     185           27 :     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          280 :   for (i = 0; i < num_pub; i++)
     194              :   {
     195          245 :     struct TALER_AgeCommitmentPrivateKeyP key = {0};
     196          245 :     struct TALER_AgeCommitmentPrivateKeyP *pkey = &key;
     197              : 
     198              :     /* Only save the private keys for age groups less than num_priv */
     199          245 :     if (i < num_priv)
     200          138 :       pkey = &ncp->proof.privs[i];
     201              : 
     202              : #ifndef AGE_RESTRICTION_WITH_ECDSA
     203          245 :     GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
     204              :                                                  sizeof(seed_i),
     205              :                                                  &pkey->priv);
     206          245 :     GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
     207          245 :                                            &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          245 :     seed_i.bits[0] += 1;
     217              :   }
     218           35 : }
     219              : 
     220              : 
     221              : enum GNUNET_GenericReturnValue
     222          418 : TALER_age_commitment_derive (
     223              :   const struct TALER_AgeCommitment *orig,
     224              :   const struct GNUNET_HashCode *salt,
     225              :   struct TALER_AgeCommitment *newac)
     226              : {
     227          418 :   GNUNET_assert (NULL != newac);
     228          418 :   GNUNET_assert (((int) orig->num) ==
     229              :                  __builtin_popcount (orig->mask.bits) - 1);
     230              : 
     231          418 :   newac->mask = orig->mask;
     232          418 :   newac->num = orig->num;
     233          418 :   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         3344 :   for (size_t i = 0; i < orig->num; i++)
     240              :   {
     241         2926 :     GNUNET_CRYPTO_edx25519_public_key_derive (
     242         2926 :       &orig->pubs[i].pub,
     243              :       salt,
     244              :       sizeof(*salt),
     245         2926 :       &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          418 :   return GNUNET_OK;
     264              : }
     265              : 
     266              : 
     267              : enum GNUNET_GenericReturnValue
     268           64 : 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           64 :   ret = GNUNET_CRYPTO_hkdf_gnunet (&salt,
     277              :                                    sizeof (salt),
     278              :                                    "age commitment",
     279              :                                    strlen ("age commitment"),
     280              :                                    secret,
     281              :                                    sizeof(*secret));
     282           64 :   if (GNUNET_OK != ret)
     283              :   {
     284            0 :     GNUNET_break (0);
     285            0 :     return ret;
     286              :   }
     287              : 
     288           64 :   return TALER_age_commitment_derive (
     289              :     orig,
     290              :     &salt,
     291              :     newac);
     292              : }
     293              : 
     294              : 
     295              : enum GNUNET_GenericReturnValue
     296          354 : 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          354 :   GNUNET_assert (NULL != newacp);
     303          354 :   GNUNET_assert (orig->proof.num <=
     304              :                  orig->commitment.num);
     305          354 :   GNUNET_assert (((int) orig->commitment.num) ==
     306              :                  __builtin_popcount (orig->commitment.mask.bits) - 1);
     307              : 
     308          354 :   ret = TALER_age_commitment_derive (
     309              :     &orig->commitment,
     310              :     salt,
     311              :     &newacp->commitment);
     312          354 :   if (GNUNET_OK != ret)
     313              :   {
     314            0 :     GNUNET_break (0);
     315            0 :     return ret;
     316              :   }
     317              : 
     318          354 :   newacp->proof.num = orig->proof.num;
     319          354 :   newacp->proof.privs = NULL;
     320          354 :   if (0 != newacp->proof.num)
     321          338 :     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         1482 :   for (size_t i = 0; i < orig->proof.num; i++)
     328              :   {
     329         1128 :     GNUNET_CRYPTO_edx25519_private_key_derive (
     330         1128 :       &orig->proof.privs[i].priv,
     331              :       salt,
     332              :       sizeof(*salt),
     333         1128 :       &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          354 :   return GNUNET_OK;
     354              : }
     355              : 
     356              : 
     357              : enum GNUNET_GenericReturnValue
     358          288 : 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          288 :   ret =      GNUNET_CRYPTO_hkdf_gnunet (&salt,
     367              :                                         sizeof (salt),
     368              :                                         "age commitment",
     369              :                                         strlen ("age commitment"),
     370              :                                         secret,
     371              :                                         sizeof(*secret));
     372          288 :   if (GNUNET_OK != ret)
     373              :   {
     374            0 :     GNUNET_break (0);
     375            0 :     return ret;
     376              :   }
     377              : 
     378          288 :   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         2178 : 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         2178 :   GNUNET_assert (NULL != attest);
     430         2178 :   GNUNET_assert (NULL != cp);
     431              : 
     432         2178 :   group = TALER_get_age_group (&cp->commitment.mask,
     433              :                                age);
     434              : 
     435         2178 :   GNUNET_assert (group < 32);
     436              : 
     437         2178 :   if (0 == group)
     438              :   {
     439              :     /* Age group 0 means: no attestation necessary.
     440              :      * We set the signature to zero and communicate success. */
     441          792 :     memset (attest,
     442              :             0,
     443              :             sizeof(struct TALER_AgeAttestationP));
     444          792 :     return GNUNET_OK;
     445              :   }
     446              : 
     447         1386 :   if (group > cp->proof.num)
     448          585 :     return GNUNET_NO;
     449              : 
     450              :   {
     451          801 :     struct TALER_AgeAttestationPPS at = {
     452          801 :       .purpose.size = htonl (sizeof(at)),
     453          801 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
     454          801 :       .mask.bits_nbo = htonl (cp->commitment.mask.bits),
     455          801 :       .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          801 :     sign (&cp->proof.privs[group - 1].priv,
     464              :           &at,
     465              :           &attest->signature);
     466              :   }
     467              : #undef sign
     468              : 
     469          801 :   return GNUNET_OK;
     470              : }
     471              : 
     472              : 
     473              : enum GNUNET_GenericReturnValue
     474         1593 : 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         1593 :   GNUNET_assert (NULL != attest);
     482         1593 :   GNUNET_assert (NULL != comm);
     483              : 
     484         1593 :   group = TALER_get_age_group (&comm->mask,
     485              :                                age);
     486              : 
     487         1593 :   GNUNET_assert (group < 32);
     488              : 
     489              :   /* Age group 0 means: no attestation necessary. */
     490         1593 :   if (0 == group)
     491          792 :     return GNUNET_OK;
     492              : 
     493          801 :   if (group > comm->num)
     494              :   {
     495            0 :     GNUNET_break_op (0);
     496            0 :     return GNUNET_NO;
     497              :   }
     498              : 
     499              :   {
     500          801 :     struct TALER_AgeAttestationPPS at = {
     501          801 :       .purpose.size = htonl (sizeof(at)),
     502          801 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
     503          801 :       .mask.bits_nbo = htonl (comm->mask.bits),
     504          801 :       .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          801 :     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           64 : TALER_age_commitment_free (
     523              :   struct TALER_AgeCommitment *commitment)
     524              : {
     525           64 :   if (NULL == commitment)
     526            0 :     return;
     527              : 
     528           64 :   if (NULL != commitment->pubs)
     529              :   {
     530           64 :     GNUNET_free (commitment->pubs);
     531           64 :     commitment->pubs = NULL;
     532              :   }
     533              : }
     534              : 
     535              : 
     536              : void
     537            0 : TALER_age_proof_free (
     538              :   struct TALER_AgeProof *proof)
     539              : {
     540            0 :   if (NULL == proof)
     541            0 :     return;
     542              : 
     543            0 :   if (NULL != proof->privs)
     544              :   {
     545            0 :     GNUNET_CRYPTO_zero_keys (
     546            0 :       proof->privs,
     547            0 :       sizeof(*proof->privs) * proof->num);
     548              : 
     549            0 :     GNUNET_free (proof->privs);
     550            0 :     proof->privs = NULL;
     551              :   }
     552              : }
     553              : 
     554              : 
     555              : void
     556          817 : TALER_age_commitment_proof_free (
     557              :   struct TALER_AgeCommitmentProof *acp)
     558              : {
     559          817 :   if (NULL == acp)
     560          264 :     return;
     561              : 
     562          553 :   if (NULL != acp->proof.privs)
     563              :   {
     564          460 :     GNUNET_CRYPTO_zero_keys (
     565          460 :       acp->proof.privs,
     566          460 :       sizeof(*acp->proof.privs) * acp->proof.num);
     567              : 
     568          460 :     GNUNET_free (acp->proof.privs);
     569          460 :     acp->proof.privs = NULL;
     570              :   }
     571              : 
     572          553 :   if (NULL != acp->commitment.pubs)
     573              :   {
     574          489 :     GNUNET_free (acp->commitment.pubs);
     575          489 :     acp->commitment.pubs = NULL;
     576              :   }
     577              : }
     578              : 
     579              : 
     580              : struct TALER_AgeCommitmentProof *
     581           32 : TALER_age_commitment_proof_duplicate (
     582              :   const struct TALER_AgeCommitmentProof *acp)
     583              : {
     584              :   struct TALER_AgeCommitmentProof *nacp;
     585              : 
     586           32 :   GNUNET_assert (NULL != acp);
     587           32 :   GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 ==
     588              :                  (int) acp->commitment.num);
     589              : 
     590           32 :   nacp = GNUNET_new (struct TALER_AgeCommitmentProof);
     591              : 
     592           32 :   TALER_age_commitment_proof_deep_copy (nacp, acp);
     593           32 :   return nacp;
     594              : }
     595              : 
     596              : 
     597              : struct TALER_AgeCommitment *
     598            0 : TALER_age_commitment_duplicate (
     599              :   const struct TALER_AgeCommitment *ac)
     600              : {
     601              :   struct TALER_AgeCommitment *nac;
     602              : 
     603            0 :   GNUNET_assert (NULL != ac);
     604            0 :   GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 ==
     605              :                  (int) ac->num);
     606              : 
     607            0 :   nac = GNUNET_new (struct TALER_AgeCommitment);
     608            0 :   TALER_age_commitment_deep_copy (nac, ac);
     609            0 :   return nac;
     610              : }
     611              : 
     612              : 
     613              : void
     614           49 : TALER_age_commitment_proof_deep_copy (
     615              :   struct TALER_AgeCommitmentProof *nacp,
     616              :   const struct TALER_AgeCommitmentProof *acp)
     617              : {
     618           49 :   GNUNET_assert (NULL != acp);
     619           49 :   GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 ==
     620              :                  (int) acp->commitment.num);
     621              : 
     622           49 :   *nacp = *acp;
     623           49 :   nacp->commitment.pubs =
     624           49 :     GNUNET_new_array (acp->commitment.num,
     625              :                       struct TALER_AgeCommitmentPublicKeyP);
     626           49 :   nacp->proof.privs =
     627           49 :     GNUNET_new_array (acp->proof.num,
     628              :                       struct TALER_AgeCommitmentPrivateKeyP);
     629              : 
     630          392 :   for (size_t i = 0; i < acp->commitment.num; i++)
     631          343 :     nacp->commitment.pubs[i] = acp->commitment.pubs[i];
     632              : 
     633          181 :   for (size_t i = 0; i < acp->proof.num; i++)
     634          132 :     nacp->proof.privs[i] = acp->proof.privs[i];
     635           49 : }
     636              : 
     637              : 
     638              : void
     639            0 : TALER_age_commitment_deep_copy (
     640              :   struct TALER_AgeCommitment *nac,
     641              :   const struct TALER_AgeCommitment *ac)
     642              : {
     643            0 :   GNUNET_assert (NULL != ac);
     644            0 :   GNUNET_assert (__builtin_popcount (ac->mask.bits) - 1 ==
     645              :                  (int) ac->num);
     646              : 
     647            0 :   *nac = *ac;
     648            0 :   nac->pubs =
     649            0 :     GNUNET_new_array (ac->num,
     650              :                       struct TALER_AgeCommitmentPublicKeyP);
     651              : 
     652            0 :   for (size_t i = 0; i < ac->num; i++)
     653            0 :     nac->pubs[i] = ac->pubs[i];
     654              : 
     655            0 : }
     656              : 
     657              : 
     658              : enum GNUNET_GenericReturnValue
     659          187 : TALER_parse_age_group_string (
     660              :   const char *groups,
     661              :   struct TALER_AgeMask *mask)
     662              : {
     663              : 
     664          187 :   const char *pos = groups;
     665          187 :   unsigned int prev = 0;
     666          187 :   unsigned int val = 0;
     667              :   char c;
     668              : 
     669              :   /* reset mask */
     670          187 :   mask->bits = 0;
     671              : 
     672         3740 :   while (*pos)
     673              :   {
     674         3553 :     c = *pos++;
     675         3553 :     if (':' == c)
     676              :     {
     677         1122 :       if (prev >= val)
     678            0 :         return GNUNET_SYSERR;
     679              : 
     680         1122 :       mask->bits |= 1 << val;
     681         1122 :       prev = val;
     682         1122 :       val = 0;
     683         1122 :       continue;
     684              :     }
     685              : 
     686         2431 :     if ('0'>c || '9'<c)
     687            0 :       return GNUNET_SYSERR;
     688              : 
     689         2431 :     val = 10 * val + c - '0';
     690              : 
     691         2431 :     if (0>=val || 32<=val)
     692            0 :       return GNUNET_SYSERR;
     693              :   }
     694              : 
     695          187 :   if (32<=val || prev>=val)
     696            0 :     return GNUNET_SYSERR;
     697              : 
     698          187 :   mask->bits |= (1 << val);
     699          187 :   mask->bits |= 1; // mark zeroth group, too
     700              : 
     701          187 :   return GNUNET_OK;
     702              : }
     703              : 
     704              : 
     705              : const char *
     706            5 : TALER_age_mask_to_string (
     707              :   const struct TALER_AgeMask *mask)
     708              : {
     709              :   static char buf[256] = {0};
     710            5 :   uint32_t bits = mask->bits;
     711            5 :   unsigned int n = 0;
     712            5 :   char *pos = buf;
     713              : 
     714            5 :   memset (buf, 0, sizeof(buf));
     715              : 
     716           62 :   while (bits != 0)
     717              :   {
     718           57 :     bits >>= 1;
     719           57 :     n++;
     720           57 :     if (0 == (bits & 1))
     721              :     {
     722           49 :       continue;
     723              :     }
     724              : 
     725            8 :     if (n > 9)
     726              :     {
     727            4 :       *(pos++) = '0' + n / 10;
     728              :     }
     729            8 :     *(pos++) = '0' + n % 10;
     730              : 
     731            8 :     if (0 != (bits >> 1))
     732              :     {
     733            3 :       *(pos++) = ':';
     734              :     }
     735              :   }
     736            5 :   return buf;
     737              : }
     738              : 
     739              : 
     740              : void
     741           51 : 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           51 :   struct GNUNET_HashCode seed_i = {0};
     748              :   uint8_t num_pub;
     749              :   uint8_t num_priv;
     750              : 
     751           51 :   GNUNET_assert (NULL != mask);
     752           51 :   GNUNET_assert (NULL != secret);
     753           51 :   GNUNET_assert (NULL != ncp);
     754           51 :   GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
     755              : 
     756           51 :   num_pub = __builtin_popcount (mask->bits) - 1;
     757           51 :   num_priv = TALER_get_age_group (mask, max_age);
     758              : 
     759           51 :   GNUNET_assert (31 > num_priv);
     760           51 :   GNUNET_assert (num_priv <= num_pub);
     761              : 
     762           51 :   ncp->commitment.mask.bits = mask->bits;
     763           51 :   ncp->commitment.num = num_pub;
     764           51 :   ncp->proof.num = num_priv;
     765           51 :   ncp->proof.privs = NULL;
     766           51 :   ncp->commitment.pubs = GNUNET_new_array (
     767              :     num_pub,
     768              :     struct TALER_AgeCommitmentPublicKeyP);
     769           51 :   if (0 < num_priv)
     770           46 :     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          408 :   for (size_t i = 0; i < num_pub; i++)
     778              :   {
     779              :     enum GNUNET_GenericReturnValue ret;
     780          357 :     const char *label = i < num_priv ? "age-commitment" : "age-factor";
     781          357 :     uint32_t ibe = htonl ((uint32_t) i);
     782              : 
     783          357 :     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          357 :     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          357 :     if (i < num_priv)
     792              :     {
     793           96 :       struct TALER_AgeCommitmentPrivateKeyP *pkey = &ncp->proof.privs[i];
     794              : 
     795              : #ifndef AGE_RESTRICTION_WITH_ECDSA
     796           96 :       GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
     797              :                                                    sizeof(seed_i),
     798              :                                                    &pkey->priv);
     799           96 :       GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
     800           96 :                                              &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          261 :       GNUNET_CRYPTO_edx25519_public_key_derive (
     815              :         &TALER_age_commitment_base_public_key,
     816              :         &seed_i,
     817              :         sizeof(seed_i),
     818          261 :         &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           51 : }
     830              : 
     831              : 
     832              : enum GNUNET_GenericReturnValue
     833           27 : TALER_parse_coarse_date (
     834              :   const char *in,
     835              :   const struct TALER_AgeMask *mask,
     836              :   uint32_t *out)
     837              : {
     838           27 :   struct tm date = {0};
     839           27 :   struct tm limit = {0};
     840              :   time_t seconds;
     841              : 
     842           27 :   if (NULL == in)
     843              :   {
     844              :     /* FIXME[oec]: correct behaviour? */
     845            0 :     *out = 0;
     846            0 :     return GNUNET_OK;
     847              :   }
     848              : 
     849           27 :   GNUNET_assert (NULL !=mask);
     850           27 :   GNUNET_assert (NULL !=out);
     851              : 
     852           27 :   if (NULL == strptime (in, "%Y-%m-%d", &date))
     853              :   {
     854           20 :     if (NULL == strptime (in, "%Y-%m-00", &date))
     855           18 :       if (NULL == strptime (in, "%Y-00-00", &date))
     856            5 :         return GNUNET_SYSERR;
     857              :     /* turns out that the day is off by one in the last two cases */
     858           15 :     date.tm_mday += 1;
     859              :   }
     860              : 
     861           22 :   seconds = timegm (&date);
     862           22 :   if (-1 == seconds)
     863            0 :     return GNUNET_SYSERR;
     864              : 
     865              :   /* calculate the limit date for the largest age group */
     866              :   {
     867           22 :     time_t l = time (NULL);
     868           22 :     localtime_r (&l, &limit);
     869              :   }
     870           22 :   limit.tm_year -= TALER_adult_age (mask);
     871           22 :   GNUNET_assert (-1 != timegm (&limit));
     872              : 
     873           22 :   if ((limit.tm_year < date.tm_year)
     874           10 :       || ((limit.tm_year == date.tm_year)
     875            5 :           && (limit.tm_mon < date.tm_mon))
     876           10 :       || ((limit.tm_year == date.tm_year)
     877            5 :           && (limit.tm_mon == date.tm_mon)
     878            0 :           && (limit.tm_mday < date.tm_mday)))
     879           12 :     *out = seconds / 60 / 60 / 24;
     880              :   else
     881           10 :     *out = 0;
     882              : 
     883           22 :   return GNUNET_OK;
     884              : }
     885              : 
     886              : 
     887              : /* end util/age_restriction.c */
        

Generated by: LCOV version 2.0-1