donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit ab6503604e4af554d873bbcbeac249d824e120b4
parent 019c7f21eb9238c2af776ea443ac11e20616953d
Author: Matyja Lukas Adam <lukas.matyja@students.bfh.ch>
Date:   Sat, 22 Jun 2024 02:34:00 +0200

app - add URI scheme and make taxid hash work

Diffstat:
Msrc/include/donau_crypto_lib.h | 2+-
Msrc/lib/donau_api_charity_delete.c | 18+++++++++---------
Msrc/testing/testing_api_cmd_charity_get.c | 46++++++++++++++++++++++++----------------------
Msrc/testing/testing_api_cmd_donation_statement_get.c | 14+++++++-------
Msrc/testing/testing_api_cmd_issue_receipts.c | 37++++++++++++++++++++-----------------
Mverification-app/app/src/main/AndroidManifest.xml | 14+++++++++++---
Mverification-app/app/src/main/cpp/verification.cpp | 39+++++++++++++++++----------------------
Mverification-app/app/src/main/java/taler/donau/verification/MainActivity.java | 7++-----
Mverification-app/app/src/main/java/taler/donau/verification/Results.java | 42+++++++++++++++++++++++++++++-------------
9 files changed, 120 insertions(+), 99 deletions(-)

diff --git a/src/include/donau_crypto_lib.h b/src/include/donau_crypto_lib.h @@ -194,7 +194,7 @@ struct DONAU_UniqueDonorIdentifierNonce */ struct DONAU_HashDonorTaxId { - struct GNUNET_HashCode hash; + unsigned char hash[512/8]; }; /** diff --git a/src/lib/donau_api_charity_delete.c b/src/lib/donau_api_charity_delete.c @@ -72,8 +72,8 @@ struct DONAU_CharityDeleteHandle */ static void handle_charity_delete_finished (void *cls, - long response_code, - const void *resp_obj) + long response_code, + const void *resp_obj) { struct DONAU_CharityDeleteHandle *cdh = cls; const json_t *j = resp_obj; @@ -162,10 +162,10 @@ DONAU_charity_delete ( cdh->cb_cls = cb_cls; char arg_str[sizeof (id) * 2 + 32]; GNUNET_snprintf (arg_str, - sizeof (arg_str), - "charities/%llu", - (unsigned long long) - id); + sizeof (arg_str), + "charities/%llu", + (unsigned long long) + id); cdh->url = TALER_url_join (url, arg_str, NULL); @@ -190,9 +190,9 @@ DONAU_charity_delete ( "Requesting charity deletion with URL `%s'.\n", cdh->url); cdh->job = GNUNET_CURL_job_add_with_ct_json (ctx, - eh, - &handle_charity_delete_finished, - cdh); + eh, + &handle_charity_delete_finished, + cdh); return cdh; } diff --git a/src/testing/testing_api_cmd_charity_get.c b/src/testing/testing_api_cmd_charity_get.c @@ -114,32 +114,34 @@ status_run (void *cls, ss->is = is; /* Get charity id from trait */ { - const struct TALER_TESTING_Command *charity_post_cmd; - const unsigned long long *charity_id; - - charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is, - ss->charity_reference); - - if (GNUNET_OK != - TALER_TESTING_get_trait_charity_id (charity_post_cmd, - &charity_id)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - ss->charity_id = (uint64_t) *(charity_id); + const struct TALER_TESTING_Command *charity_post_cmd; + const unsigned long long *charity_id; + + charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is, + ss-> + charity_reference); + + if (GNUNET_OK != + TALER_TESTING_get_trait_charity_id (charity_post_cmd, + &charity_id)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + ss->charity_id = (uint64_t) *(charity_id); } ss->cgh = DONAU_charity_get ( TALER_TESTING_interpreter_get_context (is), - TALER_TESTING_get_donau_url (is), + TALER_TESTING_get_donau_url (is), ss->charity_id, ss->bearer, &charity_status_cb, ss); } + /** * Cleanup the state from a "reserve status" CMD, and possibly * cancel a pending operation thereof. @@ -155,9 +157,9 @@ status_cleanup (void *cls, if (NULL != ss->cgh) { - // log incomplete command - TALER_TESTING_command_incomplete (ss->is, - cmd->label); + // log incomplete command + TALER_TESTING_command_incomplete (ss->is, + cmd->label); DONAU_charity_get_cancel (ss->cgh); ss->cgh = NULL; } @@ -167,9 +169,9 @@ status_cleanup (void *cls, struct TALER_TESTING_Command TALER_TESTING_cmd_charity_get (const char *label, - const char *charity_reference, - const struct DONAU_BearerToken *bearer, - unsigned int expected_response_code) + const char *charity_reference, + const struct DONAU_BearerToken *bearer, + unsigned int expected_response_code) { struct StatusState *ss; ss = GNUNET_new (struct StatusState); diff --git a/src/testing/testing_api_cmd_donation_statement_get.c b/src/testing/testing_api_cmd_donation_statement_get.c @@ -122,8 +122,8 @@ donation_statement_status_cb (void *cls, &ss->donation_statement. donation_statement_sig)) { - char *qr_string = generate_QR_string(&dsr->details.ok.donau_pub, - &ss->donation_statement); + char *qr_string = generate_QR_string (&dsr->details.ok.donau_pub, + &ss->donation_statement); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "qr-string: %s\n", qr_string); GNUNET_free(qr_string); @@ -165,11 +165,11 @@ status_run (void *cls, "issue-receipts"); if (GNUNET_OK != TALER_TESTING_get_trait_salted_tax_id_hash - (issue_receipts_cmd, &h_donor_tax_id) || - GNUNET_OK != TALER_TESTING_get_trait_donor_salt - (issue_receipts_cmd, &donor_salt) || - GNUNET_OK != TALER_TESTING_get_trait_donor_tax_id - (issue_receipts_cmd, &donor_tax_id)) + (issue_receipts_cmd, &h_donor_tax_id) || + GNUNET_OK != TALER_TESTING_get_trait_donor_salt + (issue_receipts_cmd, &donor_salt) || + GNUNET_OK != TALER_TESTING_get_trait_donor_tax_id + (issue_receipts_cmd, &donor_tax_id)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); diff --git a/src/testing/testing_api_cmd_issue_receipts.c b/src/testing/testing_api_cmd_issue_receipts.c @@ -472,9 +472,9 @@ cleanup (void *cls, ss->birh = NULL; } - GNUNET_free(ss->h_udis); + GNUNET_free (ss->h_udis); GNUNET_free (ss->alg_values); - GNUNET_free(ss->blinding_secrets); + GNUNET_free (ss->blinding_secrets); GNUNET_free (ss->bkps); GNUNET_free (ss); } @@ -497,8 +497,8 @@ issue_receipts_traits (void *cls, { struct StatusState *ss = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_donor_salt (ss->donor_salt), - TALER_TESTING_make_trait_donor_tax_id (ss->donor_tax_id), + TALER_TESTING_make_trait_donor_salt (ss->donor_salt), + TALER_TESTING_make_trait_donor_tax_id (ss->donor_tax_id), TALER_TESTING_make_trait_salted_tax_id_hash ( (const struct DONAU_HashDonorTaxId *) &ss->h_donor_tax_id), TALER_TESTING_make_trait_donation_receipts ( @@ -534,19 +534,22 @@ TALER_TESTING_cmd_issue_receipts (const char *label, ss->uses_cs = uses_cs; ss->donor_salt = (const char*) salt; ss->donor_tax_id = (const char*) donor_tax_id; - struct DONAU_HashDonorTaxId h_donor_tax_id; - struct GNUNET_HashContext *hash_context; - hash_context = GNUNET_CRYPTO_hash_context_start (); - - GNUNET_CRYPTO_hash_context_read (hash_context, - donor_tax_id, - sizeof((*donor_tax_id))), - GNUNET_CRYPTO_hash_context_read (hash_context, - salt, - sizeof((*salt))); - GNUNET_CRYPTO_hash_context_finish (hash_context, - &h_donor_tax_id.hash); - ss->h_donor_tax_id = h_donor_tax_id; + + // use libsodium SHA-512 Hash for compatibility reasons with the Donau Verify app. + unsigned char hash[512/8]; + crypto_hash_sha512_state state; + crypto_hash_sha512_init(&state); + + unsigned int tax_length; + for (tax_length = 0; donor_tax_id[tax_length]!= '\0'; ++tax_length); + unsigned int salt_length; + for (salt_length = 0; salt[salt_length]!= '\0'; ++salt_length); + + crypto_hash_sha512_update(&state, (const unsigned char*) donor_tax_id, tax_length); + crypto_hash_sha512_update(&state, (const unsigned char*) salt, salt_length); + + crypto_hash_sha512_final(&state, hash); + GNUNET_memcpy(ss->h_donor_tax_id.hash, hash, sizeof(hash)); { struct TALER_TESTING_Command cmd = { .cls = ss, diff --git a/verification-app/app/src/main/AndroidManifest.xml b/verification-app/app/src/main/AndroidManifest.xml @@ -21,15 +21,23 @@ tools:targetApi="31"> <activity android:name=".MainActivity" - android:exported="true" - android:label="@string/app_name"> + android:label="@string/app_name" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - <activity android:name=".Results"></activity> + <activity android:name=".Results" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + <data android:scheme="donau"/> + </intent-filter> + </activity> </application> </manifest> \ No newline at end of file diff --git a/verification-app/app/src/main/cpp/verification.cpp b/verification-app/app/src/main/cpp/verification.cpp @@ -391,7 +391,7 @@ struct GNUNET_HashCode */ struct DONAU_HashDonorTaxId { - struct GNUNET_HashCode hash; + unsigned char hash[512/8]; }; @@ -567,8 +567,8 @@ Java_taler_donau_verification_Results_ed25519_1verify( const char *const pub_str = reinterpret_cast<const char *const>(env->GetStringUTFChars( public_key, 0)); const char* total_amount = env->GetStringUTFChars(totalAmount, 0); - const char *const tax_id = reinterpret_cast<const char *const>(env->GetStringUTFChars(taxId, 0)); - //const char* salt = env->GetStringUTFChars(taxIdSalt, 0); + const unsigned char* tax_id = reinterpret_cast<const unsigned char*>(env->GetStringUTFChars(taxId, 0)); + const unsigned char* salt = reinterpret_cast<const unsigned char*>(env->GetStringUTFChars(taxIdSalt, 0)); unsigned long long donation_year; char dummy; @@ -580,29 +580,24 @@ Java_taler_donau_verification_Results_ed25519_1verify( { return -6; } - //struct GNUNET_HashContext *hash_context; - // hash_context = GNUNET_CRYPTO_hash_context_start (); - -/* GNUNET_CRYPTO_hash_context_read (hash_context, - tax_id, - sizeof((*tax_id))), - GNUNET_CRYPTO_hash_context_read (hash_context, - salt, - sizeof((*salt))); - GNUNET_CRYPTO_hash_context_finish (hash_context, - &h_donor_tax_id.hash);*/ struct DONAU_DonauPublicKeyP pub; struct DONAU_DonauSignatureP sig; struct DONAU_HashDonorTaxId h_donor_tax_id; - if (0 != - GNUNET_STRINGS_string_to_data (tax_id, - strlen (tax_id), - &h_donor_tax_id, - sizeof (h_donor_tax_id))) - { - return -2; - } + + crypto_hash_sha512_state state; + crypto_hash_sha512_init(&state); + + unsigned int tax_length; + for (tax_length = 0; tax_id[tax_length]!= '\0'; ++tax_length); + unsigned int salt_length; + for (salt_length = 0; salt[salt_length]!= '\0'; ++salt_length); + + crypto_hash_sha512_update(&state, tax_id, tax_length); + crypto_hash_sha512_update(&state, salt, salt_length); + + crypto_hash_sha512_final(&state, h_donor_tax_id.hash); + struct DONAU_DonationStatementConfirmationPS confirm = { .purpose.purpose = htonl (1500), .purpose.size = htonl (sizeof (confirm)), diff --git a/verification-app/app/src/main/java/taler/donau/verification/MainActivity.java b/verification-app/app/src/main/java/taler/donau/verification/MainActivity.java @@ -69,11 +69,8 @@ public class MainActivity extends AppCompatActivity { }); } }); - //temporary for debugging should be a valid cleartext message for signing and the public key - //sendRequestDialog("00000S00002XR000000000001W0000008NAN400000000000000AHAZC1CQTRFWWHM4C1CNGDSTYB4DPF9EBMHYC1W66CHMF3PVBBQDQAHGVAZN1W5ZHXE8BCBKCN7GWT94HWGW2JW4JH3GZ3XCJQJQ1M40001Z8:" + - // "EN7JZPG9FXEZ2NNYGVK92FZVWPM16RF2A4Q56JDFG295442XSP4Y19PMZ34R8Q2F6D9EEBWD6YEJVC32QSK2C5EMXQ8RVKXM1BRG81R:" + - // "E24CDJHGSPZG20ZSSTMTBREGCCP495WKETQYCYA9C93EPMZN4FEG"); - sendRequestDialog("donau://2024/EUR:15/N2NYR2SFNGZSS388R2SB0VKNWP8VCYJWQ93WR3RCCS38Y7DPPQEVEN31PNZA3RBZ3TWGPRQ6SAF1SMJ93S1R55R95271Y7TS5F5E388/EN7JZPG9FXEZ2NNYGVK92FZVWPM16RF2A4Q56JDFG295442XSP4Y19PMZ34R8Q2F6D9EEBWD6YEJVC32QSK2C5EMXQ8RVKXM1BRG81R/E24CDJHGSPZG20ZSSTMTBREGCCP495WKETQYCYA9C93EPMZN4FEG"); + //temporary for debugging + sendRequestDialog("donau://2024/EUR:15/7560001010000/1234/SAAM5BA1F9H4VT6T78CFC3X63HAMY2TXB597XBVZ0EMXEZ90QPJ3000BXDBJ3ECHGB8AEX9FFQ5BAXVSF6X6NXM98PY353F2R99PP1R/E24CDJHGSPZG20ZSSTMTBREGCCP495WKETQYCYA9C93EPMZN4FEG"); } diff --git a/verification-app/app/src/main/java/taler/donau/verification/Results.java b/verification-app/app/src/main/java/taler/donau/verification/Results.java @@ -18,6 +18,7 @@ package taler.donau.verification; import android.annotation.SuppressLint; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.TableLayout; @@ -30,6 +31,7 @@ import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.List; public class Results extends AppCompatActivity { static { @@ -38,12 +40,10 @@ public class Results extends AppCompatActivity { // QR-string : YEAR/TOTALAMOUNT/TAXID/TAXIDSALT/ED25519SIGNATURE/PUBKEY // CrockfordBase32 encoded: TAXID, SALT, SIGNATURE, PUBLICKEY - // FIXME: The public key should be requested directly from the Donau over HTTPS + // FIXME: The public key should be requested directly from the Donau over HTTPS, + // for this the donau base url is needed -> pass it over QR code? - private final int NUMBER_OF_ARGUMENTS = 5; - // hard coded (only temporary for testing) - private final int SIGNATURECODE = 1500; - // uint64_t + private final int NUMBER_OF_ARGUMENTS = 6; private String year; private String totalAmount; private String taxId; @@ -75,12 +75,27 @@ public class Results extends AppCompatActivity { totalView = findViewById(R.id.total); tableLayout = findViewById(R.id.tableLayout); tableLayout.setVisibility(View.INVISIBLE); - Intent intent = getIntent(); - String[] scheme = intent.getStringExtra("QR-String").split("//"); - if (scheme == null || scheme.length != 2 || !scheme[0].equals("donau:")) { - statusHandling(SignatureStatus.INVALID_SCHEME); - return; + Intent intent = getIntent(); + String scheme[]; + // handle URI scheme + if (null != intent.getData()) { + scheme = new String[2]; + Uri uri = intent.getData(); + List<String> pathSegments = uri.getPathSegments(); + StringBuilder fullPath = new StringBuilder(); + fullPath.append(uri.getHost()); + for (String segment : pathSegments) { + fullPath.append("/").append(segment); + } + scheme[1] = fullPath.toString(); + // handle self scanned QR code + } else { + scheme = intent.getStringExtra("QR-String").split("//"); + if (scheme == null || scheme.length != 2 || !scheme[0].equals("donau:")) { + statusHandling(SignatureStatus.INVALID_SCHEME); + return; + } } String[] parts = scheme[1].split("/"); if (parts == null || parts.length != NUMBER_OF_ARGUMENTS) { @@ -92,8 +107,9 @@ public class Results extends AppCompatActivity { year = parts[0]; totalAmount = parts[1]; taxId = parts[2]; - eddsaSignature = parts[3]; - publicKey = parts[4]; + salt = parts[3]; + eddsaSignature = parts[4]; + publicKey = parts[5]; } catch (Exception e) { statusHandling(SignatureStatus.MALFORMED_ARGUMENT); return; @@ -142,7 +158,7 @@ public class Results extends AppCompatActivity { tableLayout.setVisibility(View.VISIBLE); sigStatusView.setText(R.string.valid_signature); yearView.setText(year); - taxidView.setText("tbd"); + taxidView.setText(taxId); totalView.setText(totalAmount); rootView.setBackgroundResource(R.color.green); break;