LCOV - code coverage report
Current view: top level - util - age_restriction.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 256 299 85.6 %
Date: 2025-06-05 21:03:14 Functions: 19 22 86.4 %

          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"
      22             : #include "taler_util.h"
      23             : #include "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_AgeCommitmentHash *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_AgeCommitmentHash));
      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_AgeAttestationPS
     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_EccSignaturePurpose 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_AgeAttestation *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_AgeAttestation));
     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_AgeAttestationPS 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_AgeAttestation *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_AgeAttestationPS 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 1.16