Unverified Commit ccdee715 authored by Joseph Mark's avatar Joseph Mark Committed by GitHub
Browse files

feat: sign with seed reference (#580)



* Decrypt and sign without seed crossing bridge

- decrypt the seed in rust and return a raw pointer as i64
- from ios return int64_t
- from android return double using Double.longBitsToDouble
- in typescript use number
- sign messages by passing the seed reference and converting back to a
String pointer in rust

* Comment new functions in native.ts

* Remove debug code

* lint

* lint fixes
Co-authored-by: Hanwen Cheng's avatarHanwen Cheng <heawen.cheng@gmail.com>
parent 8781d0a1
Pipeline #85937 failed with stages
in 3 minutes and 35 seconds
......@@ -164,7 +164,51 @@ public class EthkeyBridge extends ReactContextBaseJavaModule {
}
}
private static native String ethkeyBrainwalletAddress(String seed);
@ReactMethod
public void decryptDataRef(String data, String password, Promise promise) {
try {
// `long` is incompatible with the bridge so pass as a double
double d = Double.longBitsToDouble(ethkeyDecryptDataRef(data, password));
if (Double.isNaN(d)) {
promise.reject("reference is nan", "reference is nan");
} else {
promise.resolve(d);
}
} catch (Exception e) {
promise.reject("decrypted ref", "decrypted ref");
}
}
@ReactMethod
public void destroyDataRef(double data_ref, Promise promise) {
try {
ethkeyDestroyDataRef(Double.doubleToRawLongBits(data_ref));
promise.resolve(0);
} catch (Exception e) {
promise.reject("destroy ref", "destroy ref");
}
}
@ReactMethod
public void brainWalletSignWithRef(double seed_ref, String message, Promise promise) {
try {
promise.resolve(ethkeyBrainwalletSignWithRef(Double.doubleToRawLongBits(seed_ref), message));
} catch (Exception e) {
promise.reject("invalid brain wallet phrase", "invalid brain wallet phrase");
}
}
@ReactMethod
public void substrateSignWithRef(double seed_ref, String message, Promise promise) {
try {
String s = substrateBrainwalletSignWithRef(Double.doubleToRawLongBits(seed_ref), message);
promise.resolve(s);
} catch (Exception e) {
promise.reject("invalid substrate phrase", "invalid substrate phrase");
}
}
private static native String ethkeyBrainwalletAddress(String seed);
private static native String ethkeyBrainwalletBIP39Address(String seed);
private static native String ethkeyBrainwalletSign(String seed, String message);
private static native String ethkeyRlpItem(String data, int position);
......@@ -180,4 +224,8 @@ public class EthkeyBridge extends ReactContextBaseJavaModule {
private static native String substrateBrainwalletAddress(String seed, int prefix);
private static native String substrateBrainwalletSign(String seed, String message);
private static native boolean schnorrkelVerify(String seed, String message, String signature);
private static native long ethkeyDecryptDataRef(String data, String password);
private static native void ethkeyDestroyDataRef(long data_ref);
private static native String ethkeyBrainwalletSignWithRef(long seed_ref, String message);
private static native String substrateBrainwalletSignWithRef(long seed_ref, String message);
}
......@@ -52,5 +52,9 @@ RCT_EXTERN_METHOD(substrateAddress:(NSString*)seed version:(NSUInteger*)version
RCT_EXTERN_METHOD(substrateSign:(NSString*)seed message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(blake2b:(NSString*)data resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(schnorrkelVerify: (NSString*)seed message:(NSString*)message signature:(NSString*)signature resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(decryptDataRef:(NSString*)data password:(NSString*)password resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(destroyDataRef:(int64_t)dataRef resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(brainWalletSignWithRef:(int64_t)seedRef message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateSignWithRef:(int64_t)seedRef message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end
......@@ -213,4 +213,48 @@ class EthkeyBridge: NSObject {
reject("Failed to verify signature.", nil, nil)
}
}
@objc func decryptDataRef(_ data: String, password: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
var data_ptr = data.asPtr()
var password_ptr = password.asPtr()
let data_ref = decrypt_data_ref(&error, &data_ptr, &password_ptr)
if error == 0 {
resolve(data_ref)
} else {
reject("error in decrypt_data_ref", nil, nil)
}
}
@objc func destroyDataRef(_ data_ref: Int64, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
destroy_data_ref(&error, data_ref)
if error == 0 {
resolve(0)
} else {
reject("error in destroy_data_ref", nil, nil)
}
}
@objc func brainWalletSignWithRef(_ seed_ref: Int64, message: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
var message_ptr = message.asPtr()
let signature_rust_str = ethkey_brainwallet_sign_with_ref(&error, seed_ref, &message_ptr)
let signature_rust_str_ptr = rust_string_ptr(signature_rust_str)
let signature = String.fromStringPtr(ptr: signature_rust_str_ptr!.pointee)
rust_string_ptr_destroy(signature_rust_str_ptr)
rust_string_destroy(signature_rust_str)
resolve(signature)
}
@objc func substrateSignWithRef(_ seed_ref: Int64, message: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
var message_ptr = message.asPtr()
let signature_rust_str = substrate_brainwallet_sign_with_ref(&error, seed_ref, &message_ptr)
let signature_rust_str_ptr = rust_string_ptr(signature_rust_str)
let signature = String.fromStringPtr(ptr: signature_rust_str_ptr!.pointee)
rust_string_ptr_destroy(signature_rust_str_ptr)
rust_string_destroy(signature_rust_str)
resolve(signature)
}
}
......@@ -78,3 +78,11 @@ struct rust_string* substrate_brainwallet_address(unsigned* error, const struct
struct rust_string* substrate_brainwallet_sign(unsigned* error, const struct rust_string_ptr* seed, const struct rust_string_ptr* data);
struct rust_string* schnorrkel_verify(unsigned* error, const struct rust_string_ptr* seed, const struct rust_string_ptr* msg, const struct rust_string_ptr* signature);
int64_t decrypt_data_ref(unsigned* error, const struct rust_string_ptr* encrypted_data, const struct rust_string_ptr* password);
void destroy_data_ref(unsigned* error, int64_t data_ref);
struct rust_string* ethkey_brainwallet_sign_with_ref(unsigned* error, int64_t seed_ref, const struct rust_string_ptr* message);
struct rust_string* substrate_brainwallet_sign_with_ref(unsigned* error, int64_t seed_ref, const struct rust_string_ptr* data);
......@@ -229,6 +229,48 @@ export! {
let signature: Vec<u8> = signature.from_hex().ok()?;
keypair.verify_signature(&message, &signature)
}
@Java_io_parity_signer_EthkeyBridge_ethkeyDecryptDataRef
fn decrypt_data_ref(data: &str, password: String) -> Option<i64> {
let password = Protected::new(password.into_bytes());
let crypto: Crypto = serde_json::from_str(data).ok()?;
let decrypted = crypto.decrypt(&password).ok()?;
let res = Box::into_raw(Box::new(String::from_utf8(decrypted).ok())) as i64;
Some(res)
}
@Java_io_parity_signer_EthkeyBridge_ethkeyDestroyDataRef
fn destroy_data_ref(data_ref: i64) -> () {
unsafe { Box::from_raw(data_ref as *mut String) };
}
@Java_io_parity_signer_EthkeyBridge_ethkeyBrainwalletSignWithRef
fn ethkey_brainwallet_sign_with_ref(seed_ref: i64, message: &str) -> Option<String> {
let seed = unsafe { Box::from_raw(seed_ref as *mut String) };
let (_, keypair) = KeyPair::from_auto_phrase(&seed);
let message: Vec<u8> = message.from_hex().ok()?;
let signature = keypair.sign(&message).ok()?;
// so that the reference remains valid
let _ = Box::into_raw(seed) as i64;
Some(signature.to_hex())
}
@Java_io_parity_signer_EthkeyBridge_substrateBrainwalletSignWithRef
fn substrate_brainwallet_sign_with_ref(seed_ref: i64, message: &str) -> Option<String> {
let seed = unsafe { Box::from_raw(seed_ref as *mut String) };
let keypair = sr25519::KeyPair::from_suri(&seed)?;
let message: Vec<u8> = message.from_hex().ok()?;
let signature = keypair.sign(&message);
// so that the reference remains valid
let _ = Box::into_raw(seed) as i64;
Some(signature.to_hex())
}
}
#[cfg(test)]
......
......@@ -102,6 +102,36 @@ impl<'a> Argument<'static> for &'a str {
}
}
#[cfg(not(feature = "jni"))]
impl Argument<'static> for i64 {
type Ext = i64;
type Env = Cell<u32>;
fn convert(_: &Self::Env, val: Self::Ext) -> Self {
val
}
}
#[cfg(not(feature = "jni"))]
impl Return<'static> for () {
type Ext = *mut std::ffi::c_void;
type Env = Cell<u32>;
fn convert(_: &Self::Env, _val: Self) -> Self::Ext {
std::ptr::null_mut()
}
}
#[cfg(not(feature = "jni"))]
impl Return<'static> for i64 {
type Ext = i64;
type Env = Cell<u32>;
fn convert(_: &Self::Env, val: Self) -> Self::Ext {
val
}
}
#[cfg(not(feature = "jni"))]
impl Return<'static> for bool {
type Ext = u8;
......@@ -201,6 +231,36 @@ impl<'jni> Argument<'jni> for String {
}
}
#[cfg(feature = "jni")]
impl<'jni> Argument<'jni> for i64 {
type Ext = jni::sys::jlong;
type Env = JNIEnv<'jni>;
fn convert(_env: &Self::Env, val: Self::Ext) -> Self {
val as i64
}
}
#[cfg(feature = "jni")]
impl<'jni> Return<'jni> for () {
type Ext = *mut std::ffi::c_void;
type Env = JNIEnv<'jni>;
fn convert(_env: &Self::Env, _val: Self) -> Self::Ext {
std::ptr::null_mut()
}
}
#[cfg(feature = "jni")]
impl<'jni> Return<'jni> for i64 {
type Ext = jni::sys::jlong;
type Env = JNIEnv<'jni>;
fn convert(_env: &Self::Env, val: Self) -> Self::Ext {
val as Self::Ext
}
}
#[cfg(feature = "jni")]
impl<'jni> Return<'jni> for String {
type Ext = jstring;
......
......@@ -157,3 +157,32 @@ export function schnorrkelVerify(
): Promise<boolean> {
return EthkeyBridge.schnorrkelVerify(seed, message, signature);
}
// Decrypt data and return a reference to the result
export function decryptDataRef(
data: string,
password: string
): Promise<number> {
return EthkeyBridge.decryptDataRef(data, password);
}
// Clean up the memory allocation made by decryptDataRef
export function destroyDataRef(dataRef: number): Promise<number> {
return EthkeyBridge.destroyDataRef(dataRef);
}
// Use a reference returned by decryptDataRef to sign a message
export function brainWalletSignWithRef(
seedRef: number,
message: string
): Promise<string> {
return EthkeyBridge.brainWalletSignWithRef(seedRef, message);
}
// Use a reference returned by decryptDataRef to sign a message
export function substrateSignWithRef(
seedRef: number,
message: string
): Promise<string> {
return EthkeyBridge.substrateSignWithRef(seedRef, message);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment