WIP: Maybe integrated libsodium + JNI
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
[submodule "gm-java"]
|
||||
path = gm-java
|
||||
url = ssh://gitea@git.qpq.swiss:21203/QPQ-AG/gm-java.git
|
||||
[submodule "app/src/main/cpp/libsodium"]
|
||||
path = app/src/main/cpp/libsodium
|
||||
url = https://git.qpq.swiss/QPQ-AG/libsodium.git
|
||||
|
||||
@@ -20,6 +20,19 @@ android {
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
version = "3.22.1"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
project("gaju-native")
|
||||
|
||||
set(LIBSODIUM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libsodium)
|
||||
set(SODIUM_CONFIG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sodium-config)
|
||||
|
||||
# Define macros for libsodium on Android
|
||||
add_definitions(-DCONFIG_H -D_GNU_SOURCE -D_POSIX_SOURCE)
|
||||
add_definitions(-DHAVE_MMAP -DHAVE_MPROTECT -DHAVE_MADVISE)
|
||||
|
||||
# Target-specific optimizations
|
||||
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
|
||||
add_definitions(-DHAVE_ARMCRYPTO -DHAVE_TI_MODE)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
|
||||
add_definitions(-DHAVE_TI_MODE)
|
||||
endif()
|
||||
|
||||
add_library(gaju-native SHARED
|
||||
gaju-jni.c
|
||||
|
||||
# Core & Runtime
|
||||
${LIBSODIUM_DIR}/src/libsodium/sodium/core.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/sodium/runtime.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/sodium/utils.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/sodium/version.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/sodium/codecs.c
|
||||
|
||||
# Random
|
||||
${LIBSODIUM_DIR}/src/libsodium/randombytes/randombytes.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c
|
||||
|
||||
# Hash (SHA)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_hash/crypto_hash.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_hash/sha256/hash_sha256.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_hash/sha256/cp/hash_sha256_cp.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_hash/sha512/hash_sha512.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_hash/sha512/cp/hash_sha512_cp.c
|
||||
|
||||
# Generic Hash (Blake2b)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_generichash/crypto_generichash.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_generichash/blake2b/generichash_blake2.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c
|
||||
|
||||
# Sign (Ed25519)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_sign/crypto_sign.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_sign/ed25519/sign_ed25519.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_sign/ed25519/ref10/keypair.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_sign/ed25519/ref10/open.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_sign/ed25519/ref10/sign.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_sign/ed25519/ref10/obsolete.c
|
||||
|
||||
# Scalarmult (Curve25519)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_scalarmult/crypto_scalarmult.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_scalarmult/curve25519/scalarmult_curve25519.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c
|
||||
|
||||
# Core Crypto
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_core/hchacha20/core_hchacha20.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_core/hsalsa20/core_hsalsa20.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_core/softaes/softaes.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_verify/verify.c
|
||||
|
||||
# OneTimeAuth (Poly1305)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_onetimeauth/crypto_onetimeauth.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_onetimeauth/poly1305/donna/poly1305_donna.c
|
||||
|
||||
# Stream (Chacha20/Salsa20)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_stream/crypto_stream.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_stream/chacha20/stream_chacha20.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_stream/salsa20/stream_salsa20.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_stream/salsa20/ref/salsa20_ref.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_stream/xsalsa20/stream_xsalsa20.c
|
||||
|
||||
# PWHash (Argon2)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/crypto_pwhash.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/argon2.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/argon2-core.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/argon2-encoding.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-ref.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/blake2b-long.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/pwhash_argon2i.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/pwhash_argon2id.c
|
||||
|
||||
# AEAD (Aegis)
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_aead/aegis128l/aead_aegis128l.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_aead/aegis128l/aegis128l_soft.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_aead/aegis256/aead_aegis256.c
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_aead/aegis256/aegis256_soft.c
|
||||
)
|
||||
|
||||
# Architecture specific optimizations
|
||||
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
|
||||
target_sources(gaju-native PRIVATE
|
||||
${LIBSODIUM_DIR}/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-neon.c
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(gaju-native PRIVATE
|
||||
${SODIUM_CONFIG_DIR}
|
||||
${LIBSODIUM_DIR}/src/libsodium/include
|
||||
${LIBSODIUM_DIR}/src/libsodium/include/sodium
|
||||
${LIBSODIUM_DIR}/src/libsodium/include/sodium/private
|
||||
)
|
||||
|
||||
# Link log library for Android
|
||||
find_library(log-lib log)
|
||||
target_link_libraries(gaju-native ${log-lib})
|
||||
|
||||
# Target for IDE discovery (read-only reference)
|
||||
add_custom_target(libsodium_all_headers SOURCES
|
||||
${LIBSODIUM_DIR}/src/libsodium/include/sodium.h
|
||||
)
|
||||
@@ -0,0 +1,83 @@
|
||||
#include <jni.h>
|
||||
#include <sodium.h>
|
||||
#include <string.h>
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_swiss_qpq_gajumobile_security_GajuNative_memzero(JNIEnv *env, jclass clazz, jbyteArray array) {
|
||||
if (array == NULL) return;
|
||||
jsize len = (*env)->GetArrayLength(env, array);
|
||||
jbyte *data = (*env)->GetByteArrayElements(env, array, NULL);
|
||||
if (data != NULL) {
|
||||
sodium_memzero(data, (size_t) len);
|
||||
(*env)->ReleaseByteArrayElements(env, array, data, 0);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_swiss_qpq_gajumobile_security_GajuNative_cryptoSignSeedKeypair(JNIEnv *env, jclass clazz, jbyteArray seed) {
|
||||
if (seed == NULL || (*env)->GetArrayLength(env, seed) != crypto_sign_SEEDBYTES) return NULL;
|
||||
|
||||
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_sign_SECRETKEYBYTES];
|
||||
jbyte *seed_data = (*env)->GetByteArrayElements(env, seed, NULL);
|
||||
|
||||
crypto_sign_seed_keypair(pk, sk, (unsigned char *) seed_data);
|
||||
(*env)->ReleaseByteArrayElements(env, seed, seed_data, JNI_ABORT);
|
||||
|
||||
jbyteArray result = (*env)->NewByteArray(env, crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES);
|
||||
(*env)->SetByteArrayRegion(env, result, 0, crypto_sign_PUBLICKEYBYTES, (jbyte *) pk);
|
||||
(*env)->SetByteArrayRegion(env, result, crypto_sign_PUBLICKEYBYTES, crypto_sign_SECRETKEYBYTES, (jbyte *) sk);
|
||||
|
||||
sodium_memzero(pk, sizeof pk);
|
||||
sodium_memzero(sk, sizeof sk);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_swiss_qpq_gajumobile_security_GajuNative_cryptoSignDetached(JNIEnv *env, jclass clazz, jbyteArray message, jbyteArray secretKey) {
|
||||
if (message == NULL || secretKey == NULL || (*env)->GetArrayLength(env, secretKey) != crypto_sign_SECRETKEYBYTES) return NULL;
|
||||
|
||||
jsize msg_len = (*env)->GetArrayLength(env, message);
|
||||
unsigned char sig[crypto_sign_BYTES];
|
||||
jbyte *msg_data = (*env)->GetByteArrayElements(env, message, NULL);
|
||||
jbyte *sk_data = (*env)->GetByteArrayElements(env, secretKey, NULL);
|
||||
|
||||
crypto_sign_detached(sig, NULL, (unsigned char *) msg_data, (unsigned long long) msg_len, (unsigned char *) sk_data);
|
||||
|
||||
(*env)->ReleaseByteArrayElements(env, message, msg_data, JNI_ABORT);
|
||||
(*env)->ReleaseByteArrayElements(env, secretKey, sk_data, JNI_ABORT);
|
||||
|
||||
jbyteArray result = (*env)->NewByteArray(env, crypto_sign_BYTES);
|
||||
(*env)->SetByteArrayRegion(env, result, 0, crypto_sign_BYTES, (jbyte *) sig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_swiss_qpq_gajumobile_security_GajuNative_cryptoSignVerifyDetached(JNIEnv *env, jclass clazz, jbyteArray signature, jbyteArray message, jbyteArray publicKey) {
|
||||
if (signature == NULL || message == NULL || publicKey == NULL) return JNI_FALSE;
|
||||
if ((*env)->GetArrayLength(env, signature) != crypto_sign_BYTES) return JNI_FALSE;
|
||||
if ((*env)->GetArrayLength(env, publicKey) != crypto_sign_PUBLICKEYBYTES) return JNI_FALSE;
|
||||
|
||||
jsize msg_len = (*env)->GetArrayLength(env, message);
|
||||
jbyte *sig_data = (*env)->GetByteArrayElements(env, signature, NULL);
|
||||
jbyte *msg_data = (*env)->GetByteArrayElements(env, message, NULL);
|
||||
jbyte *pk_data = (*env)->GetByteArrayElements(env, publicKey, NULL);
|
||||
|
||||
int res = crypto_sign_verify_detached((unsigned char *) sig_data, (unsigned char *) msg_data, (unsigned long long) msg_len, (unsigned char *) pk_data);
|
||||
|
||||
(*env)->ReleaseByteArrayElements(env, signature, sig_data, JNI_ABORT);
|
||||
(*env)->ReleaseByteArrayElements(env, message, msg_data, JNI_ABORT);
|
||||
(*env)->ReleaseByteArrayElements(env, publicKey, pk_data, JNI_ABORT);
|
||||
|
||||
return res == 0 ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
if (sodium_init() == -1) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
Submodule
+1
Submodule app/src/main/cpp/libsodium added at 77e1ce5d6d
@@ -0,0 +1,8 @@
|
||||
#ifndef sodium_version_H
|
||||
#define sodium_version_H
|
||||
|
||||
#define SODIUM_VERSION_STRING "1.0.22"
|
||||
#define SODIUM_LIBRARY_VERSION_MAJOR 26
|
||||
#define SODIUM_LIBRARY_VERSION_MINOR 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef sodium_version_H
|
||||
#define sodium_version_H
|
||||
|
||||
#define SODIUM_VERSION_STRING "1.0.22"
|
||||
#define SODIUM_LIBRARY_VERSION_MAJOR 26
|
||||
#define SODIUM_LIBRARY_VERSION_MINOR 1
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,7 @@ import swiss.qpq.gajumobile.security.BiometricHelper
|
||||
import swiss.qpq.gajumobile.security.SecurityManager
|
||||
import swiss.qpq.gajumobile.security.KeyManager
|
||||
import swiss.qpq.gajumobile.security.AccountAirlock
|
||||
import swiss.qpq.gajumobile.security.GajuNative
|
||||
import swiss.qpq.gajumobile.ui.screens.AccountNamingScreen
|
||||
import swiss.qpq.gajumobile.ui.screens.CreateWalletScreen
|
||||
import swiss.qpq.gajumobile.ui.screens.DashboardScreen
|
||||
@@ -54,7 +55,6 @@ import swiss.qpq.gajumobile.ui.theme.MyApplicationTheme
|
||||
import swiss.qpq.gajumaru.core.formatting.GajuFormat
|
||||
import swiss.qpq.gajumaru.core.tools.Grids
|
||||
import swiss.qpq.gajumaru.core.tools.NodeClient
|
||||
import swiss.qpq.gajumaru.core.tools.CryptoUtils
|
||||
import swiss.qpq.gajumaru.core.tools.TransactionService
|
||||
import java.math.BigInteger
|
||||
import java.util.UUID
|
||||
@@ -657,7 +657,7 @@ fun GajuRouter(
|
||||
)
|
||||
|
||||
// 4. Wipe seckey
|
||||
CryptoUtils.wipe(seckey)
|
||||
GajuNative.memzero(seckey)
|
||||
|
||||
// 5. Post tx
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.security.SecureRandom;
|
||||
import java.util.UUID;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import swiss.qpq.gajumaru.core.crypto.Ed25519;
|
||||
import swiss.qpq.gajumaru.core.crypto.Vault;
|
||||
import swiss.qpq.gajumaru.core.encoding.Mnemonic;
|
||||
import swiss.qpq.gajumaru.core.tools.CryptoUtils;
|
||||
@@ -69,8 +68,12 @@ public final class AccountAirlock {
|
||||
// 1. Decode mnemonic to seed
|
||||
seed = Mnemonic.decode(phrase);
|
||||
|
||||
// 2. Generate Public Key
|
||||
byte[] publicKey = Ed25519.publicKey(seed);
|
||||
// 2. Generate Public Key using Libsodium
|
||||
byte[] keypair = GajuNative.INSTANCE.cryptoSignSeedKeypair(seed);
|
||||
byte[] publicKey = new byte[32];
|
||||
System.arraycopy(keypair, 0, publicKey, 0, 32);
|
||||
// keypair contains sk too, but we only need pk here for the Account model
|
||||
GajuNative.INSTANCE.memzero(keypair);
|
||||
|
||||
// 3. Generate random Data Encryption Key (DEK)
|
||||
SecureRandom random = new SecureRandom();
|
||||
@@ -156,11 +159,21 @@ public final class AccountAirlock {
|
||||
envelope.getEncryptedData()
|
||||
);
|
||||
|
||||
return Ed25519.sign(plaintextPrivateKey, message);
|
||||
// Libsodium SK is 64 bytes: seed || public_key
|
||||
byte[] keypair = GajuNative.INSTANCE.cryptoSignSeedKeypair(plaintextPrivateKey);
|
||||
byte[] secretKey = new byte[64];
|
||||
System.arraycopy(keypair, 32, secretKey, 0, 64);
|
||||
|
||||
byte[] sig = GajuNative.INSTANCE.cryptoSignDetached(message, secretKey);
|
||||
|
||||
GajuNative.INSTANCE.memzero(keypair);
|
||||
GajuNative.INSTANCE.memzero(secretKey);
|
||||
|
||||
return sig;
|
||||
|
||||
} finally {
|
||||
if (plaintextDek != null) CryptoUtils.wipe(plaintextDek);
|
||||
if (plaintextPrivateKey != null) CryptoUtils.wipe(plaintextPrivateKey);
|
||||
if (plaintextPrivateKey != null) GajuNative.INSTANCE.memzero(plaintextPrivateKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +233,7 @@ public final class AccountAirlock {
|
||||
|
||||
} finally {
|
||||
if (plaintextDek != null) CryptoUtils.wipe(plaintextDek);
|
||||
if (plaintextPrivateKey != null) CryptoUtils.wipe(plaintextPrivateKey);
|
||||
if (plaintextPrivateKey != null) GajuNative.INSTANCE.memzero(plaintextPrivateKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package swiss.qpq.gajumobile.security
|
||||
|
||||
object GajuNative {
|
||||
init {
|
||||
System.loadLibrary("gaju-native")
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeros out the provided byte array using native memset.
|
||||
*/
|
||||
external fun memzero(array: ByteArray)
|
||||
|
||||
/**
|
||||
* Generates an Ed25519 keypair from a 32-byte seed.
|
||||
* @param seed 32-byte seed.
|
||||
* @return 96-byte array containing public key (32 bytes) followed by secret key (64 bytes).
|
||||
*/
|
||||
external fun cryptoSignSeedKeypair(seed: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Signs a message using Ed25519.
|
||||
* @param message The message to sign.
|
||||
* @param secretKey 64-byte secret key.
|
||||
* @return 64-byte detached signature.
|
||||
*/
|
||||
external fun cryptoSignDetached(message: ByteArray, secretKey: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Verifies an Ed25519 detached signature.
|
||||
* @param signature 64-byte signature.
|
||||
* @param message The message.
|
||||
* @param publicKey 32-byte public key.
|
||||
* @return true if valid, false otherwise.
|
||||
*/
|
||||
external fun cryptoSignVerifyDetached(signature: ByteArray, message: ByteArray, publicKey: ByteArray): Boolean
|
||||
}
|
||||
+1
-1
Submodule gm-java updated: 20e462ec03...954d8760ac
@@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
agp = "9.3.1"
|
||||
agp = "9.3.2"
|
||||
coreKtx = "1.10.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
|
||||
Reference in New Issue
Block a user