Skip to content

Commit 8926cef

Browse files
authored
Merge pull request #4 from tailoredmedia/bugfix/3_crypto_maybe_empty
Fix onSuccess with cryptoObject not emitting cryptoObject
2 parents 094535d + d585819 commit 8926cef

File tree

7 files changed

+186
-17
lines changed

7 files changed

+186
-17
lines changed

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ dependencies {
2626

2727
implementation project(':biometricauth')
2828

29-
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
30-
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
29+
implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
30+
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
3131
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
3232
}

app/src/main/java/com/tailoredapps/biometricsample/MainActivity.kt

Lines changed: 141 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package com.tailoredapps.biometricsample
22

3+
import android.annotation.TargetApi
4+
import android.os.Build
35
import android.os.Bundle
6+
import android.security.keystore.KeyGenParameterSpec
7+
import android.security.keystore.KeyProperties
48
import android.support.v7.app.AppCompatActivity
59
import android.util.Log
610
import android.widget.Button
@@ -9,26 +13,50 @@ import com.tailoredapps.biometricauth.BiometricAuth
913
import com.tailoredapps.biometricauth.BiometricAuthenticationCancelledException
1014
import com.tailoredapps.biometricauth.BiometricAuthenticationException
1115
import io.reactivex.android.schedulers.AndroidSchedulers
16+
import java.io.IOException
17+
import java.security.*
18+
import java.security.cert.CertificateException
19+
import javax.crypto.Cipher
20+
import javax.crypto.KeyGenerator
21+
import javax.crypto.NoSuchPaddingException
22+
import javax.crypto.SecretKey
23+
1224

1325
class MainActivity : AppCompatActivity() {
1426

1527
companion object {
1628
private const val LOG_TAG = "MainActivity"
1729
}
1830

19-
private val biometricAuth: BiometricAuth by lazy { BiometricAuth.create(this) }
31+
private val cryptoManager: CryptoManager by lazy {
32+
CryptoManager()
33+
}
34+
35+
private val biometricAuth: BiometricAuth by lazy {
36+
BiometricAuth.create(this)
37+
}
2038

21-
private val button: Button by lazy { findViewById<Button>(R.id.button) }
39+
private val btnAuthenticate: Button by lazy { findViewById<Button>(R.id.btn_authenticate) }
40+
private val btnAuthenticateWithCrypto: Button by lazy { findViewById<Button>(R.id.btn_authenticate_with_crypto) }
2241

2342
override fun onCreate(savedInstanceState: Bundle?) {
2443
super.onCreate(savedInstanceState)
2544
setContentView(R.layout.activity_main)
2645

27-
button.setOnClickListener {
46+
btnAuthenticate.setOnClickListener {
2847
testAuthenticate()
2948
}
49+
50+
btnAuthenticateWithCrypto.setOnClickListener {
51+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
52+
Toast.makeText(this, "KeyGenerator only available on >= Marshmallow", Toast.LENGTH_SHORT).show()
53+
} else {
54+
testAuthenticateWithCrypto()
55+
}
56+
}
3057
}
3158

59+
3260
private fun testAuthenticate() {
3361
if (biometricAuth.hasFingerprintHardware.not()) {
3462
Toast.makeText(this, "Devices provides no fingerprint hardware", Toast.LENGTH_SHORT).show()
@@ -65,4 +93,114 @@ class MainActivity : AppCompatActivity() {
6593
)
6694
}
6795
}
96+
97+
98+
@TargetApi(Build.VERSION_CODES.M)
99+
private fun testAuthenticateWithCrypto() {
100+
if (biometricAuth.hasFingerprintHardware.not()) {
101+
Toast.makeText(this, "Devices provides no fingerprint hardware", Toast.LENGTH_SHORT).show()
102+
} else if (biometricAuth.hasFingerprintsEnrolled.not()) {
103+
Toast.makeText(this, "No fingerprints enrolled", Toast.LENGTH_SHORT).show()
104+
} else {
105+
biometricAuth.authenticate(
106+
cryptoObject = BiometricAuth.Crypto(cryptoManager.cipher),
107+
title = "Please authenticate",
108+
subtitle = "Using 'Awesome Feature' requires your authentication.",
109+
description = "'Awesome Feature' exposes data private to you, which is why you need to authenticate.",
110+
negativeButtonText = "Cancel",
111+
prompt = "Touch the fingerprint sensor",
112+
notRecognizedErrorText = "Not recognized"
113+
)
114+
.observeOn(AndroidSchedulers.mainThread())
115+
.subscribe(
116+
{
117+
Toast.makeText(this, "Success!", Toast.LENGTH_SHORT).show()
118+
Log.d(LOG_TAG, "onSuccess()")
119+
},
120+
{ throwable ->
121+
when (throwable) {
122+
is BiometricAuthenticationException -> {
123+
Toast.makeText(this, "Error: ${throwable.errorString}", Toast.LENGTH_SHORT).show()
124+
Log.e(LOG_TAG, "BiometricAuthenticationException(${throwable.errorMessageId}, '${throwable.errorString}')", throwable)
125+
}
126+
is BiometricAuthenticationCancelledException -> {
127+
Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show()
128+
Log.d(LOG_TAG, "onError(BiometricAuthenticationCancelledException)")
129+
}
130+
else -> Log.e(LOG_TAG, "onError()", throwable)
131+
}
132+
}
133+
)
134+
}
135+
}
136+
137+
138+
private class CryptoManager {
139+
140+
companion object {
141+
private const val KEY_NAME = "test-key"
142+
}
143+
144+
private var keyStore: KeyStore? = null
145+
private var keyGenerator: KeyGenerator? = null
146+
147+
148+
val cipher: Cipher by lazy {
149+
generateKey()
150+
initCipher()
151+
}
152+
153+
154+
@TargetApi(Build.VERSION_CODES.M)
155+
private fun generateKey() {
156+
try {
157+
keyStore = KeyStore.getInstance("AndroidKeyStore").also { keyStore ->
158+
keyStore.load(null)
159+
}
160+
161+
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore").also { keyGenerator ->
162+
keyGenerator?.init(KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
163+
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
164+
.setUserAuthenticationRequired(true)
165+
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
166+
.build())
167+
168+
keyGenerator?.generateKey()
169+
}
170+
} catch (e: KeyStoreException) {
171+
e.printStackTrace()
172+
} catch (e: NoSuchAlgorithmException) {
173+
e.printStackTrace()
174+
} catch (e: NoSuchProviderException) {
175+
e.printStackTrace()
176+
} catch (e: InvalidAlgorithmParameterException) {
177+
e.printStackTrace()
178+
} catch (e: CertificateException) {
179+
e.printStackTrace()
180+
} catch (e: IOException) {
181+
e.printStackTrace()
182+
}
183+
}
184+
185+
186+
@TargetApi(Build.VERSION_CODES.M)
187+
private fun initCipher(): Cipher {
188+
try {
189+
val cipher = Cipher.getInstance(
190+
KeyProperties.KEY_ALGORITHM_AES + "/"
191+
+ KeyProperties.BLOCK_MODE_CBC + "/"
192+
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
193+
194+
keyStore!!.load(null)
195+
val key = keyStore!!.getKey(KEY_NAME, null) as SecretKey
196+
cipher.init(Cipher.ENCRYPT_MODE, key)
197+
return cipher
198+
} catch (e: NoSuchAlgorithmException) {
199+
throw RuntimeException("Failed to init cipher", e)
200+
} catch (e: NoSuchPaddingException) {
201+
throw RuntimeException("Failed to init cipher", e)
202+
}
203+
}
204+
}
205+
68206
}

app/src/main/res/layout/activity_main.xml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,26 @@
77
tools:context=".MainActivity">
88

99
<Button
10-
android:id="@+id/button"
10+
android:id="@+id/btn_authenticate"
1111
android:layout_width="wrap_content"
1212
android:layout_height="wrap_content"
13+
android:layout_marginBottom="6dp"
1314
android:text="Authenticate"
15+
app:layout_constraintBottom_toTopOf="@+id/btn_authenticate_with_crypto"
16+
app:layout_constraintEnd_toEndOf="parent"
17+
app:layout_constraintStart_toStartOf="parent"
18+
app:layout_constraintTop_toTopOf="parent"
19+
app:layout_constraintVertical_chainStyle="packed" />
20+
21+
<Button
22+
android:id="@+id/btn_authenticate_with_crypto"
23+
android:layout_width="wrap_content"
24+
android:layout_height="wrap_content"
25+
android:layout_marginTop="6dp"
26+
android:text="Authenticate with Crypto"
1427
app:layout_constraintBottom_toBottomOf="parent"
1528
app:layout_constraintEnd_toEndOf="parent"
1629
app:layout_constraintStart_toStartOf="parent"
17-
app:layout_constraintTop_toTopOf="parent" />
30+
app:layout_constraintTop_toBottomOf="@+id/btn_authenticate" />
1831

1932
</android.support.constraint.ConstraintLayout>

biometricauth/build.gradle

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ android {
2424
dependencies {
2525
implementation fileTree(dir: 'libs', include: ['*.jar'])
2626
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
27-
implementation 'com.android.support:appcompat-v7:28.0.0'
28-
implementation 'com.android.support:design:28.0.0'
29-
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
27+
api 'com.android.support:appcompat-v7:28.0.0'
28+
api 'com.android.support:design:28.0.0'
29+
api 'com.android.support.constraint:constraint-layout:1.1.3'
3030

3131

32-
api 'io.reactivex.rxjava2:rxjava:2.2.6'
33-
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
32+
api 'io.reactivex.rxjava2:rxjava:2.2.9'
33+
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
3434
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
3535
}
3636

@@ -69,6 +69,18 @@ install {
6969
url siteUrl
7070
}
7171
}
72+
73+
pom.withXml {
74+
def dependenciesNode = asNode().appendNode('dependencies')
75+
76+
//Add all declared 'api' dependencies to the pom.xml
77+
configurations.api.allDependencies.withType(ModuleDependency) { ModuleDependency dp ->
78+
def dependencyNode = dependenciesNode.appendNode('dependency')
79+
dependencyNode.appendNode('groupId', dp.group)
80+
dependencyNode.appendNode('artifactId', dp.name)
81+
dependencyNode.appendNode('version', dp.version)
82+
}
83+
}
7284
}
7385
}
7486

biometricauth/src/main/java/com/tailoredapps/biometricauth/delegate/AuthenticationEvent.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ sealed class AuthenticationEvent {
6262
*
6363
* This event signals the end of the process, as the authentication has succeeded.
6464
* No further events will be sent.
65+
*
66+
* @param crypto The crypto-object, if set in the request
6567
*/
6668
@RestrictTo(RestrictTo.Scope.LIBRARY)
6769
data class Success(

biometricauth/src/main/java/com/tailoredapps/biometricauth/delegate/marshmallow/MarshmallowFingerprintDialog.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,11 @@ class MarshmallowFingerprintDialog : BottomSheetDialogFragment() {
153153
.subscribe(
154154
{ event ->
155155
if (event is AuthenticationEvent.Success) {
156-
emitter?.onComplete()
156+
if(event.crypto != null) {
157+
emitter?.onSuccess(event.crypto)
158+
} else {
159+
emitter?.onComplete()
160+
}
157161
} else if (event is AuthenticationEvent.Error) {
158162
if (event.messageId == 5) { //"Fingerprint operation cancelled."
159163
emitter?.onCancel()

build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,20 @@ ext {
2727
}
2828

2929
buildscript {
30-
ext.kotlin_version = '1.3.21'
30+
ext.kotlin_version = '1.3.31'
3131
repositories {
3232
google()
3333
jcenter()
3434
}
3535
dependencies {
36-
classpath 'com.android.tools.build:gradle:3.3.0-rc03'
36+
classpath 'com.android.tools.build:gradle:3.4.1'
3737
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
38-
classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0'
38+
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
3939

4040
//for publishing:
4141
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
4242
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
43-
classpath 'org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.17'
43+
classpath 'org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.18'
4444

4545
// NOTE: Do not place your application dependencies here; they belong
4646
// in the individual module build.gradle files

0 commit comments

Comments
 (0)