From 968c5d1f8e6995659e79fd31bd70ae5489b18b0a Mon Sep 17 00:00:00 2001 From: akinwale Date: Wed, 20 Dec 2017 17:10:30 +0100 Subject: [PATCH] Wallet encryption (#14) * enabled wallet encryption with custom keyring backend * updated with lbry and lbryum master refs for wallet encryption --- buildozer.spec.sample | 10 ++--- buildozer.spec.travis | 2 +- recipes/pycrypto/__init__.py | 7 +-- .../pycrypto/fix-fastmath-include-dirs.patch | 11 +++++ src/main/java/io/lbry/lbrynet/Utils.java | 45 ++++++++++++++++++- src/main/python/lbrynetservice.py | 28 ++++++++++-- 6 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 recipes/pycrypto/fix-fastmath-include-dirs.patch diff --git a/buildozer.spec.sample b/buildozer.spec.sample index 9101dad0..a0f7a48c 100644 --- a/buildozer.spec.sample +++ b/buildozer.spec.sample @@ -36,7 +36,7 @@ version = 0.1 # (list) Application requirements # comma seperated e.g. requirements = sqlite3,kivy -requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git@v0.0.12rc1#egg=lbryschema, git+https://github.com/lbryio/lbryum.git@v3.1.9rc2#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.0.3, funcsigs, mock, pbr, unqlite +requirements = openssl, sqlite3, hostpython2, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15rc2#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.0.3, funcsigs, mock, pbr, unqlite # (str) Custom source folders for requirements # Sets custom source for any requirements with recipes @@ -104,10 +104,10 @@ android.ndk = 13b #android.private_storage = True # (str) Android NDK directory (if empty, it will be automatically downloaded.) -android.ndk_path = ~/Dev/SDKs/android-ndk-r13b +#android.ndk_path = ~/Dev/SDKs/android-ndk-r13b # (str) Android SDK directory (if empty, it will be automatically downloaded.) -android.sdk_path = ~/Dev/SDKs/android +#android.sdk_path = ~/Dev/SDKs/android # (str) ANT directory (if empty, it will be automatically downloaded.) #android.ant_path = @@ -191,10 +191,10 @@ android.arch = armeabi-v7a # # (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) -p4a.source_dir = ~/Dev/Python/lbry-android/p4a +p4a.source_dir = ./p4a # (str) The directory in which python-for-android should look for your own build recipes (if any) -p4a.local_recipes = ~/Dev/Python/lbry-android/recipes +p4a.local_recipes = ./recipes # (str) Filename to the hook for p4a #p4a.hook = diff --git a/buildozer.spec.travis b/buildozer.spec.travis index 722821ed..a0f7a48c 100644 --- a/buildozer.spec.travis +++ b/buildozer.spec.travis @@ -36,7 +36,7 @@ version = 0.1 # (list) Application requirements # comma seperated e.g. requirements = sqlite3,kivy -requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git@v0.0.12rc1#egg=lbryschema, git+https://github.com/lbryio/lbryum.git@v3.1.9rc2#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.0.3, funcsigs, mock, pbr, unqlite +requirements = openssl, sqlite3, hostpython2, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15rc2#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.0.3, funcsigs, mock, pbr, unqlite # (str) Custom source folders for requirements # Sets custom source for any requirements with recipes diff --git a/recipes/pycrypto/__init__.py b/recipes/pycrypto/__init__.py index 1c6aaa1d..d3dfd991 100644 --- a/recipes/pycrypto/__init__.py +++ b/recipes/pycrypto/__init__.py @@ -17,7 +17,7 @@ class PyCryptoRecipe(CompiledComponentsPythonRecipe): site_packages_name = 'Crypto' call_hostpython_via_targetpython = False - patches = ['add_length.patch'] + patches = ['add_length.patch', 'fix-fastmath-include-dirs.patch'] def get_recipe_env(self, arch=None): env = super(PyCryptoRecipe, self).get_recipe_env(arch) @@ -40,8 +40,9 @@ class PyCryptoRecipe(CompiledComponentsPythonRecipe): self.ctx.get_libs_dir(arch.arch) + '-L{}'.format(self.ctx.libs_dir)) + ' -L{}'.format( openssl_build_dir) - #env['EXTRA_CFLAGS'] = '--host linux-armv' + env['EXTRA_CFLAGS'] = '--host linux-armv' env['ac_cv_func_malloc_0_nonnull'] = 'yes' + return env def build_compiled_components(self, arch): @@ -50,7 +51,7 @@ class PyCryptoRecipe(CompiledComponentsPythonRecipe): env = self.get_recipe_env(arch) with current_directory(self.get_build_dir(arch.arch)): configure = sh.Command('./configure') - shprint(configure, '--host=arm-linux', + shprint(configure, '--host=arm-eabi', '--prefix={}'.format(self.ctx.get_python_install_dir()), '--enable-shared', _env=env) super(PyCryptoRecipe, self).build_compiled_components(arch) diff --git a/recipes/pycrypto/fix-fastmath-include-dirs.patch b/recipes/pycrypto/fix-fastmath-include-dirs.patch new file mode 100644 index 00000000..743b8869 --- /dev/null +++ b/recipes/pycrypto/fix-fastmath-include-dirs.patch @@ -0,0 +1,11 @@ +--- a/setup.py 2013-10-14 22:38:10.000000000 +0100 ++++ b/setup.py 2017-12-20 16:05:16.726389781 +0100 +@@ -370,7 +370,7 @@ + 'ext_modules': plat_ext + [ + # _fastmath (uses GNU mp library) + Extension("Crypto.PublicKey._fastmath", +- include_dirs=['src/','/usr/include/'], ++ include_dirs=['src/'], + libraries=['gmp'], + sources=["src/_fastmath.c"]), + diff --git a/src/main/java/io/lbry/lbrynet/Utils.java b/src/main/java/io/lbry/lbrynet/Utils.java index 5201da35..88e3f704 100644 --- a/src/main/java/io/lbry/lbrynet/Utils.java +++ b/src/main/java/io/lbry/lbrynet/Utils.java @@ -121,7 +121,50 @@ public final class Utils { return null; } - + + public static void setPassword(String serviceName, String username, String password, Context context, KeyStore keyStore) { + try { + String encryptedUsername = String.format("u_%s_%s", serviceName, encrypt(username.getBytes(), context, keyStore)); + String encryptedPassword = encrypt(password.getBytes(), context, keyStore); + SharedPreferences pref = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = pref.edit(); + editor.putString(encryptedUsername, encryptedPassword); + editor.commit(); + } catch (Exception ex) { + Log.e(TAG, "lbrynetservice - Could not set a password", ex); + } + } + + public static String getPassword(String serviceName, String username, Context context, KeyStore keyStore) { + try { + String encryptedUsername = String.format("u_%s_%s", serviceName, encrypt(username.getBytes(), context, keyStore)); + SharedPreferences pref = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + String encryptedPassword = pref.getString(encryptedUsername, null); + if (encryptedPassword == null || encryptedPassword.trim().length() == 0) { + return null; + } + + byte[] decoded = Base64.decode(encryptedPassword, Base64.DEFAULT); + return new String(decrypt(decoded, context, keyStore), Charset.forName("UTF8")); + } catch (Exception ex) { + Log.e(TAG, "lbrynetservice - could not decrypt password for user", ex); + } + + return null; + } + + public static void deletePassword(String serviceName, String username, Context context, KeyStore keyStore) { + try { + String encryptedUsername = String.format("u_%s_%s", serviceName, encrypt(username.getBytes(), context, keyStore)); + SharedPreferences pref = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = pref.edit(); + editor.remove(encryptedUsername); + editor.commit(); + } catch (Exception ex) { + Log.e(TAG, "lbrynetservice - Could not delete a password", ex); + } + } + public static String encrypt(byte[] input, Context context, KeyStore keyStore) throws Exception { Cipher c = Cipher.getInstance(AES_MODE, "BC"); c.init(Cipher.ENCRYPT_MODE, getSecretKey(context, keyStore)); diff --git a/src/main/python/lbrynetservice.py b/src/main/python/lbrynetservice.py index 9065876a..1aa71f8f 100644 --- a/src/main/python/lbrynetservice.py +++ b/src/main/python/lbrynetservice.py @@ -1,3 +1,4 @@ +import keyring.backend import platform import ssl @@ -35,12 +36,12 @@ def save_api_keys(keys, path): if key_name in keys: secret = keys[key_name].secret # TODO: For testing. Normally, this should not be displayed. - log.info('Saving API Secret: %s', secret); - context = service.getApplicationContext(); + log.info('Saving API Secret: %s', secret) + context = service.getApplicationContext() lbrynet_utils.saveApiSecret(secret, context, ks) def initialize_api_key_file(key_path): - context = service.getApplicationContext(); + context = service.getApplicationContext() secret = lbrynet_utils.loadApiSecret(context, ks) if secret is None: keys = {} @@ -53,6 +54,25 @@ lbrynet.daemon.auth.util.load_api_keys = load_api_keys lbrynet.daemon.auth.util.save_api_keys = save_api_keys lbrynet.daemon.auth.util.initialize_api_key_file = initialize_api_key_file +# Keyring backend +class LbryAndroidKeyring(keyring.backend.KeyringBackend): + priority = 1 + + def set_password(self, servicename, username, password): + context = service.getApplicationContext() + lbrynet_utils.setPassword(servicename, username, password, context, ks) + + def get_password(self, servicename, username): + context = service.getApplicationContext() + return lbrynet_utils.getPassword(servicename, username, context, ks) + + def delete_password(self, servicename, username): + context = service.getApplicationContext() + lbrynet_utils.deletePassword(servicename, username, context, ks) + +# set the keyring backend +keyring.set_keyring(LbryAndroidKeyring()) + import logging.handlers from lbrynet.core import log_support from twisted.internet import defer, reactor @@ -109,7 +129,7 @@ def start(): if test_internet_connection(): analytics_manager = analytics.Manager.new_instance() - start_server_and_listen(True, analytics_manager) + start_server_and_listen(False, analytics_manager) reactor.run() else: log.info("Not connected to internet, unable to start")