Skip to content

Commit

Permalink
# This is a combination of 24 commits.
Browse files Browse the repository at this point in the history
# This is the 1st commit message:

added new strings for upcoming keystore support

# This is the commit message #2:

moved from spending pin menu to wallet protection menu

# This is the commit message #3:

added prototype for supporting keystore and spending pin in a dialog fragment

# This is the commit message #4:

KeyStoreKeyCrypter with biometric authentication (#9)

* implemented new KeyStoreKeyCrypter and biometric authentication, untested

* added KeyStore features

# This is the commit message #5:

added a working version that supports both keycrypters without user biometric authentication

# This is the commit message #6:

small change*

# This is the commit message #7:

wip

# This is the commit message #8:

working version for encrypt but decrypt only works if its in the timout period. if decrypt needs to authenticate the user the app freezes i.e. the UI thread doesn't responde

# This is the commit message #9:

wip

# This is the commit message #10:

rebase2

# This is the commit message #11:

upgrade build successful, not tested

# This is the commit message #12:

rebase

# This is the commit message #13:

fixed tx declaration

# This is the commit message #14:

rebase

# This is the commit message #15:

rebase

# This is the commit message #16:

wip, implemented KeyCrypterFactory

# This is the commit message #17:

backup with keystorekeycrypter


# This is the commit message #18:

small changes for 0.17 upgrade + context propagation in cryptactivity

# This is the commit message #19:

sending funds when keystore encrypted fix

# This is the commit message #20:

removed button from CryptActivity, fixed backup export and import

# This is the commit message bitcoin-wallet#21:

added strings

# This is the commit message bitcoin-wallet#22:

fixed backup with keystore

# This is the commit message bitcoin-wallet#23:

added string resources and new EncryptionType

# This is the commit message bitcoin-wallet#24:

removed scrypt params from KeyStoreKeyCrypter
  • Loading branch information
Sebastian Nicol authored and oliveraemmer committed Apr 9, 2024
1 parent 0661bcc commit 92ae3d2
Show file tree
Hide file tree
Showing 29 changed files with 881 additions and 110 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ local.properties
*.iml
bin/
gen/
.DS_Store
wallet/.DS_Store
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ buildscript {
classpath('fr.avianey.androidsvgdrawable:gradle-plugin:3.0.2') {
exclude group: 'xerces'
}
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.10"
}
}

Expand Down
7 changes: 7 additions & 0 deletions wallet/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
Expand Down Expand Up @@ -211,6 +212,12 @@
android:theme="@style/My.Theme.ChildActivity"
android:windowSoftInputMode="adjustResize" />

<activity
android:name="de.schildbach.wallet.ui.CryptActivity"
android:configChanges="keyboard|keyboardHidden"
android:label="crypt_activity"
android:theme="@style/My.Theme.ChildActivity" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.file_attachment"
Expand Down
9 changes: 5 additions & 4 deletions wallet/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ dependencies {
implementation 'androidx.sqlite:sqlite:2.3.0'
implementation "androidx.room:room-runtime:2.5.0"
annotationProcessor "androidx.room:room-compiler:2.5.0"
implementation files('bitcoinj-core-0.17-SNAPSHOT.jar')
implementation 'org.bouncycastle:bcprov-jdk15to18:1.72'
implementation 'com.google.protobuf:protobuf-javalite:3.21.12'
implementation 'com.google.guava:guava:31.1-android'
Expand All @@ -40,6 +39,7 @@ dependencies {
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'com.github.tony19:logback-android:2.0.1'
testImplementation 'junit:junit:4.13.2'
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
}

ext {
Expand All @@ -52,13 +52,14 @@ android {

defaultConfig {
applicationId 'de.schildbach'
minSdkVersion 26
minSdkVersion 28
}

buildTypes {
all {
minifyEnabled true
minifyEnabled false
shrinkResources false
crunchPngs false
proguardFile 'proguard.cfg'
}
debug {
Expand Down Expand Up @@ -128,4 +129,4 @@ task svgToPngMipmap(type: fr.avianey.androidsvgdrawable.gradle.SvgDrawableTask)
targetedDensities = ['xxhdpi', 'xxxhdpi']
outputFormat = 'PNG'
outputType = 'mipmap'
}
}
Binary file added wallet/libs/bcprov-jdk15to18-1.77.jar
Binary file not shown.
Binary file added wallet/libs/bitcoinj-core-0.17-SNAPSHOT.jar
Binary file not shown.
Binary file added wallet/libs/bitcoinj-wallettool.jar
Binary file not shown.
Binary file added wallet/libs/jcip-annotations-1.0.jar
Binary file not shown.
Binary file not shown.
Binary file added wallet/libs/picocli-4.7.5.jar
Binary file not shown.
Binary file added wallet/libs/protobuf-javalite-3.22.5.jar
Binary file not shown.
15 changes: 15 additions & 0 deletions wallet/res/layout/crypt_content.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">

<TextView
android:id="@+id/crypt_instruction_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fingerprint scanner"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp"/>

</RelativeLayout>
23 changes: 21 additions & 2 deletions wallet/res/layout/encrypt_keys_dialog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,28 @@
android:layout_marginRight="@dimen/list_entry_padding_horizontal_lax"
android:orientation="vertical">

<RadioGroup
android:id="@+id/encrypt_keys_dialog_radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RadioButton android:id="@+id/encrypt_keys_dialog_radio_spending_pin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/encrypt_keys_dialog_radio_spending_pin"/>
<RadioButton android:id="@+id/encrypt_keys_dialog_radio_keystore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/encrypt_keys_dialog_radio_keystore"/>
</RadioGroup>

<TextView
android:id="@+id/encrypt_keys_dialog_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_entry_padding_vertical"
android:text="@string/encrypt_keys_dialog_message" />
android:text="@string/encrypt_keys_dialog_message_spending_pin" />

<LinearLayout
android:id="@+id/encrypt_keys_dialog_password_old_group"
Expand Down Expand Up @@ -50,6 +67,7 @@
</LinearLayout>

<LinearLayout
android:id="@+id/encrypt_keys_dialog_password_new_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_entry_padding_vertical"
Expand Down Expand Up @@ -84,11 +102,12 @@
android:text="@string/encrypt_keys_dialog_show" />

<TextView
android:id="@+id/encrypt_keys_dialog_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_entry_padding_vertical_lax"
android:layout_marginTop="@dimen/list_entry_padding_vertical_lax"
android:text="@string/encrypt_keys_dialog_warning"
android:text="@string/encrypt_keys_dialog_warning_spending_pin"
android:textColor="@color/fg_less_significant"
android:textSize="@dimen/font_size_small" />
</LinearLayout>
Expand Down
5 changes: 2 additions & 3 deletions wallet/res/menu/wallet_options.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@
android:showAsAction="never"
android:title="@string/import_keys_dialog_title" />
<item
android:id="@+id/wallet_options_encrypt_keys"
android:id="@+id/wallet_options_protection"
android:showAsAction="never"
android:title="@string/encrypt_keys_dialog_title"
android:visible="false" />
android:title="@string/encrypt_keys_dialog_title"/>
</menu>
</item>
<item
Expand Down
26 changes: 23 additions & 3 deletions wallet/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,13 @@
<string name="import_export_keys_dialog_show">Show password</string>
<string name="import_export_keys_dialog_failure_title">Error</string>
<string name="import_keys_intent_filter_action">Restore wallet</string>
<string name="encrypt_keys_dialog_title">Spending PIN</string>
<string name="encrypt_keys_dialog_message">Spending from your wallet will be protected with the chosen PIN.</string>
<string name="encrypt_keys_dialog_warning">Important: You need to remember your PIN! Do not use common numbers (like birthdates).</string>
<string name="encrypt_keys_dialog_title">Wallet protection</string>
<string name="encrypt_keys_dialog_radio_spending_pin">Spending PIN</string>
<string name="encrypt_keys_dialog_radio_keystore">KeyStore</string>
<string name="encrypt_keys_dialog_message_keystore">Spending from your wallet will require biometric authentication (fingerprint, face scan).</string>
<string name="encrypt_keys_dialog_message_spending_pin">Spending from your wallet will be protected with the chosen PIN.</string>
<string name="encrypt_keys_dialog_warning_spending_pin">Important: You need to remember your PIN! Do not use common numbers (like birthdates).</string>
<string name="encrypt_keys_dialog_warning_keystore">Important: Your encryption key will be bound to your device. Be sure to make a back up to avoid losing access to your funds if the device is lost.</string>
<string name="encrypt_keys_dialog_password_old">Old PIN</string>
<string name="encrypt_keys_dialog_password_strength_weak">weak</string>
<string name="encrypt_keys_dialog_password_strength_fair">fair</string>
Expand Down Expand Up @@ -339,6 +343,22 @@
<string name="notification_channel_ongoing_name">Background activity</string>
<string name="notification_channel_important_name">Important alerts</string>
<string name="appwidget_wallet_balance_title">Bitcoin balance</string>
<string name="biometric_hardward_not_available">No biometric hardware available</string>
<string name="biometric_hardward_not_available_info">This device does not support biometric authentication. Please use Spending PIN instead of KeyStore encryption</string>
<string name="biometric_hardward_currently_not_available">Biometric hardware is currently unavailable</string>
<string name="biometric_auth_not_enrolled">Biometric authentication not enrolled</string>
<string name="biometric_auth_not_enrolled_info">Please enroll biometric authentication to use KeyStore encryption</string>
<string name="biometric_update_required">Security update required</string>
<string name="biometric_update_required_info">A security update is required to use KeyStore encryption with biometric authentication</string>
<string name="biometric_unsupported_version">Unsupported android version</string>
<string name="biometric_unsupported_version_info">This android version does not support the required strong biometric authentication (fingerprint, iris, or face)</string>
<string name="biometric_unknown_error">Unknown error</string>
<string name="biometric_unknown_error_info">An unknown error occurred. Please make sure this device supports strong biometric authentication (fingerprint, iris, or face)</string>
<string name="biometric_auth">Biometric Authentication</string>
<string name="biometric_auth_required_info">Authentication is required to continue</string>
<string name="biometric_auth_utilised_info">This app uses biometric authentication to protect your data.</string>
<string name="biometric_auth_cancelled">Authentication cancelled</string>


<!-- generic buttons -->
<string name="button_ok">OK</string>
Expand Down
11 changes: 8 additions & 3 deletions wallet/src/de/schildbach/wallet/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.bitcoinj.base.Coin;
import org.bitcoinj.base.ScriptType;
import org.bitcoinj.core.Context;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.BitcoinNetworkParams;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.TestNet3Params;
Expand All @@ -44,7 +45,7 @@
public final class Constants {

/** Network this wallet is on (e.g. testnet or mainnet). */
public static final BitcoinNetworkParams NETWORK_PARAMETERS =
public static final NetworkParameters NETWORK_PARAMETERS =
!BuildConfig.FLAVOR.equals("prod") ? TestNet3Params.get() : MainNetParams.get();

/** Bitcoinj global context. */
Expand Down Expand Up @@ -131,8 +132,6 @@ public final static class Files {
public static final String DEFAULT_EXCHANGE_CURRENCY = "USD";

/** Donation address for tip/donate action. */
//public static final String DONATION_ADDRESS = NETWORK_PARAMETERS.getId().equals(NetworkParameters.ID_MAINNET)
//? "bc1qdde452znpy4lpksqt766hskkmpxcw6cp0s4jvk" : null;
public static final String DONATION_ADDRESS = NETWORK_PARAMETERS.getId().equals(BitcoinNetwork.ID_MAINNET)
? "bc1q6swnv7p95wvxwdpld8f6a8zrzxwmaumh2qg2n4" : null;

Expand Down Expand Up @@ -210,6 +209,12 @@ public final static class Files {
public static final int SCRYPT_ITERATIONS_TARGET = 65536;
public static final int SCRYPT_ITERATIONS_TARGET_LOWRAM = 32768;

/** KeyStore Key Reference */
public static final String KEY_STORE_KEY_REF = "BitcoinWalletKey";
/** KeyStore Provider */
public static final String KEY_STORE_PROVIDER = "AndroidKeyStore";
public static final String KEY_STORE_TRANSFORMATION = "AES/GCM/NoPadding";

/** Default ports for Electrum servers */
public static final int ELECTRUM_SERVER_DEFAULT_PORT_TCP = NETWORK_PARAMETERS.getId()
.equals(BitcoinNetwork.ID_MAINNET) ? 50001 : 51001;
Expand Down
36 changes: 23 additions & 13 deletions wallet/src/de/schildbach/wallet/WalletApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.SettableFuture;

import de.schildbach.wallet.crypto.KeyStoreKeyCrypterFactory;
import de.schildbach.wallet.service.BlockchainService;
import de.schildbach.wallet.service.BlockchainState;
import de.schildbach.wallet.ui.Event;
Expand All @@ -50,6 +52,7 @@
import de.schildbach.wallet.util.Toast;
import de.schildbach.wallet.util.WalletUtils;
import org.bitcoinj.core.VersionMessage;
import org.bitcoinj.crypto.KeyCrypterFactory;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.utils.ContextPropagatingThreadFactory;
import org.bitcoinj.utils.Threading;
Expand All @@ -63,6 +66,7 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -162,22 +166,30 @@ public void getWalletAsync(final OnWalletLoadedListener listener) {
public void run() {
synchronized (getWalletLock) {
initMnemonicCode();
if (walletFiles == null)
loadWalletFromProtobuf();
if (walletFiles == null) {
try {
loadWalletFromProtobuf();
} catch (UnreadableWalletException e) {
throw new RuntimeException(e);
}
}
}
listener.onWalletLoaded(walletFiles.getWallet());
}

@WorkerThread
private void loadWalletFromProtobuf() {
private void loadWalletFromProtobuf() throws UnreadableWalletException {
Wallet wallet;
if (walletFile.exists()) {
try (final FileInputStream walletStream = new FileInputStream(walletFile)) {
final Stopwatch watch = Stopwatch.createStarted();
wallet = new WalletProtobufSerializer().readWallet(walletStream);
KeyCrypterFactory keyCrypterFactory = new KeyStoreKeyCrypterFactory(WalletApplication.this);
WalletProtobufSerializer serializer = new WalletProtobufSerializer();
serializer.setKeyCrypterFactory(keyCrypterFactory);
wallet = serializer.readWallet(walletStream);
watch.stop();

if (!wallet.getParams().equals(Constants.NETWORK_PARAMETERS))
if (!wallet.network().equals(Constants.NETWORK_PARAMETERS.network()))
throw new UnreadableWalletException(
"bad wallet network parameters: " + wallet.getParams().getId());

Expand All @@ -195,23 +207,21 @@ private void loadWalletFromProtobuf() {
new Toast(WalletApplication.this).postLongToast(R.string.toast_wallet_reset);
}

if (!wallet.getParams().equals(Constants.NETWORK_PARAMETERS))
if (!wallet.network().equals(Constants.NETWORK_PARAMETERS.network()))
throw new Error("bad wallet network parameters: " + wallet.getParams().getId());

wallet.cleanup();
walletFiles = wallet.autosaveToFile(walletFile, Constants.Files.WALLET_AUTOSAVE_DELAY_MS,
TimeUnit.MILLISECONDS, null);
final Duration duration = Duration.ofMillis(Constants.Files.WALLET_AUTOSAVE_DELAY_MS);
walletFiles = wallet.autosaveToFile(walletFile, duration, null);
} else {
final Stopwatch watch = Stopwatch.createStarted();
wallet = Wallet.createDeterministic(Constants.NETWORK_PARAMETERS,
final Stopwatch watch = Stopwatch.createStarted();wallet = Wallet.createDeterministic(Constants.NETWORK_PARAMETERS.network(),
Constants.DEFAULT_OUTPUT_SCRIPT_TYPE);
walletFiles = wallet.autosaveToFile(walletFile, Constants.Files.WALLET_AUTOSAVE_DELAY_MS,
TimeUnit.MILLISECONDS, null);
final Duration duration = Duration.ofMillis(Constants.Files.WALLET_AUTOSAVE_DELAY_MS);
walletFiles = wallet.autosaveToFile(walletFile, duration, null);
autosaveWalletNow(); // persist...
WalletUtils.autoBackupWallet(WalletApplication.this, wallet); // ...and backup asap
watch.stop();
log.info("fresh {} wallet created, took {}", Constants.DEFAULT_OUTPUT_SCRIPT_TYPE, watch);

config.armBackupReminder();
}
}
Expand Down

0 comments on commit 92ae3d2

Please sign in to comment.