LCOV - code coverage report
Current view: top level - util - age_restriction.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 108 128 84.4 %
Date: 2022-08-25 06:15:09 Functions: 7 9 77.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2022 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 <gcrypt.h>
      25             : 
      26             : void
      27           2 : TALER_age_commitment_hash (
      28             :   const struct TALER_AgeCommitment *commitment,
      29             :   struct TALER_AgeCommitmentHash *ahash)
      30             : {
      31             :   struct GNUNET_HashContext *hash_context;
      32             :   struct GNUNET_HashCode hash;
      33             : 
      34           2 :   GNUNET_assert (NULL != ahash);
      35           2 :   if (NULL == commitment)
      36             :   {
      37           0 :     memset (ahash, 0, sizeof(struct TALER_AgeCommitmentHash));
      38           0 :     return;
      39             :   }
      40             : 
      41           2 :   GNUNET_assert (__builtin_popcount (commitment->mask.bits) - 1 ==
      42             :                  (int) commitment->num);
      43             : 
      44           2 :   hash_context = GNUNET_CRYPTO_hash_context_start ();
      45             : 
      46          16 :   for (size_t i = 0; i < commitment->num; i++)
      47             :   {
      48          14 :     GNUNET_CRYPTO_hash_context_read (hash_context,
      49          14 :                                      &commitment->keys[i],
      50             :                                      sizeof(commitment->keys[i]));
      51             :   }
      52             : 
      53           2 :   GNUNET_CRYPTO_hash_context_finish (hash_context,
      54             :                                      &hash);
      55           2 :   GNUNET_memcpy (&ahash->shash.bits,
      56             :                  &hash.bits,
      57             :                  sizeof(ahash->shash.bits));
      58             : }
      59             : 
      60             : 
      61             : /* To a given age value between 0 and 31, returns the index of the age group
      62             :  * defined by the given mask.
      63             :  */
      64             : uint8_t
      65        6081 : get_age_group (
      66             :   const struct TALER_AgeMask *mask,
      67             :   uint8_t age)
      68             : {
      69        6081 :   uint32_t m = mask->bits;
      70        6081 :   uint8_t i = 0;
      71             : 
      72       67438 :   while (m > 0)
      73             :   {
      74       67398 :     if (0 >= age)
      75        6041 :       break;
      76       61357 :     m = m >> 1;
      77       61357 :     i += m & 1;
      78       61357 :     age--;
      79             :   }
      80        6081 :   return i;
      81             : }
      82             : 
      83             : 
      84             : enum GNUNET_GenericReturnValue
      85          35 : TALER_age_restriction_commit (
      86             :   const struct TALER_AgeMask *mask,
      87             :   const uint8_t age,
      88             :   const struct GNUNET_HashCode *seed,
      89             :   struct TALER_AgeCommitmentProof *new)
      90             : {
      91             :   struct GNUNET_HashCode seed_i;
      92          35 :   uint8_t num_pub = __builtin_popcount (mask->bits) - 1;
      93          35 :   uint8_t num_priv = get_age_group (mask, age);
      94             :   size_t i;
      95             : 
      96          35 :   GNUNET_assert (NULL != seed);
      97          35 :   GNUNET_assert (NULL != new);
      98          35 :   GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
      99          35 :   GNUNET_assert (31 > num_priv);
     100          35 :   GNUNET_assert (num_priv <= num_pub);
     101             : 
     102          35 :   seed_i = *seed;
     103          35 :   new->commitment.mask.bits = mask->bits;
     104          35 :   new->commitment.num = num_pub;
     105          35 :   new->proof.num = num_priv;
     106          35 :   new->proof.keys = NULL;
     107             : 
     108          35 :   new->commitment.keys = GNUNET_new_array (
     109             :     num_pub,
     110             :     struct TALER_AgeCommitmentPublicKeyP);
     111             : 
     112          35 :   if (0 < num_priv)
     113          27 :     new->proof.keys = GNUNET_new_array (
     114             :       num_priv,
     115             :       struct TALER_AgeCommitmentPrivateKeyP);
     116             : 
     117             :   /* Create as many private keys as we need and fill the rest of the
     118             :    * public keys with valid curve points.
     119             :    * We need to make sure that the public keys are proper points on the
     120             :    * elliptic curve, so we can't simply fill the struct with random values. */
     121         280 :   for (i = 0; i < num_pub; i++)
     122             :   {
     123         245 :     struct TALER_AgeCommitmentPrivateKeyP key = {0};
     124         245 :     struct TALER_AgeCommitmentPrivateKeyP *pkey = &key;
     125             : 
     126             :     /* Only save the private keys for age groups less than num_priv */
     127         245 :     if (i < num_priv)
     128         138 :       pkey = &new->proof.keys[i];
     129             : 
     130             : #ifndef AGE_RESTRICTION_WITH_ECDSA
     131         245 :     GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
     132             :                                                  sizeof(seed_i),
     133             :                                                  &pkey->priv);
     134         245 :     GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
     135         245 :                                            &new->commitment.keys[i].pub);
     136         245 :     seed_i.bits[0] += 1;
     137             :   }
     138             : 
     139          35 :   return GNUNET_OK;
     140             : #else
     141             :     if  (GNUNET_OK !=
     142             :          GNUNET_CRYPTO_kdf (pkey,
     143             :                             sizeof (*pkey),
     144             :                             &salti,
     145             :                             sizeof (salti),
     146             :                             "age commitment",
     147             :                             strlen ("age commitment"),
     148             :                             NULL, 0))
     149             :       goto FAIL;
     150             : 
     151             :     /* See GNUNET_CRYPTO_ecdsa_key_create */
     152             :     pkey->priv.d[0] &= 248;
     153             :     pkey->priv.d[31] &= 127;
     154             :     pkey->priv.d[31] |= 64;
     155             : 
     156             :     GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
     157             :                                         &new->commitment.keys[i].pub);
     158             : 
     159             :   }
     160             : 
     161             :   return GNUNET_OK;
     162             : 
     163             : FAIL:
     164             :   GNUNET_free (new->commitment.keys);
     165             :   if (NULL != new->proof.keys)
     166             :     GNUNET_free (new->proof.keys);
     167             :   return GNUNET_SYSERR;
     168             : #endif
     169             : }
     170             : 
     171             : 
     172             : enum GNUNET_GenericReturnValue
     173          66 : TALER_age_commitment_derive (
     174             :   const struct TALER_AgeCommitmentProof *orig,
     175             :   const struct GNUNET_HashCode *salt,
     176             :   struct TALER_AgeCommitmentProof *newacp)
     177             : {
     178          66 :   GNUNET_assert (NULL != newacp);
     179          66 :   GNUNET_assert (orig->proof.num <=
     180             :                  orig->commitment.num);
     181          66 :   GNUNET_assert (((int) orig->commitment.num) ==
     182             :                  __builtin_popcount (orig->commitment.mask.bits) - 1);
     183             : 
     184          66 :   newacp->commitment.mask = orig->commitment.mask;
     185          66 :   newacp->commitment.num = orig->commitment.num;
     186          66 :   newacp->commitment.keys = GNUNET_new_array (
     187             :     newacp->commitment.num,
     188             :     struct TALER_AgeCommitmentPublicKeyP);
     189             : 
     190          66 :   newacp->proof.num = orig->proof.num;
     191          66 :   newacp->proof.keys = NULL;
     192          66 :   if (0 != newacp->proof.num)
     193          50 :     newacp->proof.keys = GNUNET_new_array (
     194             :       newacp->proof.num,
     195             :       struct TALER_AgeCommitmentPrivateKeyP);
     196             : 
     197             : #ifndef AGE_RESTRICTION_WITH_ECDSA
     198             :   /* 1. Derive the public keys */
     199         528 :   for (size_t i = 0; i < orig->commitment.num; i++)
     200             :   {
     201         462 :     GNUNET_CRYPTO_edx25519_public_key_derive (
     202         462 :       &orig->commitment.keys[i].pub,
     203             :       salt,
     204             :       sizeof(*salt),
     205         462 :       &newacp->commitment.keys[i].pub);
     206             :   }
     207             : 
     208             :   /* 2. Derive the private keys */
     209         330 :   for (size_t i = 0; i < orig->proof.num; i++)
     210             :   {
     211         264 :     GNUNET_CRYPTO_edx25519_private_key_derive (
     212         264 :       &orig->proof.keys[i].priv,
     213             :       salt,
     214             :       sizeof(*salt),
     215         264 :       &newacp->proof.keys[i].priv);
     216             :   }
     217             : #else
     218             :   char label[sizeof(uint64_t) + 1] = {0};
     219             : 
     220             :   /* Because GNUNET_CRYPTO_ecdsa_public_key_derive expects char * (and calls
     221             :    * strlen on it), we must avoid 0's in the label.  */
     222             :   uint64_t nz_salt = salt | 0x8040201008040201;
     223             :   memcpy (label, &nz_salt, sizeof(nz_salt));
     224             : 
     225             :   /* 1. Derive the public keys */
     226             :   for (size_t i = 0; i < orig->commitment.num; i++)
     227             :   {
     228             :     GNUNET_CRYPTO_ecdsa_public_key_derive (
     229             :       &orig->commitment.keys[i].pub,
     230             :       label,
     231             :       "age commitment derive",
     232             :       &newacp->commitment.keys[i].pub);
     233             :   }
     234             : 
     235             :   /* 2. Derive the private keys */
     236             :   for (size_t i = 0; i < orig->proof.num; i++)
     237             :   {
     238             :     struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
     239             :     priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
     240             :       &orig->proof.keys[i].priv,
     241             :       label,
     242             :       "age commitment derive");
     243             :     newacp->proof.keys[i].priv = *priv;
     244             :     GNUNET_free (priv);
     245             :   }
     246             : #endif
     247             : 
     248          66 :   return GNUNET_OK;
     249             : }
     250             : 
     251             : 
     252             : GNUNET_NETWORK_STRUCT_BEGIN
     253             : 
     254             : /**
     255             :  * Age group mask in network byte order.
     256             :  */
     257             : struct TALER_AgeMaskNBO
     258             : {
     259             :   uint32_t bits_nbo;
     260             : };
     261             : 
     262             : /**
     263             :  * Used for attestation of a particular age
     264             :  */
     265             : struct TALER_AgeAttestationPS
     266             : {
     267             :   /**
     268             :    * Purpose must be #TALER_SIGNATURE_WALLET_AGE_ATTESTATION.
     269             :    * (no GNUNET_PACKED here because the struct is already packed)
     270             :    */
     271             :   struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
     272             : 
     273             :   /**
     274             :    * Age mask that defines the underlying age groups
     275             :    */
     276             :   struct TALER_AgeMaskNBO mask GNUNET_PACKED;
     277             : 
     278             :   /**
     279             :    * The particular age that this attestation is for.
     280             :    * We use uint32_t here for alignment.
     281             :    */
     282             :   uint32_t age GNUNET_PACKED;
     283             : };
     284             : 
     285             : GNUNET_NETWORK_STRUCT_END
     286             : 
     287             : 
     288             : enum GNUNET_GenericReturnValue
     289        2178 : TALER_age_commitment_attest (
     290             :   const struct TALER_AgeCommitmentProof *cp,
     291             :   uint8_t age,
     292             :   struct TALER_AgeAttestation *attest)
     293             : {
     294             :   uint8_t group;
     295             : 
     296        2178 :   GNUNET_assert (NULL != attest);
     297        2178 :   GNUNET_assert (NULL != cp);
     298             : 
     299        2178 :   group = get_age_group (&cp->commitment.mask,
     300             :                          age);
     301             : 
     302        2178 :   GNUNET_assert (group < 32);
     303             : 
     304        2178 :   if (0 == group)
     305             :   {
     306             :     /* Age group 0 means: no attestation necessary.
     307             :      * We set the signature to zero and communicate success. */
     308         792 :     memset (attest,
     309             :             0,
     310             :             sizeof(struct TALER_AgeAttestation));
     311         792 :     return GNUNET_OK;
     312             :   }
     313             : 
     314        1386 :   if (group > cp->proof.num)
     315         585 :     return GNUNET_NO;
     316             : 
     317             :   {
     318         801 :     struct TALER_AgeAttestationPS at = {
     319         801 :       .purpose.size = htonl (sizeof(at)),
     320         801 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
     321         801 :       .mask.bits_nbo = htonl (cp->commitment.mask.bits),
     322         801 :       .age = htonl (age),
     323             :     };
     324             : 
     325             : #ifndef AGE_RESTRICTION_WITH_ECDSA
     326             :   #define sign(a,b,c)  GNUNET_CRYPTO_edx25519_sign (a,b,c)
     327             : #else
     328             :   #define sign(a,b,c)  GNUNET_CRYPTO_ecdsa_sign (a,b,c)
     329             : #endif
     330         801 :     sign (&cp->proof.keys[group - 1].priv,
     331             :           &at,
     332             :           &attest->signature);
     333             :   }
     334             : 
     335         801 :   return GNUNET_OK;
     336             : }
     337             : 
     338             : 
     339             : enum GNUNET_GenericReturnValue
     340        1593 : TALER_age_commitment_verify (
     341             :   const struct TALER_AgeCommitment *comm,
     342             :   uint8_t age,
     343             :   const struct TALER_AgeAttestation *attest)
     344             : {
     345             :   uint8_t group;
     346             : 
     347        1593 :   GNUNET_assert (NULL != attest);
     348        1593 :   GNUNET_assert (NULL != comm);
     349             : 
     350        1593 :   group = get_age_group (&comm->mask,
     351             :                          age);
     352             : 
     353        1593 :   GNUNET_assert (group < 32);
     354             : 
     355             :   /* Age group 0 means: no attestation necessary. */
     356        1593 :   if (0 == group)
     357         792 :     return GNUNET_OK;
     358             : 
     359         801 :   if (group > comm->num)
     360             :   {
     361           0 :     GNUNET_break_op (0);
     362           0 :     return GNUNET_NO;
     363             :   }
     364             : 
     365             :   {
     366         801 :     struct TALER_AgeAttestationPS at = {
     367         801 :       .purpose.size = htonl (sizeof(at)),
     368         801 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
     369         801 :       .mask.bits_nbo = htonl (comm->mask.bits),
     370         801 :       .age = htonl (age),
     371             :     };
     372             : 
     373             : #ifndef AGE_RESTRICTION_WITH_ECDSA
     374             :   #define verify(a,b,c,d)      GNUNET_CRYPTO_edx25519_verify ((a),(b),(c),(d))
     375             : #else
     376             :   #define verify(a,b,c,d)      GNUNET_CRYPTO_ecdsa_verify ((a),(b),(c),(d))
     377             : #endif
     378         801 :     return verify (TALER_SIGNATURE_WALLET_AGE_ATTESTATION,
     379             :                    &at,
     380             :                    &attest->signature,
     381             :                    &comm->keys[group - 1].pub);
     382             :   }
     383             : }
     384             : 
     385             : 
     386             : void
     387           0 : TALER_age_commitment_free (
     388             :   struct TALER_AgeCommitment *commitment)
     389             : {
     390           0 :   if (NULL == commitment)
     391           0 :     return;
     392             : 
     393           0 :   if (NULL != commitment->keys)
     394             :   {
     395           0 :     GNUNET_free (commitment->keys);
     396           0 :     commitment->keys = NULL;
     397             :   }
     398           0 :   GNUNET_free (commitment);
     399             : }
     400             : 
     401             : 
     402             : void
     403           0 : TALER_age_proof_free (
     404             :   struct TALER_AgeProof *proof)
     405             : {
     406           0 :   if (NULL != proof->keys)
     407             :   {
     408           0 :     GNUNET_CRYPTO_zero_keys (
     409           0 :       proof->keys,
     410           0 :       sizeof(*proof->keys) * proof->num);
     411             : 
     412           0 :     GNUNET_free (proof->keys);
     413           0 :     proof->keys = NULL;
     414             :   }
     415           0 :   GNUNET_free (proof);
     416           0 : }
     417             : 
     418             : 
     419             : void
     420         101 : TALER_age_commitment_proof_free (
     421             :   struct TALER_AgeCommitmentProof *cp)
     422             : {
     423         101 :   if (NULL != cp->proof.keys)
     424             :   {
     425          77 :     GNUNET_CRYPTO_zero_keys (
     426          77 :       cp->proof.keys,
     427          77 :       sizeof(*cp->proof.keys) * cp->proof.num);
     428             : 
     429          77 :     GNUNET_free (cp->proof.keys);
     430          77 :     cp->proof.keys = NULL;
     431             :   }
     432             : 
     433         101 :   if (NULL != cp->commitment.keys)
     434             :   {
     435         101 :     GNUNET_free (cp->commitment.keys);
     436         101 :     cp->commitment.keys = NULL;
     437             :   }
     438         101 : }

Generated by: LCOV version 1.14