taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

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 }