LCOV - code coverage report
Current view: top level - util - test_age_restriction.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 101 126 80.2 %
Date: 2025-06-05 21:03:14 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (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             : /**
      18             :  * @file util/test_age_restriction.c
      19             :  * @brief Tests for age restriction specific logic
      20             :  * @author Özgür Kesim
      21             :  */
      22             : #include "platform.h"
      23             : #include "taler_util.h"
      24             : #include <gnunet/gnunet_common.h>
      25             : 
      26             : /**
      27             :  * Encodes the age mask into a string, like "8:10:12:14:16:18:21"
      28             :  *
      29             :  * @param mask Age mask
      30             :  * @return String representation of the age mask, allocated by GNUNET_malloc.
      31             :  *         Can be used as value in the TALER config.
      32             :  */
      33             : static char *
      34          64 : age_mask_to_string (
      35             :   const struct TALER_AgeMask *m)
      36             : {
      37          64 :   uint32_t bits = m->bits;
      38          64 :   unsigned int n = 0;
      39          64 :   char *buf = GNUNET_malloc (32 * 3); // max characters possible
      40          64 :   char *pos = buf;
      41             : 
      42          64 :   if (NULL == buf)
      43             :   {
      44           0 :     return buf;
      45             :   }
      46             : 
      47        1536 :   while (bits != 0)
      48             :   {
      49        1472 :     bits >>= 1;
      50        1472 :     n++;
      51        1472 :     if (0 == (bits & 1))
      52             :     {
      53        1152 :       continue;
      54             :     }
      55             : 
      56         320 :     if (n > 9)
      57             :     {
      58         256 :       *(pos++) = '0' + n / 10;
      59             :     }
      60         320 :     *(pos++) = '0' + n % 10;
      61             : 
      62         320 :     if (0 != (bits >> 1))
      63             :     {
      64         256 :       *(pos++) = ':';
      65             :     }
      66             :   }
      67          64 :   return buf;
      68             : }
      69             : 
      70             : 
      71             : static enum GNUNET_GenericReturnValue
      72           1 : test_groups (void)
      73             : {
      74             :   struct
      75             :   {
      76             :     uint32_t bits;
      77             :     uint8_t group[33];
      78           1 :   } test[] = {
      79             :     {
      80             :       .bits =
      81             :         1 | 1 << 5 | 1 << 13 | 1 << 23,
      82             : 
      83             :       .group = { 0, 0, 0, 0, 0,
      84             :                  1, 1, 1, 1, 1, 1, 1, 1,
      85             :                  2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
      86             :                  3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }
      87             : 
      88             : 
      89             :     },
      90             :     {
      91             :       .bits =
      92             :         1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21,
      93             :       .group = { 0, 0, 0, 0, 0, 0, 0, 0,
      94             :                  1, 1,
      95             :                  2, 2,
      96             :                  3, 3,
      97             :                  4, 4,
      98             :                  5, 5,
      99             :                  6, 6, 6,
     100             :                  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}
     101             : 
     102             : 
     103             :     }
     104             :   };
     105             : 
     106           3 :   for (uint8_t t = 0; t < sizeof(test) / sizeof(test[0]); t++)
     107             :   {
     108           2 :     struct TALER_AgeMask mask = {.bits = test[t].bits};
     109             : 
     110          66 :     for (uint8_t i = 0; i < 32; i++)
     111             :     {
     112          64 :       uint8_t r = TALER_get_age_group (&mask, i);
     113          64 :       char *m = age_mask_to_string (&mask);
     114             : 
     115          64 :       printf ("TALER_get_age_group(%s, %2d) = %d vs %d (exp)\n",
     116             :               m,
     117             :               i,
     118             :               r,
     119          64 :               test[t].group[i]);
     120             : 
     121          64 :       if (test[t].group[i] != r)
     122           0 :         return GNUNET_SYSERR;
     123             : 
     124          64 :       GNUNET_free (m);
     125             :     }
     126             :   }
     127             : 
     128           1 :   return GNUNET_OK;
     129             : }
     130             : 
     131             : 
     132             : static enum GNUNET_GenericReturnValue
     133           1 : test_dates (void)
     134             : {
     135           1 :   struct TALER_AgeMask mask = {
     136             :     .bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21
     137             :   };
     138             :   struct
     139             :   {
     140             :     const char *date;
     141             :     uint32_t expected;
     142             :     enum GNUNET_GenericReturnValue ret;
     143             :   }
     144           1 :   test [] = {
     145             :     {.date = "abcd-00-00", .expected = 0, .ret = GNUNET_SYSERR},
     146             :     {.date = "1900-00-01", .expected = 0, .ret = GNUNET_SYSERR},
     147             :     {.date = "19000001",   .expected = 0, .ret = GNUNET_SYSERR},
     148             :     {.date = "2001-33-05", .expected = 0, .ret = GNUNET_SYSERR},
     149             :     {.date = "2001-33-35", .expected = 0, .ret = GNUNET_SYSERR},
     150             : 
     151             :     {.date = "1900-00-00", .expected = 0, .ret = GNUNET_OK},
     152             :     {.date = "2001-00-00", .expected = 0, .ret = GNUNET_OK},
     153             :     {.date = "2001-03-00", .expected = 0, .ret = GNUNET_OK},
     154             :     {.date = "2001-03-05", .expected = 0, .ret = GNUNET_OK},
     155             : 
     156             :     /* These dates should be far enough for the near future so that
     157             :      * the expected values are correct. Will need adjustment in 2044 :) */
     158             :     {.date = "2022-11-26", .expected = 19322, .ret = GNUNET_OK },
     159             :     {.date = "2022-11-27", .expected = 19323, .ret = GNUNET_OK },
     160             :     {.date = "2023-06-26", .expected = 19534, .ret = GNUNET_OK },
     161             :     {.date = "2023-06-01", .expected = 19509, .ret = GNUNET_OK },
     162             :     {.date = "2023-06-00", .expected = 19509, .ret = GNUNET_OK },
     163             :     {.date = "2023-01-01", .expected = 19358, .ret = GNUNET_OK },
     164             :     {.date = "2023-00-00", .expected = 19358, .ret = GNUNET_OK },
     165             : 
     166             :     /* Special case: .date == NULL meands birthday == current date, which
     167             :      * should be 21 years in the future.  We will set these values below in the
     168             :      * loop */
     169             :     {.date = NULL, .expected = 0, .ret = GNUNET_OK },
     170             :   };
     171           1 :   char buf[256] = {0};
     172             : 
     173          18 :   for (uint8_t t = 0; t < sizeof(test) / sizeof(test[0]); t++)
     174             :   {
     175             :     uint32_t d;
     176             :     enum GNUNET_GenericReturnValue ret;
     177          17 :     const char *date = test[t].date;
     178             : 
     179          17 :     if (NULL == test[t].date)
     180             :     {
     181             :       /* Special case:  We set .date to the current date. */
     182             :       time_t tn;
     183             :       struct tm now;
     184             : 
     185           1 :       time (&tn);
     186           1 :       localtime_r (&tn, &now);
     187           1 :       strftime (buf, sizeof(buf), "%Y-%m-%d", &now);
     188           1 :       date = &buf[0];
     189             : 
     190             :       /* The expected value is the number of days since 1970-01-01,
     191             :        * counted simplistically */
     192           1 :       test[t].expected = timegm (&now) / 60 / 60 / 24;
     193             :     }
     194             : 
     195          17 :     ret = TALER_parse_coarse_date (date,
     196             :                                    &mask,
     197             :                                    &d);
     198          17 :     if (ret != test[t].ret)
     199             :     {
     200           0 :       printf (
     201             :         "dates[%d] for date `%s` expected parser to return: %d, got: %d\n",
     202           0 :         t, date, test[t].ret, ret);
     203           0 :       return GNUNET_SYSERR;
     204             :     }
     205             : 
     206          17 :     if (ret == GNUNET_SYSERR)
     207           5 :       continue;
     208             : 
     209          12 :     if (d != test[t].expected)
     210             :     {
     211           0 :       printf (
     212             :         "dates[%d] for date `%s` expected value %d, but got %d\n",
     213             :         t, date, test[t].expected, d);
     214           0 :       return GNUNET_SYSERR;
     215             :     }
     216             : 
     217          12 :     printf ("dates[%d] for date `%s` got expected value %d\n",
     218             :             t, date, d);
     219             :   }
     220             : 
     221           1 :   printf ("done with dates\n");
     222             : 
     223           1 :   return GNUNET_OK;
     224             : }
     225             : 
     226             : 
     227             : static enum GNUNET_GenericReturnValue
     228           1 : test_lowest (void)
     229             : {
     230           1 :   struct TALER_AgeMask mask = {
     231             :     .bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21
     232             :   };
     233             : 
     234             :   struct { uint8_t age; uint8_t expected; }
     235           1 :   test [] = {
     236             :     {.age = 1, .expected = 0 },
     237             :     {.age = 2, .expected = 0 },
     238             :     {.age = 3, .expected = 0 },
     239             :     {.age = 4, .expected = 0 },
     240             :     {.age = 5, .expected = 5 },
     241             :     {.age = 6, .expected = 5 },
     242             :     {.age = 7, .expected = 5 },
     243             :     {.age = 8, .expected = 5 },
     244             :     {.age = 9, .expected = 9 },
     245             :     {.age = 10, .expected = 9 },
     246             :     {.age = 11, .expected = 9 },
     247             :     {.age = 12, .expected = 9 },
     248             :     {.age = 13, .expected = 13 },
     249             :     {.age = 14, .expected = 13 },
     250             :     {.age = 15, .expected = 13 },
     251             :     {.age = 16, .expected = 13 },
     252             :     {.age = 17, .expected = 17 },
     253             :     {.age = 18, .expected = 17 },
     254             :     {.age = 19, .expected = 17 },
     255             :     {.age = 20, .expected = 17 },
     256             :     {.age = 21, .expected = 21 },
     257             :     {.age = 22, .expected = 21 },
     258             :   };
     259             : 
     260          23 :   for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++)
     261             :   {
     262          22 :     uint8_t l = TALER_get_lowest_age (&mask, test[n].age);
     263          22 :     printf ("lowest[%d] for age %d, expected lowest: %d, got: %d\n",
     264          22 :             n, test[n].age, test[n].expected, l);
     265          22 :     if (test[n].expected != l)
     266           0 :       return GNUNET_SYSERR;
     267             :   }
     268             : 
     269           1 :   return GNUNET_OK;
     270             : }
     271             : 
     272             : 
     273             : static enum GNUNET_GenericReturnValue
     274           1 : test_adult (void)
     275             : {
     276             :   struct { struct TALER_AgeMask mask; uint8_t expected; }
     277           1 :   test[] = {
     278             :     { .mask = {.bits = 1 | 1 << 2},
     279             :       .expected = 2 },
     280             :     { .mask = {.bits = 1 | 1 << 2 | 1 << 3},
     281             :       .expected = 3 },
     282             :     { .mask = {.bits = 1 | 1 << 3},
     283             :       .expected = 3 },
     284             :     { .mask = {.bits = 1 | 1 << 22},
     285             :       .expected = 22 },
     286             :     { .mask = {.bits = 1 | 1 << 10 | 1 << 16 | 1 << 22},
     287             :       .expected = 22 },
     288             :   };
     289           6 :   for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++)
     290             :   {
     291           5 :     uint8_t l = TALER_adult_age (&test[n].mask);
     292           5 :     printf ("adult[%d] for mask %s, expected: %d, got: %d\n",
     293           5 :             n, TALER_age_mask_to_string (&test[n].mask), test[n].expected, l);
     294           5 :     if (test[n].expected != l)
     295           0 :       return GNUNET_SYSERR;
     296             :   }
     297           1 :   printf ("done with adult\n");
     298             : 
     299           1 :   return GNUNET_OK;
     300             : }
     301             : 
     302             : 
     303             : static struct TALER_AgeMask age_mask = {
     304             :   .bits = 1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21
     305             : };
     306             : 
     307             : 
     308             : static enum GNUNET_GenericReturnValue
     309           1 : test_attestation (void)
     310             : {
     311             :   uint8_t age;
     312          34 :   for (age = 0; age < 33; age++)
     313             :   {
     314             :     enum GNUNET_GenericReturnValue ret;
     315          33 :     struct TALER_AgeCommitmentProof acp[3] = {0};
     316          33 :     struct TALER_AgeAttestation at = {0};
     317          33 :     uint8_t age_group = TALER_get_age_group (&age_mask, age);
     318             :     struct GNUNET_HashCode seed;
     319             : 
     320          33 :     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     321             :                                 &seed,
     322             :                                 sizeof(seed));
     323          33 :     TALER_age_restriction_commit (&age_mask,
     324             :                                   age,
     325             :                                   &seed,
     326             :                                   &acp[0]);
     327          33 :     printf (
     328             :       "commit(age:%d); proof.num: %ld; age_group: %d\n",
     329             :       age,
     330             :       acp[0].proof.num,
     331             :       age_group);
     332             : 
     333             :     /* Also derive two more commitments right away */
     334          99 :     for (uint8_t i = 0; i<2; i++)
     335             :     {
     336             :       struct GNUNET_HashCode salt;
     337          66 :       GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     338             :                                   &salt,
     339             :                                   sizeof (salt));
     340          66 :       GNUNET_assert (GNUNET_OK ==
     341             :                      TALER_age_commitment_proof_derive (&acp[i],
     342             :                                                         &salt,
     343             :                                                         &acp[i + 1]));
     344             :     }
     345             : 
     346         132 :     for (uint8_t i = 0; i < 3; i++)
     347             :     {
     348        2277 :       for (uint8_t min = 0; min < 22; min++)
     349             :       {
     350        2178 :         uint8_t min_group = TALER_get_age_group (&age_mask, min);
     351        2178 :         ret = TALER_age_commitment_attest (&acp[i],
     352             :                                            min,
     353             :                                            &at);
     354        2178 :         printf (
     355             :           "[%s]: attest(min:%d, age:%d) == %d; age_group: %d, min_group: %d\n",
     356             :           i == 0 ? "commit" : "derive",
     357             :           min,
     358             :           age,
     359             :           ret,
     360             :           age_group,
     361             :           min_group);
     362             : 
     363        2178 :         if (min_group <= age_group &&
     364             :             GNUNET_OK != ret)
     365             :         {
     366           0 :           GNUNET_break (0);
     367           0 :           ret = GNUNET_SYSERR;
     368           0 :           break;
     369             :         }
     370             : 
     371        2178 :         if (min_group > age_group &&
     372             :             GNUNET_NO != ret)
     373             :         {
     374           0 :           GNUNET_break (0);
     375           0 :           ret = GNUNET_SYSERR;
     376           0 :           break;
     377             :         }
     378             : 
     379        2178 :         if (min_group > age_group)
     380         585 :           continue;
     381             : 
     382        1593 :         ret = TALER_age_commitment_verify (&acp[i].commitment,
     383             :                                            min,
     384             :                                            &at);
     385        1593 :         printf (
     386             :           "[%s]: verify(min:%d, age:%d) == %d; age_group:%d, min_group: %d\n",
     387             :           i == 0 ? "commit" : "derive",
     388             :           min,
     389             :           age,
     390             :           ret,
     391             :           age_group,
     392             :           min_group);
     393             : 
     394        1593 :         if (GNUNET_OK != ret)
     395             :         {
     396           0 :           GNUNET_break (0);
     397           0 :           break;
     398             :         }
     399             :       }
     400          99 :       TALER_age_commitment_proof_free (&acp[i]);
     401             :     }
     402             : 
     403          33 :     if (GNUNET_SYSERR == ret)
     404             :     {
     405           0 :       GNUNET_break (0);
     406           0 :       return ret;
     407             :     }
     408             :   }
     409           1 :   return GNUNET_OK;
     410             : }
     411             : 
     412             : 
     413             : int
     414           1 : main (int argc,
     415             :       const char *const argv[])
     416             : {
     417             :   (void) argc;
     418             :   (void) argv;
     419           1 :   GNUNET_log_setup ("test-age-restriction",
     420             :                     "INFO",
     421             :                     NULL);
     422           1 :   if (GNUNET_OK != test_groups ())
     423           0 :     return 1;
     424           1 :   if (GNUNET_OK != test_lowest ())
     425           0 :     return 2;
     426           1 :   if (GNUNET_OK != test_attestation ())
     427             :   {
     428           0 :     GNUNET_break (0);
     429           0 :     return 3;
     430             :   }
     431           1 :   if (GNUNET_OK != test_dates ())
     432           0 :     return 4;
     433           1 :   if (GNUNET_OK != test_adult ())
     434           0 :     return 5;
     435           1 :   return 0;
     436             : }
     437             : 
     438             : 
     439             : /* end of test_age_restriction.c */

Generated by: LCOV version 1.16