LCOV - code coverage report
Current view: top level - util - test_age_restriction.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 80.2 % 126 101
Test Date: 2026-01-04 22:17:00 Functions: 100.0 % 7 7

            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 "taler/platform.h"
      23              : #include "taler/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_AgeAttestationP 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 2.0-1