1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-14 08:40:49 +02:00

Compare commits

..

7 Commits

Author SHA1 Message Date
m2049r
b1d91e2671 new version 2018-05-27 10:59:10 +02:00
m2049r
271cd2d4a8 deal with all broken variants (#292)
* remove variant code for arm32

* deal with all broken variants
2018-05-25 23:44:37 +02:00
m2049r
22c5a543db upgrade to v0.12.1.0 (#291) 2018-05-25 22:38:05 +02:00
m2049r
cd986860c5 remove variant code for arm32 (#290) 2018-05-25 22:37:50 +02:00
m2049r
0cf5981eae Fixes "Invalid Password" although password correct (#289)
* don't log warning

* fix cn_slow_hash variant&prehash
cn_slow_hash signature was changed in monero-core but the linker didn't
notice - also added code to support wallets created with variant &
prehash enabled
2018-05-25 18:20:48 +02:00
m2049r
e109df34f0 remove if save fails (#281) 2018-05-25 18:20:27 +02:00
m2049r
5a7aa6cc77 testnet => stagenet (#288) 2018-05-25 18:19:29 +02:00
24 changed files with 130 additions and 77 deletions

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 93
versionName "1.5.3 'CrAzY Nacho'"
versionCode 94
versionName "1.5.4 'CrAzY Nacho'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {

View File

@@ -697,16 +697,26 @@ Java_com_m2049r_xmrwallet_model_Wallet_isSynchronized(JNIEnv *env, jobject insta
//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_cnSlowHash(JNIEnv *env, jobject clazz,
jbyteArray data) {
Java_com_m2049r_xmrwallet_util_KeyStoreHelper_slowHash(JNIEnv *env, jobject clazz,
jbyteArray data, jint brokenVariant) {
char hash[HASH_SIZE];
jsize size = env->GetArrayLength(data);
if ((brokenVariant > 0) && (size < 200 /*sizeof(union hash_state)*/)) {
return nullptr;
}
jbyte *buffer = env->GetByteArrayElements(data, NULL);
jsize size = env->GetArrayLength(data);
char hash[HASH_SIZE];
cn_slow_hash(buffer, (size_t) size, hash);
switch (brokenVariant) {
case 1:
slow_hash_broken(buffer, hash, 1);
break;
case 2:
slow_hash_broken(buffer, hash, 0);
break;
default: // not broken
slow_hash(buffer, (size_t) size, hash);
}
env->ReleaseByteArrayElements(data, buffer, JNI_ABORT); // do not update java byte[]
jbyteArray result = env->NewByteArray(HASH_SIZE);
env->SetByteArrayRegion(result, 0, HASH_SIZE, (jbyte *) hash);
return result;
@@ -757,8 +767,6 @@ Java_com_m2049r_xmrwallet_model_Wallet_isAddressValid(JNIEnv *env, jobject clazz
return static_cast<jboolean>(isValid);
}
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);
JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getPaymentIdFromAddress(JNIEnv *env, jobject clazz,
jstring address,

View File

@@ -60,7 +60,15 @@ enum {
HASH_DATA_AREA = 136
};
void cn_slow_hash(const void *data, size_t length, char *hash);
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed);
inline void slow_hash(const void *data, const size_t length, char *hash) {
cn_slow_hash(data, length, hash, 0 /* variant */, 0/*prehashed*/);
}
inline void slow_hash_broken(const void *data, char *hash, int variant) {
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/);
}
#ifdef __cplusplus
}

View File

@@ -102,10 +102,10 @@ public class GenerateReviewFragment extends Fragment {
bAccept = (Button) view.findViewById(R.id.bAccept);
boolean testnet = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
tvWalletMnemonic.setTextIsSelectable(testnet);
tvWalletSpendKey.setTextIsSelectable(testnet);
tvWalletPassword.setTextIsSelectable(testnet);
boolean allowCopy = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
tvWalletMnemonic.setTextIsSelectable(allowCopy);
tvWalletSpendKey.setTextIsSelectable(allowCopy);
tvWalletPassword.setTextIsSelectable(allowCopy);
bAccept.setOnClickListener(new View.OnClickListener() {
@Override

View File

@@ -1007,11 +1007,11 @@ public class LoginActivity extends SecureActivity
case R.id.action_privacy_policy:
PrivacyFragment.display(getSupportFragmentManager());
return true;
case R.id.action_testnet:
case R.id.action_stagenet:
try {
LoginFragment loginFragment = (LoginFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
item.setChecked(loginFragment.onTestnetMenuItem());
item.setChecked(loginFragment.onStagenetMenuItem());
} catch (ClassCastException ex) {
// never mind then
}

View File

@@ -18,15 +18,12 @@ package com.m2049r.xmrwallet;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -41,14 +38,11 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -343,69 +337,65 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.list_menu, menu);
menu.findItem(R.id.action_testnet).setChecked(testnetCheckMenu);
menu.findItem(R.id.action_stagenet).setChecked(stagenetCheckMenu);
super.onCreateOptionsMenu(menu, inflater);
}
private boolean testnetCheckMenu = BuildConfig.DEBUG;
private boolean stagenetCheckMenu = BuildConfig.DEBUG;
//boolean isTestnet() {
// return testnet;
//}
public boolean onTestnetMenuItem() {
boolean lastState = testnetCheckMenu;
public boolean onStagenetMenuItem() {
boolean lastState = stagenetCheckMenu;
setNet(!lastState, true); // set and save
return !lastState;
}
public void setNet(boolean testnetChecked, boolean save) {
this.testnetCheckMenu = testnetChecked;
NetworkType net = testnetChecked ? NetworkType.NetworkType_Testnet : NetworkType.NetworkType_Mainnet;
public void setNet(boolean stagenetChecked, boolean save) {
this.stagenetCheckMenu = stagenetChecked;
NetworkType net = stagenetChecked ? NetworkType.NetworkType_Stagenet : NetworkType.NetworkType_Mainnet;
activityCallback.setNetworkType(net);
activityCallback.showNet();
if (save) {
savePrefs(true); // use previous state as we just clicked it
}
if (testnetChecked) {
setDaemon(daemonTestNet);
if (stagenetChecked) {
setDaemon(daemonStageNet);
} else {
setDaemon(daemonMainNet);
}
loadList();
}
private static final String PREF_DAEMON_TESTNET = "daemon_testnet";
private static final String PREF_DAEMON_STAGENET = "daemon_stagenet";
private static final String PREF_DAEMON_MAINNET = "daemon_mainnet";
private static final String PREF_DAEMONLIST_MAINNET =
"node.moneroworld.com:18089;node.xmrbackb.one;node.xmr.be";
private static final String PREF_DAEMONLIST_TESTNET =
"testnet.xmrchain.net";
private static final String PREF_DAEMONLIST_STAGENET =
"stagenet.xmr-tw.org";
private NodeList daemonTestNet;
private NodeList daemonStageNet;
private NodeList daemonMainNet;
void loadPrefs() {
SharedPreferences sharedPref = activityCallback.getPrefs();
daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET));
daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, PREF_DAEMONLIST_TESTNET));
setNet(testnetCheckMenu, false);
daemonStageNet = new NodeList(sharedPref.getString(PREF_DAEMON_STAGENET, PREF_DAEMONLIST_STAGENET));
setNet(stagenetCheckMenu, false);
}
void savePrefs() {
savePrefs(false);
}
void savePrefs(boolean usePreviousTestnetState) {
Timber.d("SAVE / %s", usePreviousTestnetState);
void savePrefs(boolean usePreviousNetState) {
Timber.d("SAVE / %s", usePreviousNetState);
// save the daemon address for the net
boolean testnet = testnetCheckMenu ^ usePreviousTestnetState;
boolean stagenet = stagenetCheckMenu ^ usePreviousNetState;
String daemon = getDaemon();
if (testnet) {
daemonTestNet.setRecent(daemon);
if (stagenet) {
daemonStageNet.setRecent(daemon);
} else {
daemonMainNet.setRecent(daemon);
}
@@ -413,7 +403,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
SharedPreferences sharedPref = activityCallback.getPrefs();
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(PREF_DAEMON_MAINNET, daemonMainNet.toString());
editor.putString(PREF_DAEMON_TESTNET, daemonTestNet.toString());
editor.putString(PREF_DAEMON_STAGENET, daemonStageNet.toString());
editor.apply();
}

View File

@@ -233,7 +233,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean isBitcoinAddress() {
String address = etAddress.getEditText().getText().toString();
if ((address.length() >= 27) && (address.length() <= 34))
if ((address.length() >= 27) && (address.length() <= 35))
return BitcoinAddressValidator.validate(address);
else
return false;

View File

@@ -247,7 +247,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
private XmrToApi xmrToApi = null;
private final XmrToApi getXmrToApi() {
private XmrToApi getXmrToApi() {
if (xmrToApi == null) {
synchronized (this) {
if (xmrToApi == null) {

View File

@@ -164,8 +164,6 @@ public class Wallet {
public static native boolean isAddressValid(String address, int networkType);
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);
public static native String getPaymentIdFromAddress(String address, int networkType);
public static native long getMaximumAllowedAmount();

View File

@@ -228,7 +228,6 @@ public class WalletManager {
public String getDaemonAddress() {
if (daemonAddress == null) {
// assume testnet not explicitly initialised
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
}
return this.daemonAddress;
@@ -236,13 +235,13 @@ public class WalletManager {
private native void setDaemonAddressJ(String address);
String daemonUsername = "";
private String daemonUsername = "";
public String getDaemonUsername() {
return daemonUsername;
}
String daemonPassword = "";
private String daemonPassword = "";
public String getDaemonPassword() {
return daemonPassword;

View File

@@ -24,7 +24,6 @@ public class FingerprintHelper {
KeyStoreHelper.loadWalletUserPass(context, wallet);
return true;
} catch (KeyStoreHelper.BrokenPasswordStoreException ex) {
Timber.w(ex);
return false;
}
}

View File

@@ -302,6 +302,16 @@ public class Helper {
else return "";
}
public static byte[] hexToBytes(String hex) {
final int len = hex.length();
final byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
static public void setMoneroHome(Context context) {
try {
String home = getStorage(context, HOME_DIR).getAbsolutePath();
@@ -351,6 +361,18 @@ public class Helper {
return crazyPass;
}
// or maybe it is a broken CrAzYpass? (of which we have two variants)
String brokenCrazyPass2 = KeyStoreHelper.getBrokenCrazyPass(context, password, 2);
if ((brokenCrazyPass2 != null)
&& WalletManager.getInstance().verifyWalletPassword(walletPath, brokenCrazyPass2, true)) {
return brokenCrazyPass2;
}
String brokenCrazyPass1 = KeyStoreHelper.getBrokenCrazyPass(context, password, 1);
if ((brokenCrazyPass1 != null)
&& WalletManager.getInstance().verifyWalletPassword(walletPath, brokenCrazyPass1, true)) {
return brokenCrazyPass1;
}
return null;
}

View File

@@ -19,6 +19,7 @@ package com.m2049r.xmrwallet.util;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyGenParameterSpec;
@@ -60,17 +61,21 @@ public class KeyStoreHelper {
System.loadLibrary("monerujo");
}
public static native byte[] cnSlowHash(byte[] data);
public static native byte[] slowHash(byte[] data, int brokenVariant);
static final private String RSA_ALIAS = "MonerujoRSA";
public static String getCrazyPass(Context context, String password) {
private static String getCrazyPass(Context context, String password, int brokenVariant) {
byte[] data = password.getBytes(StandardCharsets.UTF_8);
byte[] sig = null;
try {
KeyStoreHelper.createKeys(context, RSA_ALIAS);
sig = KeyStoreHelper.signData(RSA_ALIAS, data);
return CrazyPassEncoder.encode(cnSlowHash(sig));
byte[] hash = slowHash(sig, brokenVariant);
if (hash == null) {
throw new IllegalStateException("Slow Hash is null!");
}
return CrazyPassEncoder.encode(hash);
} catch (NoSuchProviderException | NoSuchAlgorithmException |
InvalidAlgorithmParameterException | KeyStoreException |
InvalidKeyException | SignatureException ex) {
@@ -78,16 +83,46 @@ public class KeyStoreHelper {
}
}
public static String getCrazyPass(Context context, String password) {
return getCrazyPass(context, password, 0);
}
public static String getBrokenCrazyPass(Context context, String password, int brokenVariant) {
// due to a link bug in the initial implementation, some crazypasses were built with
// prehash & variant == 1
// since there are wallets out there, we need to keep this here
// yes, it's a mess
if (isArm32() && (brokenVariant != 2)) return null;
return getCrazyPass(context, password, brokenVariant);
}
private static Boolean isArm32 = null;
public static boolean isArm32() {
if (isArm32 != null) return isArm32;
synchronized (KeyStoreException.class) {
if (isArm32 != null) return isArm32;
if (Build.SUPPORTED_ABIS[0].equals("armeabi-v7a")) {
isArm32 = true;
} else {
isArm32 = false;
}
return isArm32;
}
}
public static boolean saveWalletUserPass(@NonNull Context context, String wallet, String password) {
String walletKeyAlias = SecurityConstants.WALLET_PASS_KEY_PREFIX + wallet;
byte[] data = password.getBytes(StandardCharsets.UTF_8);
try {
KeyStoreHelper.createKeys(context, walletKeyAlias);
byte[] encrypted = KeyStoreHelper.encrypt(walletKeyAlias, data);
if (encrypted == null) return false;
context.getSharedPreferences(SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE).edit()
.putString(wallet, Base64.encodeToString(encrypted, Base64.DEFAULT))
.apply();
SharedPreferences.Editor e = context.getSharedPreferences(SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE).edit();
if (encrypted == null) {
e.remove(wallet).apply();
return false;
}
e.putString(wallet, Base64.encodeToString(encrypted, Base64.DEFAULT)).apply();
return true;
} catch (NoSuchProviderException | NoSuchAlgorithmException |
InvalidAlgorithmParameterException | KeyStoreException ex) {
@@ -229,8 +264,7 @@ public class KeyStoreHelper {
return (KeyStore.PrivateKeyEntry) entry;
} catch (IOException | NoSuchAlgorithmException | CertificateException
| UnrecoverableEntryException | KeyStoreException ex) {
Timber.e(ex);
return null;
throw new IllegalStateException(ex);
}
}

View File

@@ -3,10 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_testnet"
android:id="@+id/action_stagenet"
android:checkable="true"
android:orderInCategory="100"
android:title="@string/menu_testnet"
android:title="@string/menu_stagenet"
app:showAsAction="never" />
<item

View File

@@ -1,7 +1,6 @@
<resources>
<string name="wallet_activity_name">Wallet</string>
<string name="menu_testnet">Testnet</string>
<string name="menu_about">Über</string>
<string name="menu_privacy">Datenschutzerklärung</string>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="wallet_activity_name">Monedero</string>
<string name="menu_testnet">Testnet</string>
<string name="menu_about">Acerca De</string>
<string name="menu_privacy">Política de Privacidad</string>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="wallet_activity_name">Portefeuille</string>
<string name="menu_testnet">Testnet</string>
<string name="menu_about">À Propos</string>
<string name="menu_privacy">Politique de Confidentialité</string>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="wallet_activity_name">Portafoglio</string>
<string name="menu_testnet">Testnet</string>
<string name="menu_about">Informazioni</string>
<string name="menu_privacy">Politiche Privacy</string>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="wallet_activity_name">Lommebok</string>
<string name="menu_testnet">Testnett</string>
<string name="menu_about">Om</string>
<string name="menu_privacy">Personvernserklæring</string>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="wallet_activity_name">钱包</string>
<string name="menu_testnet">Testnet</string>
<string name="menu_about">关于</string>
<string name="menu_privacy">隐私权政策</string>

View File

@@ -1,7 +1,6 @@
<resources>
<string name="wallet_activity_name">錢包</string>
<string name="menu_testnet">Testnet</string>
<string name="menu_about">關於</string>
<string name="menu_privacy">隱私權政策</string>

View File

@@ -2,7 +2,7 @@
<string name="app_name" translatable="false">monerujo</string>
<string name="wallet_activity_name">Wallet</string>
<string name="menu_testnet">Testnet</string>
<string name="menu_stagenet" translatable="false">Stagenet</string>
<string name="menu_about">About</string>
<string name="menu_privacy">Privacy Policy</string>

View File

@@ -25,6 +25,7 @@ public class BitcoinAddressValidatorTest {
@Test
public void validateBTC_shouldValidate() {
assertTrue(BitcoinAddressValidator.validate("2NBMEXXS4v8ubajzfQUjYvh2ptLkxzH8uTC", true));
assertTrue(BitcoinAddressValidator.validate("2N9fzq66uZYQXp7uqrPBH6jKBhjrgTzpGCy", true));
assertTrue(BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false));
assertTrue(BitcoinAddressValidator.validate("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", false));

View File

@@ -556,7 +556,8 @@ struct Wallet
}
static uint64_t maximumAllowedAmount();
// Easylogger wrapper
static void init(const char *argv0, const char *default_log_base_name);
static void init(const char *argv0, const char *default_log_base_name) { init(argv0, default_log_base_name, "", true); }
static void init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console);
static void debug(const std::string &category, const std::string &str);
static void info(const std::string &category, const std::string &str);
static void warning(const std::string &category, const std::string &str);