verification.cpp (15596B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024 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 #include <jni.h> 18 #include <string> 19 #include <android/log.h> 20 #include <sys/endian.h> 21 22 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "verification", __VA_ARGS__) 23 24 // needed for libsodium 25 #include <sodium/crypto_sign.h> 26 #include <sodium/crypto_hash_sha512.h> 27 28 /** 29 * Maximum legal 'value' for an amount, based on IEEE double (for JavaScript compatibility). 30 */ 31 #define TALER_AMOUNT_MAX_VALUE (1LLU << 52) 32 33 /** 34 * Define as empty, GNUNET_PACKED should suffice, but this won't work on W32 35 */ 36 #define GNUNET_NETWORK_STRUCT_BEGIN 37 38 /** 39 * Define as empty, GNUNET_PACKED should suffice, but this won't work on W32; 40 */ 41 #define GNUNET_NETWORK_STRUCT_END 42 43 /** 44 * gcc-ism to get packed structs. 45 */ 46 #define GNUNET_PACKED __attribute__ ((packed)) 47 48 #define TALER_CURRENCY_LEN 12 49 50 /** 51 * @brief The "fraction" value in a `struct TALER_Amount` represents which 52 * fraction of the "main" value? 53 * 54 * Note that we need sub-cent precision here as transaction fees might 55 * be that low, and as we want to support microdonations. 56 * 57 * An actual `struct Amount a` thus represents 58 * "a.value + (a.fraction / #TALER_AMOUNT_FRAC_BASE)" units of "a.currency". 59 */ 60 #define TALER_AMOUNT_FRAC_BASE 100000000 61 62 enum GNUNET_GenericReturnValue 63 { 64 GNUNET_SYSERR = -1, 65 GNUNET_NO = 0, 66 GNUNET_OK = 1, 67 /* intentionally identical to #GNUNET_OK! */ 68 GNUNET_YES = 1, 69 }; 70 71 /** 72 * Public ECC key (always for curve Ed25519) encoded in a format 73 * suitable for network transmission and EdDSA signatures. Refer 74 * to section 5.1.3 of rfc8032, for a thorough explanation of how 75 * this value maps to the x- and y-coordinates. 76 */ 77 struct GNUNET_CRYPTO_EddsaPublicKey 78 { 79 /** 80 * Point Q consists of a y-value mod p (256 bits); the x-value is 81 * always positive. The point is stored in Ed25519 standard 82 * compact format. 83 */ 84 unsigned char q_y[256 / 8]; 85 }; 86 87 /** 88 * @brief an ECC signature using EdDSA. 89 * See cr.yp.to/papers.html#ed25519 90 */ 91 struct GNUNET_CRYPTO_EddsaSignature 92 { 93 /** 94 * R value. 95 */ 96 unsigned char r[256 / 8]; 97 98 /** 99 * S value. 100 */ 101 unsigned char s[256 / 8]; 102 }; 103 104 /** 105 * Regular online message signing key used by Donau. 106 */ 107 struct DONAU_DonauPublicKeyP 108 { 109 /** 110 * Donau uses EdDSA for non-blind signing. 111 */ 112 struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub; 113 114 }; 115 116 /** 117 * @brief Type of signature used by the donau for non-blind signatures. 118 */ 119 struct DONAU_DonauSignatureP 120 { 121 /** 122 * Donau uses EdDSA for for non-blind signatures. 123 */ 124 struct GNUNET_CRYPTO_EddsaSignature eddsa_sig; 125 }; 126 127 /** 128 * @brief header of what an ECC signature signs 129 * this must be followed by "size - 8" bytes of 130 * the actual signed data 131 */ 132 struct GNUNET_CRYPTO_EccSignaturePurpose 133 { 134 /** 135 * How many bytes does this signature sign? 136 * (including this purpose header); in network 137 * byte order (!). 138 */ 139 uint32_t size GNUNET_PACKED; 140 141 /** 142 * What does this signature vouch for? This 143 * must contain a GNUNET_SIGNATURE_PURPOSE_XXX 144 * constant (from gnunet_signatures.h). In 145 * network byte order! 146 */ 147 uint32_t purpose GNUNET_PACKED; 148 }; 149 150 GNUNET_NETWORK_STRUCT_BEGIN 151 152 /** 153 * @brief Amount, encoded for network transmission. 154 */ 155 struct TALER_AmountNBO 156 { 157 /** 158 * Value in the main currency, in NBO. 159 */ 160 uint64_t value GNUNET_PACKED; 161 162 /** 163 * Fraction (integer multiples of #TALER_AMOUNT_FRAC_BASE), in NBO. 164 */ 165 uint32_t fraction GNUNET_PACKED; 166 167 /** 168 * Type of the currency being represented. Length is 12. 169 */ 170 char currency[TALER_CURRENCY_LEN]; 171 }; 172 173 GNUNET_NETWORK_STRUCT_END 174 175 /** 176 * @brief Representation of monetary value in a given currency. 177 */ 178 struct TALER_Amount 179 { 180 /** 181 * Value (numerator of fraction) 182 */ 183 uint64_t value; 184 185 /** 186 * Fraction (integer multiples of #TALER_AMOUNT_FRAC_BASE). 187 */ 188 uint32_t fraction; 189 190 /** 191 * Currency string, left adjusted and padded with zeros. All zeros 192 * for "invalid" values. 193 */ 194 char currency[TALER_CURRENCY_LEN]; 195 }; 196 197 int 198 TALER_amount_is_valid (const struct TALER_Amount *amount) 199 { 200 if (amount->value > TALER_AMOUNT_MAX_VALUE) 201 { 202 return -1; 203 } 204 return ('\0' != amount->currency[0]) ? 1 : 0; 205 } 206 207 uint64_t 208 GNUNET_htonll (uint64_t n) 209 { 210 return (((uint64_t) htonl (n)) << 32) + htonl (n >> 32); 211 //return n; 212 } 213 214 void 215 TALER_amount_hton (struct TALER_AmountNBO *res, 216 const struct TALER_Amount *d) 217 { 218 if (1 != TALER_amount_is_valid (d)) { 219 res = NULL; 220 return; 221 } 222 res->value = GNUNET_htonll (d->value); 223 res->fraction = htonl (d->fraction); 224 for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++) 225 res->currency[i] = d->currency[i]; 226 } 227 228 /** 229 * Set @a a to "invalid". 230 * 231 * @param[out] a amount to set to invalid 232 */ 233 static void 234 invalidate (struct TALER_Amount *a) 235 { 236 memset (a, 237 0, 238 sizeof (struct TALER_Amount)); 239 } 240 241 enum GNUNET_GenericReturnValue 242 TALER_check_currency (const char *str) 243 { 244 if (strlen (str) >= TALER_CURRENCY_LEN) 245 { 246 return GNUNET_SYSERR; 247 } 248 /* validate str has only legal characters in it! */ 249 for (unsigned int i = 0; '\0' != str[i]; i++) 250 { 251 if ( ('A' > str[i]) || ('Z' < str[i]) ) 252 { 253 return GNUNET_SYSERR; 254 } 255 } 256 return GNUNET_OK; 257 } 258 259 enum GNUNET_GenericReturnValue 260 TALER_string_to_amount (const char *str, 261 struct TALER_Amount *amount) 262 { 263 int n; 264 uint32_t b; 265 const char *colon; 266 const char *value; 267 268 /* skip leading whitespace */ 269 while (isspace ( (unsigned char) str[0])) 270 str++; 271 if ('\0' == str[0]) 272 { 273 invalidate (amount); 274 return GNUNET_SYSERR; 275 } 276 277 /* parse currency */ 278 colon = strchr (str, (int) ':'); 279 if ( (NULL == colon) || 280 (colon == str) || 281 ((colon - str) >= TALER_CURRENCY_LEN) ) 282 { 283 invalidate (amount); 284 return GNUNET_SYSERR; 285 } 286 287 if (TALER_CURRENCY_LEN <= (colon - str)) return GNUNET_SYSERR; 288 for (unsigned int i = 0; i<colon - str; i++) 289 amount->currency[i] = str[i]; 290 /* 0-terminate *and* normalize buffer by setting everything to '\0' */ 291 memset (&amount->currency [colon - str], 292 0, 293 TALER_CURRENCY_LEN - (colon - str)); 294 if (GNUNET_OK != 295 TALER_check_currency (amount->currency)) 296 return GNUNET_SYSERR; 297 /* skip colon */ 298 value = colon + 1; 299 if ('\0' == value[0]) 300 { 301 invalidate (amount); 302 return GNUNET_SYSERR; 303 } 304 305 amount->value = 0; 306 amount->fraction = 0; 307 308 /* parse value */ 309 while ('.' != *value) 310 { 311 if ('\0' == *value) 312 { 313 /* we are done */ 314 return GNUNET_OK; 315 } 316 if ( (*value < '0') || 317 (*value > '9') ) 318 { 319 invalidate (amount); 320 return GNUNET_SYSERR; 321 } 322 n = *value - '0'; 323 if ( (amount->value * 10 < amount->value) || 324 (amount->value * 10 + n < amount->value) || 325 (amount->value > TALER_AMOUNT_MAX_VALUE) || 326 (amount->value * 10 + n > TALER_AMOUNT_MAX_VALUE) ) 327 { 328 invalidate (amount); 329 return GNUNET_SYSERR; 330 } 331 amount->value = (amount->value * 10) + n; 332 value++; 333 } 334 335 /* skip the dot */ 336 value++; 337 338 339 /* parse fraction */ 340 if ('\0' == *value) 341 { 342 invalidate (amount); 343 return GNUNET_SYSERR; 344 } 345 b = TALER_AMOUNT_FRAC_BASE / 10; 346 while ('\0' != *value) 347 { 348 if (0 == b) 349 { 350 invalidate (amount); 351 return GNUNET_SYSERR; 352 } 353 if ( (*value < '0') || 354 (*value > '9') ) 355 { 356 invalidate (amount); 357 return GNUNET_SYSERR; 358 } 359 n = *value - '0'; 360 amount->fraction += n * b; 361 b /= 10; 362 value++; 363 } 364 return GNUNET_OK; 365 } 366 367 enum GNUNET_GenericReturnValue 368 TALER_string_to_amount_nbo (const char *str, 369 struct TALER_AmountNBO *amount_nbo) 370 { 371 struct TALER_Amount amount; 372 373 if (GNUNET_OK != 374 TALER_string_to_amount (str, 375 &amount)) 376 return GNUNET_SYSERR; 377 TALER_amount_hton (amount_nbo, 378 &amount); 379 return GNUNET_OK; 380 } 381 382 /** 383 * Donor's hashed and salted unique donation identifier. 384 */ 385 struct DONAU_HashDonorTaxId 386 { 387 unsigned char hash[512 / 8]; 388 }; 389 390 391 GNUNET_NETWORK_STRUCT_BEGIN 392 393 /** 394 * @brief Format used to generate the signature/donation statement 395 * over the total amount and a donor identifier of a year. 396 */ 397 struct DONAU_DonationStatementConfirmationPS 398 { 399 /** 400 * Purpose must be #DONAU_SIGNATURE_DONAU_DONATION_STATEMENT. Signed 401 * by a `struct DONAU_DonauPublicKeyP` using EdDSA. 402 */ 403 struct GNUNET_CRYPTO_EccSignaturePurpose purpose; 404 405 /** 406 * Total amount donated of a specific @a year. 407 */ 408 struct TALER_AmountNBO amount_tot; 409 410 /** 411 * The hash of the identifier of the donor. 412 */ 413 struct DONAU_HashDonorTaxId i; 414 415 /** 416 * The corresponding year. 417 */ 418 uint32_t year; 419 420 }; 421 422 GNUNET_NETWORK_STRUCT_END 423 424 425 /** 426 * Get the decoded value corresponding to a character according to Crockford 427 * Base32 encoding. 428 * 429 * @param a a character 430 * @return corresponding numeric value 431 */ 432 static unsigned int 433 getValue__ (unsigned char a) 434 { 435 unsigned int dec; 436 437 switch (a) 438 { 439 case 'O': 440 case 'o': 441 a = '0'; 442 break; 443 444 case 'i': 445 case 'I': 446 case 'l': 447 case 'L': 448 a = '1'; 449 break; 450 451 /* also consider U to be V */ 452 case 'u': 453 case 'U': 454 a = 'V'; 455 break; 456 457 default: 458 break; 459 } 460 if ((a >= '0') && (a <= '9')) 461 return a - '0'; 462 if ((a >= 'a') && (a <= 'z')) 463 a = toupper (a); 464 /* return (a - 'a' + 10); */ 465 dec = 0; 466 if ((a >= 'A') && (a <= 'Z')) 467 { 468 if ('I' < a) 469 dec++; 470 if ('L' < a) 471 dec++; 472 if ('O' < a) 473 dec++; 474 if ('U' < a) 475 dec++; 476 return(a - 'A' + 10 - dec); 477 } 478 return -1; 479 } 480 481 int 482 GNUNET_STRINGS_string_to_data (const char *enc, 483 size_t enclen, 484 void *out, 485 size_t out_size) 486 { 487 size_t rpos; 488 size_t wpos; 489 unsigned int bits; 490 unsigned int vbit; 491 int ret; 492 int shift; 493 unsigned char *uout; 494 size_t encoded_len; 495 496 if (0 == enclen) 497 { 498 if (0 == out_size) 499 return 0; 500 return -1; 501 } 502 if (out_size >= SIZE_MAX) return -1; 503 encoded_len = out_size * 8; 504 uout = (unsigned char *) out; 505 wpos = out_size; 506 rpos = enclen; 507 if ((encoded_len % 5) > 0) 508 { 509 vbit = encoded_len % 5; /* padding! */ 510 shift = 5 - vbit; 511 bits = (ret = getValue__ (enc[--rpos])) >> shift; 512 } 513 else 514 { 515 vbit = 5; 516 shift = 0; 517 bits = (ret = getValue__ (enc[--rpos])); 518 } 519 if ((encoded_len + shift) / 5 != enclen) 520 return -1; 521 if (-1 == ret) 522 return -1; 523 while (wpos > 0) 524 { 525 if (0 == rpos) 526 { 527 return -1; 528 } 529 bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits; 530 if (-1 == ret) 531 return -1; 532 vbit += 5; 533 if (vbit >= 8) 534 { 535 uout[--wpos] = (unsigned char) bits; 536 bits >>= 8; 537 vbit -= 8; 538 } 539 } 540 if ((0 != rpos) || (0 != vbit)) 541 return -1; 542 return 0; 543 } 544 545 extern "C" 546 JNIEXPORT jint JNICALL 547 Java_net_taler_donauverificator_Results_ed25519_1verify( 548 JNIEnv *env, jobject thiz, 549 jstring year, 550 jstring totalAmount, 551 jstring taxId, 552 jstring taxIdSalt, 553 jstring signature, 554 jstring public_key) { 555 556 const char *const year_string = reinterpret_cast<const char *const>(env->GetStringUTFChars( 557 year, 0)); 558 const char *const s_str = reinterpret_cast<const char *const>(env->GetStringUTFChars( 559 signature, 0)); 560 const char *const pub_str = reinterpret_cast<const char *const>(env->GetStringUTFChars( 561 public_key, 0)); 562 const char* total_amount = env->GetStringUTFChars(totalAmount, 0); 563 const unsigned char* tax_id = reinterpret_cast<const unsigned char*>(env->GetStringUTFChars(taxId, 0)); 564 const unsigned char* salt = reinterpret_cast<const unsigned char*>(env->GetStringUTFChars(taxIdSalt, 0)); 565 566 unsigned long long donation_year; 567 char dummy; 568 if ( (NULL == year_string) || 569 (1 != sscanf (year_string, 570 "%llu%c", 571 &donation_year, 572 &dummy)) ) 573 { 574 return -6; 575 } 576 577 struct DONAU_DonauPublicKeyP pub; 578 struct DONAU_DonauSignatureP sig; 579 struct DONAU_HashDonorTaxId h_donor_tax_id; 580 581 crypto_hash_sha512_state state; 582 crypto_hash_sha512_init(&state); 583 584 unsigned int tax_length; 585 for (tax_length = 0; tax_id[tax_length]!= '\0'; ++tax_length); 586 unsigned int salt_length; 587 for (salt_length = 0; salt[salt_length]!= '\0'; ++salt_length); 588 589 // H = SHA-512( UTF8(tax_id) || 0x00 || UTF8(salt) || 0x00 ) 590 crypto_hash_sha512_update(&state, tax_id, tax_length); 591 unsigned char sep = 0x00; 592 crypto_hash_sha512_update(&state, &sep, 1); 593 crypto_hash_sha512_update(&state, salt, salt_length); 594 crypto_hash_sha512_update(&state, &sep, 1); 595 596 crypto_hash_sha512_final(&state, h_donor_tax_id.hash); 597 598 struct DONAU_DonationStatementConfirmationPS confirm = { 599 .purpose.purpose = htonl (1500), 600 .purpose.size = htonl (sizeof (confirm)), 601 .year = htonl (donation_year), 602 .i = h_donor_tax_id 603 }; 604 if (GNUNET_OK != TALER_string_to_amount_nbo (total_amount, 605 &confirm.amount_tot)) { 606 return -3; 607 } 608 if (0 != 609 GNUNET_STRINGS_string_to_data (s_str, 610 strlen (s_str), 611 &sig, 612 sizeof (sig))) 613 { 614 return -4; 615 } 616 if (0 != 617 GNUNET_STRINGS_string_to_data (pub_str, 618 strlen (pub_str), 619 &pub, 620 sizeof (pub))) 621 { 622 return -5; 623 } 624 size_t mlen = ntohl (confirm.purpose.size); 625 const unsigned char *m = (const unsigned char *) &confirm.purpose; 626 const unsigned char *s = (const unsigned char *) &sig.eddsa_sig; 627 const unsigned char *eddsa_pub = (const unsigned char*) &pub.eddsa_pub.q_y; 628 //verify function from libsodium (also used by GNUNET) 629 return crypto_sign_verify_detached (s, m, mlen, eddsa_pub); 630 }