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 */