exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test_age_restriction.c (11538B)


      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 age_mask_to_string (
     35   const struct TALER_AgeMask *m)
     36 {
     37   uint32_t bits = m->bits;
     38   unsigned int n = 0;
     39   char *buf = GNUNET_malloc (32 * 3); // max characters possible
     40   char *pos = buf;
     41 
     42   if (NULL == buf)
     43   {
     44     return buf;
     45   }
     46 
     47   while (bits != 0)
     48   {
     49     bits >>= 1;
     50     n++;
     51     if (0 == (bits & 1))
     52     {
     53       continue;
     54     }
     55 
     56     if (n > 9)
     57     {
     58       *(pos++) = '0' + n / 10;
     59     }
     60     *(pos++) = '0' + n % 10;
     61 
     62     if (0 != (bits >> 1))
     63     {
     64       *(pos++) = ':';
     65     }
     66   }
     67   return buf;
     68 }
     69 
     70 
     71 static enum GNUNET_GenericReturnValue
     72 test_groups (void)
     73 {
     74   struct
     75   {
     76     uint32_t bits;
     77     uint8_t group[33];
     78   } 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   for (uint8_t t = 0; t < sizeof(test) / sizeof(test[0]); t++)
    107   {
    108     struct TALER_AgeMask mask = {.bits = test[t].bits};
    109 
    110     for (uint8_t i = 0; i < 32; i++)
    111     {
    112       uint8_t r = TALER_get_age_group (&mask, i);
    113       char *m = age_mask_to_string (&mask);
    114 
    115       printf ("TALER_get_age_group(%s, %2d) = %d vs %d (exp)\n",
    116               m,
    117               i,
    118               r,
    119               test[t].group[i]);
    120 
    121       if (test[t].group[i] != r)
    122         return GNUNET_SYSERR;
    123 
    124       GNUNET_free (m);
    125     }
    126   }
    127 
    128   return GNUNET_OK;
    129 }
    130 
    131 
    132 static enum GNUNET_GenericReturnValue
    133 test_dates (void)
    134 {
    135   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   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   char buf[256] = {0};
    172 
    173   for (uint8_t t = 0; t < sizeof(test) / sizeof(test[0]); t++)
    174   {
    175     uint32_t d;
    176     enum GNUNET_GenericReturnValue ret;
    177     const char *date = test[t].date;
    178 
    179     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       time (&tn);
    186       localtime_r (&tn, &now);
    187       strftime (buf, sizeof(buf), "%Y-%m-%d", &now);
    188       date = &buf[0];
    189 
    190       /* The expected value is the number of days since 1970-01-01,
    191        * counted simplistically */
    192       test[t].expected = timegm (&now) / 60 / 60 / 24;
    193     }
    194 
    195     ret = TALER_parse_coarse_date (date,
    196                                    &mask,
    197                                    &d);
    198     if (ret != test[t].ret)
    199     {
    200       printf (
    201         "dates[%d] for date `%s` expected parser to return: %d, got: %d\n",
    202         t, date, test[t].ret, ret);
    203       return GNUNET_SYSERR;
    204     }
    205 
    206     if (ret == GNUNET_SYSERR)
    207       continue;
    208 
    209     if (d != test[t].expected)
    210     {
    211       printf (
    212         "dates[%d] for date `%s` expected value %d, but got %d\n",
    213         t, date, test[t].expected, d);
    214       return GNUNET_SYSERR;
    215     }
    216 
    217     printf ("dates[%d] for date `%s` got expected value %d\n",
    218             t, date, d);
    219   }
    220 
    221   printf ("done with dates\n");
    222 
    223   return GNUNET_OK;
    224 }
    225 
    226 
    227 static enum GNUNET_GenericReturnValue
    228 test_lowest (void)
    229 {
    230   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   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   for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++)
    261   {
    262     uint8_t l = TALER_get_lowest_age (&mask, test[n].age);
    263     printf ("lowest[%d] for age %d, expected lowest: %d, got: %d\n",
    264             n, test[n].age, test[n].expected, l);
    265     if (test[n].expected != l)
    266       return GNUNET_SYSERR;
    267   }
    268 
    269   return GNUNET_OK;
    270 }
    271 
    272 
    273 static enum GNUNET_GenericReturnValue
    274 test_adult (void)
    275 {
    276   struct { struct TALER_AgeMask mask; uint8_t expected; }
    277   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   for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++)
    290   {
    291     uint8_t l = TALER_adult_age (&test[n].mask);
    292     printf ("adult[%d] for mask %s, expected: %d, got: %d\n",
    293             n, TALER_age_mask_to_string (&test[n].mask), test[n].expected, l);
    294     if (test[n].expected != l)
    295       return GNUNET_SYSERR;
    296   }
    297   printf ("done with adult\n");
    298 
    299   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 test_attestation (void)
    310 {
    311   uint8_t age;
    312   for (age = 0; age < 33; age++)
    313   {
    314     enum GNUNET_GenericReturnValue ret;
    315     struct TALER_AgeCommitmentProof acp[3] = {0};
    316     struct TALER_AgeAttestationP at = {0};
    317     uint8_t age_group = TALER_get_age_group (&age_mask, age);
    318     struct GNUNET_HashCode seed;
    319 
    320     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    321                                 &seed,
    322                                 sizeof(seed));
    323     TALER_age_restriction_commit (&age_mask,
    324                                   age,
    325                                   &seed,
    326                                   &acp[0]);
    327     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     for (uint8_t i = 0; i<2; i++)
    335     {
    336       struct GNUNET_HashCode salt;
    337       GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    338                                   &salt,
    339                                   sizeof (salt));
    340       GNUNET_assert (GNUNET_OK ==
    341                      TALER_age_commitment_proof_derive (&acp[i],
    342                                                         &salt,
    343                                                         &acp[i + 1]));
    344     }
    345 
    346     for (uint8_t i = 0; i < 3; i++)
    347     {
    348       for (uint8_t min = 0; min < 22; min++)
    349       {
    350         uint8_t min_group = TALER_get_age_group (&age_mask, min);
    351         ret = TALER_age_commitment_attest (&acp[i],
    352                                            min,
    353                                            &at);
    354         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         if (min_group <= age_group &&
    364             GNUNET_OK != ret)
    365         {
    366           GNUNET_break (0);
    367           ret = GNUNET_SYSERR;
    368           break;
    369         }
    370 
    371         if (min_group > age_group &&
    372             GNUNET_NO != ret)
    373         {
    374           GNUNET_break (0);
    375           ret = GNUNET_SYSERR;
    376           break;
    377         }
    378 
    379         if (min_group > age_group)
    380           continue;
    381 
    382         ret = TALER_age_commitment_verify (&acp[i].commitment,
    383                                            min,
    384                                            &at);
    385         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         if (GNUNET_OK != ret)
    395         {
    396           GNUNET_break (0);
    397           break;
    398         }
    399       }
    400       TALER_age_commitment_proof_free (&acp[i]);
    401     }
    402 
    403     if (GNUNET_SYSERR == ret)
    404     {
    405       GNUNET_break (0);
    406       return ret;
    407     }
    408   }
    409   return GNUNET_OK;
    410 }
    411 
    412 
    413 int
    414 main (int argc,
    415       const char *const argv[])
    416 {
    417   (void) argc;
    418   (void) argv;
    419   GNUNET_log_setup ("test-age-restriction",
    420                     "INFO",
    421                     NULL);
    422   if (GNUNET_OK != test_groups ())
    423     return 1;
    424   if (GNUNET_OK != test_lowest ())
    425     return 2;
    426   if (GNUNET_OK != test_attestation ())
    427   {
    428     GNUNET_break (0);
    429     return 3;
    430   }
    431   if (GNUNET_OK != test_dates ())
    432     return 4;
    433   if (GNUNET_OK != test_adult ())
    434     return 5;
    435   return 0;
    436 }
    437 
    438 
    439 /* end of test_age_restriction.c */