montana/Montana-Protocol/Code/crates/mt-bindings/src/ffi_jni.rs
2026-05-26 21:14:51 +03:00

173 lines
5.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! JNI surface для Android Kotlin.
//!
//! Реэкспортирует те же функции что ffi_c.rs, но через JNI ABI с правильными
//! Java_<pkg>_<class>_<method> именами. Класс на стороне Kotlin: `quest.montana.app.MtBindings`.
//!
//! Сборка: `cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 -p mt-bindings build --release`.
//! Артефакт: `libmt_bindings.so` (3 ABI) → `Android/MontanaApp/app/src/main/jniLibs/{abi}/`.
#![cfg(target_os = "android")]
use jni::objects::{JByteArray, JClass, JString};
use jni::sys::{jbyteArray, jint};
use jni::JNIEnv;
use mt_codec::domain;
use mt_crypto::{keypair_from_seed, sign as mldsa_sign, verify as mldsa_verify, PublicKey, SecretKey, Signature};
use mt_mnemonic::{entropy_to_mnemonic, mldsa_seed_for_role, mnemonic_to_master_seed};
use mt_state::derive_account_id;
const MT_SUITE_MLDSA65: u16 = 0x0001;
/// Соответствует error codes из ffi_c.rs (отрицательные).
/// На Kotlin стороне трактуются как throw IllegalArgumentException/IllegalStateException.
#[no_mangle]
pub extern "system" fn Java_quest_montana_app_MtBindings_nativeAbiVersion(
_env: JNIEnv,
_class: JClass,
) -> jint {
super::ABI_VERSION as jint
}
/// 24 слова → 64-байт master_seed. Возвращает byte[64] или null если ошибка.
#[no_mangle]
pub extern "system" fn Java_quest_montana_app_MtBindings_nativeMnemonicToMasterSeed<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
mnemonic_jstr: JString<'local>,
) -> jbyteArray {
let null = std::ptr::null_mut();
let mnemonic: String = match env.get_string(&mnemonic_jstr) {
Ok(s) => s.into(),
Err(_) => return null,
};
let seed = match mnemonic_to_master_seed(&mnemonic) {
Ok(s) => s,
Err(_) => return null,
};
match env.byte_array_from_slice(&seed) {
Ok(arr) => arr.into_raw(),
Err(_) => null,
}
}
/// 32 байта entropy → 24-словная UTF-8 мнемоника.
#[no_mangle]
pub extern "system" fn Java_quest_montana_app_MtBindings_nativeEntropyToMnemonic<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
entropy: JByteArray<'local>,
) -> jni::sys::jstring {
let null = std::ptr::null_mut();
let bytes = match env.convert_byte_array(entropy) {
Ok(b) if b.len() == 32 => b,
_ => return null,
};
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
let mnemonic = entropy_to_mnemonic(&arr);
match env.new_string(mnemonic) {
Ok(s) => s.into_raw(),
Err(_) => null,
}
}
/// 24 слова → ML-DSA-65 account: byte[1952 + 4032 + 32] = pk||sk||account_id.
#[no_mangle]
pub extern "system" fn Java_quest_montana_app_MtBindings_nativeAccountFromMnemonic<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
mnemonic_jstr: JString<'local>,
) -> jbyteArray {
let null = std::ptr::null_mut();
let mnemonic: String = match env.get_string(&mnemonic_jstr) {
Ok(s) => s.into(),
Err(_) => return null,
};
let master = match mnemonic_to_master_seed(&mnemonic) {
Ok(s) => s,
Err(_) => return null,
};
let acc_seed = mldsa_seed_for_role(&master, domain::ACCOUNT_KEY);
let (pk, sk) = match keypair_from_seed(&acc_seed) {
Ok(kp) => kp,
Err(_) => return null,
};
let account_id = derive_account_id(MT_SUITE_MLDSA65, pk.as_bytes());
let mut buf =
Vec::with_capacity(super::MT_MLDSA_PUBKEY_SIZE + super::MT_MLDSA_SECKEY_SIZE + super::MT_ACCOUNT_ID_LEN);
buf.extend_from_slice(pk.as_bytes());
buf.extend_from_slice(sk.as_bytes());
buf.extend_from_slice(&account_id);
match env.byte_array_from_slice(&buf) {
Ok(arr) => arr.into_raw(),
Err(_) => null,
}
}
/// ML-DSA-65 sign(seckey[4032], msg) → signature[3309] (или null при ошибке).
#[no_mangle]
pub extern "system" fn Java_quest_montana_app_MtBindings_nativeSign<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
seckey: JByteArray<'local>,
msg: JByteArray<'local>,
) -> jbyteArray {
let null = std::ptr::null_mut();
let sk_bytes = match env.convert_byte_array(seckey) {
Ok(b) => b,
Err(_) => return null,
};
let m = match env.convert_byte_array(msg) {
Ok(b) => b,
Err(_) => return null,
};
let sk = match SecretKey::from_slice(&sk_bytes) {
Some(k) => k,
None => return null,
};
let sig = match mldsa_sign(&sk, &m) {
Ok(s) => s,
Err(_) => return null,
};
match env.byte_array_from_slice(sig.as_bytes()) {
Ok(arr) => arr.into_raw(),
Err(_) => null,
}
}
/// ML-DSA-65 verify. Возвращает 1 (valid) / 0 (invalid) / -1 (input error).
#[no_mangle]
pub extern "system" fn Java_quest_montana_app_MtBindings_nativeVerify<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
pubkey: JByteArray<'local>,
msg: JByteArray<'local>,
sig: JByteArray<'local>,
) -> jint {
let pk_bytes = match env.convert_byte_array(pubkey) {
Ok(b) => b,
Err(_) => return -1,
};
let m = match env.convert_byte_array(msg) {
Ok(b) => b,
Err(_) => return -1,
};
let sig_bytes = match env.convert_byte_array(sig) {
Ok(b) => b,
Err(_) => return -1,
};
let pk = match PublicKey::from_slice(&pk_bytes) {
Some(k) => k,
None => return -1,
};
let signature = match Signature::from_slice(&sig_bytes) {
Some(s) => s,
None => return -1,
};
if mldsa_verify(&pk, &m, &signature) { 1 } else { 0 }
}