Added module for securely storing seeds and keys.
The underlying library is not perfect but it will do for now. Closes #11. Closes #12.
This commit is contained in:
parent
688f401d25
commit
52a2dcb5c2
|
@ -75,6 +75,8 @@ dependencies {
|
|||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':qrecycler')
|
||||
implementation project(':feedback')
|
||||
implementation project(':mnemonic')
|
||||
implementation project(':lockbox')
|
||||
|
||||
// Kotlin
|
||||
implementation Deps.Kotlin.STDLIB
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -0,0 +1,48 @@
|
|||
import cash.z.ecc.android.Deps
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion Deps.compileSdkVersion
|
||||
buildToolsVersion Deps.buildToolsVersion
|
||||
|
||||
useLibrary 'android.test.runner'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion Deps.minSdkVersion
|
||||
targetSdkVersion Deps.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
|
||||
consumerProguardFiles 'consumer-rules.pro'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation Deps.Kotlin.STDLIB
|
||||
implementation Deps.AndroidX.APPCOMPAT
|
||||
implementation Deps.AndroidX.CORE_KTX
|
||||
|
||||
implementation "de.adorsys.android:securestoragelibrary:1.2.2"
|
||||
|
||||
androidTestImplementation Deps.Test.Android.JUNIT
|
||||
androidTestImplementation Deps.Test.Android.ESPRESSO
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,52 @@
|
|||
package cash.z.ecc.android.lockbox
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LockBoxText {
|
||||
|
||||
private lateinit var appContext: Context
|
||||
private lateinit var lockBox: LockBoxProvider
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
lockBox = LockBox(appContext)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSeed_store() {
|
||||
val testMessage = "Some Bytes To Test"
|
||||
val testBytes = testMessage.toByteArray()
|
||||
lockBox.setBytes("seed", testBytes)
|
||||
assertEquals(testMessage, String(lockBox.getBytes("seed")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSeed_storeNegatives() {
|
||||
val testBytes = byteArrayOf(0x00, 0x00, -0x0F, -0x0B)
|
||||
lockBox.setBytes("seed", testBytes)
|
||||
assertTrue(testBytes.contentEquals(lockBox.getBytes("seed")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSeed_storeLeadingZeros() {
|
||||
val testBytes = byteArrayOf(0x00, 0x00, 0x0F, 0x0B)
|
||||
lockBox.setBytes("seed", testBytes)
|
||||
assertTrue(testBytes.contentEquals(lockBox.getBytes("seed")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPrivateKey_retrieve() {
|
||||
val testMessage = "Some Bytes To Test"
|
||||
lockBox.setCharsUtf8("spendingKey", testMessage.toCharArray())
|
||||
assertEquals(testMessage, String(lockBox.getCharsUtf8("spendingKey")))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cash.z.ecc.android.lockbox" />
|
|
@ -0,0 +1,64 @@
|
|||
package cash.z.ecc.android.lockbox
|
||||
|
||||
import android.content.Context
|
||||
import de.adorsys.android.securestoragelibrary.SecurePreferences
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.CharBuffer
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.*
|
||||
|
||||
|
||||
class LockBox(private val appContext: Context) : LockBoxProvider {
|
||||
|
||||
override fun setBytes(key: String, value: ByteArray) {
|
||||
SecurePreferences.setValue(appContext, key, value.toHex())
|
||||
}
|
||||
|
||||
override fun getBytes(key: String): ByteArray {
|
||||
return SecurePreferences.getStringValue(appContext, key, null)!!.fromHex()
|
||||
}
|
||||
|
||||
override fun setCharsUtf8(key: String, value: CharArray) {
|
||||
setBytes(key, value.toBytes())
|
||||
}
|
||||
|
||||
override fun getCharsUtf8(key: String): CharArray {
|
||||
return getBytes(key).fromBytes()
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Extensions (TODO: find library that works better with arrays of bytes and chars)
|
||||
//
|
||||
|
||||
private fun ByteArray.toHex(): String {
|
||||
val sb = StringBuilder(size * 2)
|
||||
for (b in this)
|
||||
sb.append(String.format("%02x", b))
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun String.fromHex(): ByteArray {
|
||||
val len = length
|
||||
val data = ByteArray(len / 2)
|
||||
var i = 0
|
||||
while (i < len) {
|
||||
data[i / 2] =
|
||||
((Character.digit(this[i], 16) shl 4) + Character.digit(this[i + 1], 16)).toByte()
|
||||
i += 2
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
private fun CharArray.toBytes(): ByteArray {
|
||||
val byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(this))
|
||||
return Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());
|
||||
}
|
||||
|
||||
private fun ByteArray.fromBytes(): CharArray {
|
||||
val charBuffer = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(this))
|
||||
return Arrays.copyOf(charBuffer.array(), charBuffer.limit())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package cash.z.ecc.android.lockbox
|
||||
|
||||
/**
|
||||
* Generic interface to separate the underlying implementation used by this module and the code that
|
||||
* interacts with it.
|
||||
*/
|
||||
interface LockBoxProvider {
|
||||
fun setBytes(key: String, value: ByteArray)
|
||||
fun getBytes(key: String): ByteArray
|
||||
|
||||
fun setCharsUtf8(key: String, value: CharArray)
|
||||
fun getCharsUtf8(key: String): CharArray
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package cash.z.ecc.android.lockbox
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
|
@ -1,2 +1,2 @@
|
|||
rootProject.name='Zcash Wallet'
|
||||
include ':app', ':qrecycler', ':feedback', ':mnemonic'
|
||||
include ':app', ':qrecycler', ':feedback', ':mnemonic', ':lockbox'
|
||||
|
|
Loading…
Reference in New Issue