Skip to content

Commit

Permalink
Monerujo for Ledger Nano S (#377)
Browse files Browse the repository at this point in the history
  • Loading branch information
m2049r authored Aug 3, 2018
1 parent 679bae5 commit bf5ed79
Show file tree
Hide file tree
Showing 63 changed files with 2,637 additions and 250 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 102
versionName "1.5.12 'Maximum Nachos'"
versionCode 111
versionName "1.6.1 'Nano S'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
Expand Down
28 changes: 27 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

<application
android:name=".XmrWalletApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:name=".XmrWalletApplication"
android:theme="@style/MyMaterialTheme">

<activity
Expand All @@ -28,13 +28,39 @@
android:name=".LoginActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>

<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />

</activity>

<!--activity
android:name=".util.UsbEventReceiverActivity"
android:excludeFromRecents="true"
android:exported="false"
android:label="@string/app_name"
android:noHistory="true"
android:process=":UsbEventReceiverActivityProcess"
android:taskAffinity="com.m2049r.xmrwallet.taskAffinityUsbEventReceiver"
android:theme="@style/Theme.Transparent">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
</activity-->

<service
android:name=".service.WalletService"
android:description="@string/service_description"
Expand Down
147 changes: 147 additions & 0 deletions app/src/main/cpp/monerujo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static jclass class_ArrayList;
static jclass class_WalletListener;
static jclass class_TransactionInfo;
static jclass class_Transfer;
static jclass class_Ledger;

std::mutex _listenerMutex;

Expand All @@ -58,6 +59,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
jenv->FindClass("com/m2049r/xmrwallet/model/Transfer")));
class_WalletListener = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener")));
class_Ledger = static_cast<jclass>(jenv->NewGlobalRef(
jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger")));
return JNI_VERSION_1_6;
}
#ifdef __cplusplus
Expand Down Expand Up @@ -353,6 +356,39 @@ Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env,
return reinterpret_cast<jlong>(wallet);
}


// virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0;

JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromDeviceJ(JNIEnv *env, jobject instance,
jstring path,
jstring password,
jint networkType,
jstring deviceName,
jlong restoreHeight,
jstring subaddressLookahead) {
const char *_path = env->GetStringUTFChars(path, NULL);
const char *_password = env->GetStringUTFChars(password, NULL);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
const char *_deviceName = env->GetStringUTFChars(deviceName, NULL);
const char *_subaddressLookahead = env->GetStringUTFChars(subaddressLookahead, NULL);

Bitmonero::Wallet *wallet =
Bitmonero::WalletManagerFactory::getWalletManager()->createWalletFromDevice(
std::string(_path),
std::string(_password),
_networkType,
std::string(_deviceName),
(uint64_t) restoreHeight,
std::string(_subaddressLookahead));

env->ReleaseStringUTFChars(path, _path);
env->ReleaseStringUTFChars(password, _password);
env->ReleaseStringUTFChars(deviceName, _deviceName);
env->ReleaseStringUTFChars(subaddressLookahead, _subaddressLookahead);
return reinterpret_cast<jlong>(wallet);
}

JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_walletExists(JNIEnv *env, jobject instance,
jstring path) {
Expand All @@ -378,6 +414,20 @@ Java_com_m2049r_xmrwallet_model_WalletManager_verifyWalletPassword(JNIEnv *env,
return static_cast<jboolean>(passwordOk);
}

//virtual int queryWalletHardware(const std::string &keys_file_name, const std::string &password) const = 0;
JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_queryWalletHardware(JNIEnv *env, jobject instance,
jstring keys_file_name,
jstring password) {
const char *_keys_file_name = env->GetStringUTFChars(keys_file_name, NULL);
const char *_password = env->GetStringUTFChars(password, NULL);
int hardwareId =
Bitmonero::WalletManagerFactory::getWalletManager()->
queryWalletHardware(std::string(_keys_file_name), std::string(_password));
env->ReleaseStringUTFChars(keys_file_name, _keys_file_name);
env->ReleaseStringUTFChars(password, _password);
return static_cast<jint>(hardwareId);
}

JNIEXPORT jobject JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_findWallets(JNIEnv *env, jobject instance,
Expand Down Expand Up @@ -712,6 +762,13 @@ Java_com_m2049r_xmrwallet_model_Wallet_isSynchronized(JNIEnv *env, jobject insta
return static_cast<jboolean>(wallet->synchronized());
}

JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_isKeyOnDevice(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
bool key_on_device = wallet->isKeyOnDevice();
return static_cast<jboolean>(key_on_device);
}

//void cn_slow_hash(const void *data, size_t length, char *hash); // from crypto/hash-ops.h
JNIEXPORT jbyteArray JNICALL
Java_com_m2049r_xmrwallet_util_KeyStoreHelper_slowHash(JNIEnv *env, jobject clazz,
Expand Down Expand Up @@ -1309,6 +1366,96 @@ Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jobject i
Bitmonero::WalletManagerFactory::setLogLevel(level);
}

//
// Ledger Stuff
//

#include "monerujo_ledger.h"

/**
* @brief LedgerExchange - exchange data with Ledger Device
* @param pbSendBuffer - buffer for data to send
* @param cbSendLength - length of send buffer
* @param pbRecvBuffer - buffer for received data
* @param pcbRecvLength - pointer to size of receive buffer
* gets set with length of received data on successful return
* @return SCARD_S_SUCCESS - success
* SCARD_E_NO_READERS_AVAILABLE - no device connected / found
* SCARD_E_INSUFFICIENT_BUFFER - pbRecvBuffer is too small for the response
*/
LONG LedgerExchange(
LPCBYTE pbSendBuffer,
DWORD cbSendLength,
LPBYTE pbRecvBuffer,
LPDWORD pcbRecvLength) {
LOGD("LedgerExchange");
JNIEnv *jenv;
int envStat = attachJVM(&jenv);
if (envStat == JNI_ERR) return -1;

jmethodID exchangeMethod = jenv->GetStaticMethodID(class_Ledger, "Exchange", "([B)[B");

jsize sendLen = static_cast<jsize>(cbSendLength);
jbyteArray dataSend = jenv->NewByteArray(sendLen);
jenv->SetByteArrayRegion(dataSend, 0, sendLen, (jbyte *) pbSendBuffer);
jbyteArray dataRecv = (jbyteArray) jenv->CallStaticObjectMethod(class_Ledger, exchangeMethod,
dataSend);
jenv->DeleteLocalRef(dataSend);
if (dataRecv == nullptr) {
detachJVM(jenv, envStat);
LOGD("LedgerExchange SCARD_E_NO_READERS_AVAILABLE");
return SCARD_E_NO_READERS_AVAILABLE;
}
jsize len = jenv->GetArrayLength(dataRecv);
LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cbSendLength, len);
if (len <= *pcbRecvLength) {
*pcbRecvLength = static_cast<DWORD>(len);
jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) pbRecvBuffer);
jenv->DeleteLocalRef(dataRecv);
detachJVM(jenv, envStat);
return SCARD_S_SUCCESS;
} else {
jenv->DeleteLocalRef(dataRecv);
detachJVM(jenv, envStat);
LOGE("LedgerExchange SCARD_E_INSUFFICIENT_BUFFER");
return SCARD_E_INSUFFICIENT_BUFFER;
}
}

/**
* @brief LedgerFind - find Ledger Device and return it's name
* @param buffer - buffer for name of found device
* @param len - length of buffer
* @return 0 - success
* -1 - no device connected / found
* -2 - JVM not found
*/
int LedgerFind(char *buffer, size_t len) {
LOGD("LedgerName");
JNIEnv *jenv;
int envStat = attachJVM(&jenv);
if (envStat == JNI_ERR) return -2;

jmethodID nameMethod = jenv->GetStaticMethodID(class_Ledger, "Name", "()Ljava/lang/String;");
jstring name = (jstring) jenv->CallStaticObjectMethod(class_Ledger, nameMethod);

int ret;
if (name != nullptr) {
const char *_name = jenv->GetStringUTFChars(name, NULL);
strncpy(buffer, _name, len);
jenv->ReleaseStringUTFChars(name, _name);
buffer[len - 1] = 0; // terminate in case _name is bigger
ret = 0;
LOGD("LedgerName is %s", buffer);
} else {
buffer[0] = 0;
ret = -1;
}

detachJVM(jenv, envStat);
return ret;
}

#ifdef __cplusplus
}
#endif
46 changes: 46 additions & 0 deletions app/src/main/cpp/monerujo_ledger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2018 m2049r
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef XMRWALLET_LEDGER_H
#define XMRWALLET_LEDGER_H

#ifdef __cplusplus
extern "C"
{
#endif

#define SCARD_S_SUCCESS ((LONG)0x00000000) /**< No error was encountered. */
#define SCARD_E_INSUFFICIENT_BUFFER ((LONG)0x80100008) /**< The data buffer to receive returned data is too small for the returned data. */
#define SCARD_E_NO_READERS_AVAILABLE ((LONG)0x8010002E) /**< Cannot find a smart card reader. */

typedef long LONG;
typedef unsigned long DWORD;
typedef DWORD *LPDWORD;
typedef unsigned char BYTE;
typedef BYTE *LPBYTE;
typedef const BYTE *LPCBYTE;

typedef char CHAR;
typedef CHAR *LPSTR;

int LedgerFind(char *buffer, size_t len);
LONG LedgerExchange(LPCBYTE pbSendBuffer, DWORD cbSendLength, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength);

#ifdef __cplusplus
}
#endif

#endif //XMRWALLET_LEDGER_H
53 changes: 53 additions & 0 deletions app/src/main/java/com/btchip/BTChipException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/

package com.btchip;

public class BTChipException extends Exception {

private static final long serialVersionUID = 5512803003827126405L;

public BTChipException(String reason) {
super(reason);
}

public BTChipException(String reason, Throwable cause) {
super(reason, cause);
}

public BTChipException(String reason, int sw) {
super(reason);
this.sw = sw;
}

public int getSW() {
return sw;
}

public String toString() {
if (sw == 0) {
return "BTChip Exception : " + getMessage();
} else {
return "BTChip Exception : " + getMessage() + " " + Integer.toHexString(sw);
}
}

private int sw;

}
31 changes: 31 additions & 0 deletions app/src/main/java/com/btchip/comm/BTChipTransport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
* (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/

package com.btchip.comm;

import com.btchip.BTChipException;

public interface BTChipTransport {
public byte[] exchange(byte[] command);

public void close();

public void setDebug(boolean debugFlag);
}
Loading

0 comments on commit bf5ed79

Please sign in to comment.