donau

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

commit 195897bf2bfaa14440e252b15e00ca2a090d253c
parent 422167c8fbda055bce8abcd81a8e7442052f467d
Author: Matyja Lukas Adam <lukas.matyja@students.bfh.ch>
Date:   Tue, 11 Jun 2024 23:17:40 +0200

some changes

Diffstat:
Mverification-app/app/build.gradle | 19+++++++++++++++++++
Mverification-app/app/src/main/AndroidManifest.xml | 3++-
Mverification-app/app/src/main/java/taler/donau/verification/Results.java | 119++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mverification-app/app/src/main/res/layout/fragment_results.xml | 1+
Mverification-app/app/src/main/res/values/colors.xml | 2++
Mverification-app/settings.gradle | 1+
6 files changed, 143 insertions(+), 2 deletions(-)

diff --git a/verification-app/app/build.gradle b/verification-app/app/build.gradle @@ -30,6 +30,23 @@ android { buildFeatures { viewBinding true } + packagingOptions { + // Exclude specific files or directories that might cause conflicts + exclude 'META-INF/DEPENDENCIES' + exclude 'META-INF/LICENSE' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE' + exclude 'META-INF/NOTICE.txt' + exclude 'META-INF/ASL2.0' + exclude 'META-INF/*.kotlin_module' + + // Merge specific files if needed + merge 'META-INF/LGPL2.1' + merge 'META-INF/AL2.0' + + // Exclude everything under META-INF to avoid conflicts + resources.excludes.add("META-INF/**/*") + } } @@ -44,6 +61,8 @@ dependencies { implementation 'androidx.navigation:navigation-fragment:2.7.5' implementation 'androidx.navigation:navigation-ui:2.7.5' implementation 'com.github.yuriy-budiyev:code-scanner:2.3.2' + // https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk18on + implementation group: 'org.bouncycastle', name: 'bcpkix-jdk18on', version: '1.78.1' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/verification-app/app/src/main/AndroidManifest.xml b/verification-app/app/src/main/AndroidManifest.xml @@ -2,7 +2,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> - <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.CAMERA" + android:required="true"/> <uses-feature android:name="android.hardware.camera" 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 @@ -2,20 +2,137 @@ package taler.donau.verification; import android.content.Intent; import android.os.Bundle; +import android.view.View; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.signers.Ed25519Signer; +import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil; + public class Results extends AppCompatActivity { + // QR-string: YEAR:AMOUNT:FRACTION:TAXID:TAXIDSALT:EDD25519SIGNATURE:PUBLICKEY + // Base64 encoded: SALT,TAXID,SIGNATURE, PUBLICKEY (public key only temporary for testing) + + private final int NUMBER_OF_ARGUMENTS = 7; + // hard coded (only temporary for testing) + private final int SIGNATURECODE = 1500; + // uint64_t + private Long amount; + // uint32_t + private int fraction; + // uint64_t + private long year; + private byte[] taxid; + private byte[] salt; + private byte[] eddsaSignature; + private byte[] publicKey; + TextView textView; + + public enum SignatureStatus { + INVALID_NUMBER_OF_ARGUMENTS, + MALFORMED_ARGUMENT, + SIGNATURE_INVALID, + SIGNATURE_VALID; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_results); - TextView textView = findViewById(R.id.textView); + textView = findViewById(R.id.textView); Intent intent = getIntent(); textView.setText(intent.getStringExtra("QR-String")); + String[] parts = intent.getStringExtra("QR-String").split("/"); + if (parts == null || parts.length != NUMBER_OF_ARGUMENTS) { + statusHandling(SignatureStatus.INVALID_NUMBER_OF_ARGUMENTS); + return; + } + + try { + year = Long.parseUnsignedLong(parts[0]); + amount = Long.parseUnsignedLong(parts[1]); + fraction = Integer.parseInt(parts[2]); + taxid = Base64.getDecoder().decode(parts[3]); + salt = Base64.getDecoder().decode(parts[4]); + eddsaSignature = Base64.getDecoder().decode(parts[5]); + publicKey = Base64.getDecoder().decode(parts[6]); + } catch (Exception e) { + statusHandling(SignatureStatus.MALFORMED_ARGUMENT); + return; + } + + checkSignature(); + + } + + private void checkSignature() { + AsymmetricKeyParameter publicKeyParameters = OpenSSHPublicKeyUtil.parsePublicKey(publicKey); + Signer verifier = new Ed25519Signer(); + verifier.init(false, publicKeyParameters); + byte[] yearBytes = parseLongToBytes(year); + // TODO: hash the salted tax id to a byte array + // TODO: translate the amount to TALER AMOUNT NBO + // use ByteBuffer.order(ByteOrder.BIG_ENDIAN) to change byteorder + + byte[] message = yearBytes; // ..... + verifier.update(message, 0, message.length); + if (verifier.verifySignature(eddsaSignature)) { + statusHandling(SignatureStatus.SIGNATURE_VALID); + } else { + statusHandling(SignatureStatus.SIGNATURE_INVALID); + } + + } + + // TODO: move strings to res/values/strings + private void statusHandling(SignatureStatus es) { + View rootView = findViewById(R.id.root_view); + switch (es) { + case INVALID_NUMBER_OF_ARGUMENTS: + textView.setText("Invalid number of arguments!"); + rootView.setBackgroundResource(R.color.red); + break; + case MALFORMED_ARGUMENT: + textView.setText("Malformed argument!"); + rootView.setBackgroundResource(R.color.red); + break; + case SIGNATURE_INVALID: + textView.setText("Donation Statment signature is invalid!"); + rootView.setBackgroundResource(R.color.red); + break; + case SIGNATURE_VALID: + textView.setText("Donation Statment signature is valid!"); + rootView.setBackgroundResource(R.color.green); + break; + } + } + + private byte[] parseIntToBytes(int intValue) { + return new byte[] { + (byte)(intValue >> 24), + (byte)(intValue >> 16), + (byte)(intValue >> 8), + (byte)intValue}; + } + + private byte[] parseLongToBytes(Long longValue) { + return new byte[] { + (byte)(longValue >> 56), + (byte)(longValue >> 48), + (byte)(longValue >> 40), + (byte)(longValue >> 32), + (byte)(longValue >> 24), + (byte)(longValue >> 16), + (byte)(longValue >> 8), + (byte) (longValue >> 0)}; } } \ No newline at end of file diff --git a/verification-app/app/src/main/res/layout/fragment_results.xml b/verification-app/app/src/main/res/layout/fragment_results.xml @@ -1,4 +1,5 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/root_view" android:layout_width="match_parent" android:layout_height="match_parent"> diff --git a/verification-app/app/src/main/res/values/colors.xml b/verification-app/app/src/main/res/values/colors.xml @@ -7,4 +7,6 @@ <color name="teal_700">#FF018786</color> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> + <color name="red">#870C0C</color> + <color name="green">#045F06</color> </resources> \ No newline at end of file diff --git a/verification-app/settings.gradle b/verification-app/settings.gradle @@ -13,5 +13,6 @@ dependencyResolutionManagement { maven { url 'https://jitpack.io' } } } + rootProject.name = "Donau-Verification" include ':app'