@@ -63,6 +63,7 @@ import kotlinx.coroutines.*
63
63
import kotlinx.serialization.encodeToString
64
64
import java.net.URI
65
65
import java.util.concurrent.Executors
66
+ import java.util.concurrent.atomic.AtomicBoolean
66
67
import kotlin.coroutines.suspendCoroutine
67
68
68
69
typealias OathAction = (Result <YubiKitOathSession , Exception >) -> Unit
@@ -100,6 +101,7 @@ class OathManager(
100
101
@TargetApi(Build .VERSION_CODES .M )
101
102
private fun createKeyStoreProviderM (): KeyProvider = KeyStoreProvider ()
102
103
104
+ private val unlockOnConnect = AtomicBoolean (true )
103
105
private var pendingAction: OathAction ? = null
104
106
private var refreshJob: Job ? = null
105
107
private var addToAny = false
@@ -241,8 +243,7 @@ class OathManager(
241
243
override suspend fun processYubiKey (device : YubiKeyDevice ) {
242
244
try {
243
245
device.withConnection<SmartCardConnection , Unit > { connection ->
244
- val session = YubiKitOathSession (connection)
245
-
246
+ val session = getOathSession(connection)
246
247
val previousId = oathViewModel.sessionState.value?.deviceId
247
248
if (session.deviceId == previousId && device is NfcYubiKeyDevice ) {
248
249
// Run any pending action
@@ -414,7 +415,7 @@ class OathManager(
414
415
currentPassword : String? ,
415
416
newPassword : String ,
416
417
): String =
417
- useOathSession(" Set password" ) { session ->
418
+ useOathSession(" Set password" , unlock = false ) { session ->
418
419
if (session.isAccessKeySet) {
419
420
if (currentPassword == null ) {
420
421
throw Exception (" Must provide current password to be able to change it" )
@@ -599,6 +600,29 @@ class OathManager(
599
600
return false // the unlock did not work, session is locked
600
601
}
601
602
603
+ /* *
604
+ * Returns a [YubiKitOathSession] for the [connection].
605
+ * The session will be unlocked if [unlockOnConnect] is true.
606
+ *
607
+ * Generally we always want to try to unlock the session and that is why the variable
608
+ * [unlockOnConnect] is also reset to true.
609
+ *
610
+ * Currently, only setPassword and unsetPassword will not unlock the session.
611
+ *
612
+ * @param connection the device SmartCard connection
613
+ * @return a [YubiKitOathSession] which is unlocked or locked based on an internal parameter
614
+ */
615
+ private fun getOathSession (connection : SmartCardConnection ) : YubiKitOathSession {
616
+ val session = YubiKitOathSession (connection)
617
+
618
+ if (! unlockOnConnect.compareAndSet(false , true )) {
619
+ tryToUnlockOathSession(session)
620
+ }
621
+
622
+ return session
623
+ }
624
+
625
+
602
626
private fun calculateOathCodes (session : YubiKitOathSession ): Map <Credential , Code ?> {
603
627
val isUsbKey = appViewModel.connectedYubiKey.value != null
604
628
var timestamp = System .currentTimeMillis()
@@ -626,37 +650,30 @@ class OathManager(
626
650
unlock : Boolean = true,
627
651
action : (YubiKitOathSession ) -> T
628
652
): T {
653
+
654
+ // callers can decide whether the session should be unlocked first
655
+ unlockOnConnect.set(unlock)
629
656
return appViewModel.connectedYubiKey.value?.let {
630
- useOathSessionUsb(it, unlock, action)
631
- } ? : useOathSessionNfc(title, unlock, action)
657
+ useOathSessionUsb(it, action)
658
+ } ? : useOathSessionNfc(title, action)
632
659
}
633
660
634
661
private suspend fun <T > useOathSessionUsb (
635
662
device : UsbYubiKeyDevice ,
636
- unlock : Boolean = true,
637
663
block : (YubiKitOathSession ) -> T
638
664
): T = device.withConnection<SmartCardConnection , T > {
639
- val session = YubiKitOathSession (it)
640
- if (unlock) {
641
- tryToUnlockOathSession(session)
642
- }
643
- block(session)
665
+ block(getOathSession(it))
644
666
}
645
667
646
668
private suspend fun <T > useOathSessionNfc (
647
669
title : String ,
648
- unlock : Boolean = true,
649
670
block : (YubiKitOathSession ) -> T
650
671
): T {
651
672
try {
652
673
val result = suspendCoroutine { outer ->
653
674
pendingAction = {
654
675
outer.resumeWith(runCatching {
655
- val session = it.value
656
- if (unlock) {
657
- tryToUnlockOathSession(session)
658
- }
659
- block.invoke(session)
676
+ block.invoke(it.value)
660
677
})
661
678
}
662
679
dialogManager.showDialog(Icon .NFC , " Tap your key" , title) {
0 commit comments