Skip to content

Commit

Permalink
Default auth timeout to 0sec, fix presentation bug with keys with 0 t…
Browse files Browse the repository at this point in the history
…imeout
  • Loading branch information
mitrejcevski committed Oct 2, 2023
1 parent 0c91366 commit 974da4c
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
private val passphraseViewModel: PassphrasePromptViewModel by activityViewModels()
private val arguments by navArgs<AuthConfirmationFragmentArgs>()
private var isSendingInProgress = mutableStateOf(false)
private var androidKeyUnlockData: AndroidKeystoreSecureArea.KeyUnlockData? = null

override fun onCreateView(
inflater: LayoutInflater,
Expand Down Expand Up @@ -73,7 +74,7 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
passphraseViewModel.authorizationState.collect { value ->
if (value is PassphraseAuthResult.Success) {
authenticationSucceeded(value.userPassphrase)
onPassphraseProvided(value.userPassphrase)
passphraseViewModel.reset()
}
}
Expand Down Expand Up @@ -145,8 +146,6 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
userAuthRequest.build().authenticate(cryptoObject)
}

private var androidKeyUnlockData: AndroidKeystoreSecureArea.KeyUnlockData? = null

private fun getSubtitle(): String {
val readerCommonName = arguments.readerCommonName
val readerIsTrusted = arguments.readerIsTrusted
Expand All @@ -161,7 +160,7 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
}
}

private fun authenticationSucceeded(passphrase: String) {
private fun onPassphraseProvided(passphrase: String) {
val unlockData = BouncyCastleSecureArea.KeyUnlockData(passphrase)
val result = viewModel.sendResponseForSelection(unlockData)
onSendResponseResult(result)
Expand All @@ -181,8 +180,7 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
private fun onSendResponseResult(result: AddDocumentToResponseResult) {
when (result) {
is AddDocumentToResponseResult.UserAuthRequired -> {
val keyUnlockData = AndroidKeystoreSecureArea.KeyUnlockData(result.keyAlias)
androidKeyUnlockData = keyUnlockData
androidKeyUnlockData = AndroidKeystoreSecureArea.KeyUnlockData(result.keyAlias)
requestUserAuth(
result.allowLSKFUnlocking,
result.allowBiometricUnlocking
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ data class AddSelfSignedScreenState(
val documentName: String = "Driving License",
val secureAreaImplementationState: SecureAreaImplementationState = SecureAreaImplementationState.Android,
val userAuthentication: Boolean = true,
val userAuthenticationTimeoutSeconds: Int = 10,
val userAuthenticationTimeoutSeconds: Int = 0,
val allowLSKFUnlocking: AuthTypeState = AuthTypeState(
isEnabled = true,
canBeModified = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.graphics.Color.WHITE
import android.nfc.cardemulation.HostApduService
import android.view.View
import android.widget.ImageView
import androidx.biometric.BiometricPrompt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.android.identity.*
Expand Down Expand Up @@ -40,8 +39,6 @@ import java.util.*
class TransferManager private constructor(private val context: Context) {

companion object {
private const val LOG_TAG = "TransferManager"

@SuppressLint("StaticFieldLeak")
@Volatile
private var instance: TransferManager? = null
Expand All @@ -55,15 +52,13 @@ class TransferManager private constructor(private val context: Context) {
private var reversedQrCommunicationSetup: ReverseQrCommunicationSetup? = null
private var qrCommunicationSetup: QrCommunicationSetup? = null
private var hostApduService: HostApduService? = null
private var session: PresentationSession? = null
private var hasStarted = false

private lateinit var communication: Communication

private var transferStatusLd = MutableLiveData<TransferStatus>()

fun setCommunication(session: PresentationSession, communication: Communication) {
this.session = session
this.communication = communication
}

Expand All @@ -88,7 +83,6 @@ class TransferManager private constructor(private val context: Context) {
reversedQrCommunicationSetup = ReverseQrCommunicationSetup(
context = context,
onPresentationReady = { session, presentation ->
this.session = session
communication.setupPresentation(presentation)
},
onNewRequest = { request ->
Expand Down Expand Up @@ -116,7 +110,6 @@ class TransferManager private constructor(private val context: Context) {
onConnecting = { transferStatusLd.value = TransferStatus.CONNECTING },
onQrEngagementReady = { transferStatusLd.value = TransferStatus.QR_ENGAGEMENT_READY },
onDeviceRetrievalHelperReady = { session, deviceRetrievalHelper ->
this.session = session
communication.setupPresentation(deviceRetrievalHelper)
transferStatusLd.value = TransferStatus.CONNECTED
},
Expand Down Expand Up @@ -178,76 +171,73 @@ class TransferManager private constructor(private val context: Context) {
keyUnlockData: SecureArea.KeyUnlockData?
): AddDocumentToResponseResult {
var signingKeyUsageLimitPassed = false
session?.let {
val documentManager = DocumentManager.getInstance(context)
val documentInformation = documentManager.getDocumentInformation(credentialName)
requireValidProperty(documentInformation) { "Document not found!" }
val documentManager = DocumentManager.getInstance(context)
val documentInformation = documentManager.getDocumentInformation(credentialName)
requireValidProperty(documentInformation) { "Document not found!" }

val credential = requireNotNull(documentManager.getCredentialByName(credentialName))
val dataElements = issuerSignedEntriesToRequest.keys.flatMap { key ->
issuerSignedEntriesToRequest.getOrDefault(key, emptyList()).map { value ->
CredentialRequest.DataElement(key, value, false)
}
val credential = requireNotNull(documentManager.getCredentialByName(credentialName))
val dataElements = issuerSignedEntriesToRequest.keys.flatMap { key ->
issuerSignedEntriesToRequest.getOrDefault(key, emptyList()).map { value ->
CredentialRequest.DataElement(key, value, false)
}
}

val request = CredentialRequest(dataElements)
val authKey = credential.findAuthenticationKey(Timestamp.now())
?: throw IllegalStateException("No auth key available")
if (authKey.usageCount >= documentInformation.maxUsagesPerKey) {
logWarning("Using Auth Key previously used ${authKey.usageCount} times, and maxUsagesPerKey is ${documentInformation.maxUsagesPerKey}")
signingKeyUsageLimitPassed = true
}
val request = CredentialRequest(dataElements)
val authKey = credential.findAuthenticationKey(Timestamp.now())
?: throw IllegalStateException("No auth key available")
if (authKey.usageCount >= documentInformation.maxUsagesPerKey) {
logWarning("Using Auth Key previously used ${authKey.usageCount} times, and maxUsagesPerKey is ${documentInformation.maxUsagesPerKey}")
signingKeyUsageLimitPassed = true
}

val staticAuthData = StaticAuthDataParser(authKey.issuerProvidedData).parse()
val mergedIssuerNamespaces = MdocUtil.mergeIssuerNamesSpaces(
request, credential.nameSpacedData, staticAuthData
)
val staticAuthData = StaticAuthDataParser(authKey.issuerProvidedData).parse()
val mergedIssuerNamespaces = MdocUtil.mergeIssuerNamesSpaces(
request, credential.nameSpacedData, staticAuthData
)

val transcript = communication.getSessionTranscript() ?: byteArrayOf()
val authOption = AddSelfSignedScreenState.MdocAuthStateOption.valueOf(documentInformation.mDocAuthOption)
try {
val generator = DocumentGenerator(docType, staticAuthData.issuerAuth, transcript)
.setIssuerNamespaces(mergedIssuerNamespaces)
if (authOption == AddSelfSignedScreenState.MdocAuthStateOption.ECDSA) {
generator.setDeviceNamespacesSignature(NameSpacedData.Builder().build(),
authKey.secureArea,
authKey.alias,
keyUnlockData,
SecureArea.ALGORITHM_ES256
)
} else {
generator.setDeviceNamespacesMac(NameSpacedData.Builder().build(),
authKey.secureArea,
authKey.alias,
keyUnlockData,
authKey.attestation.first().publicKey
)
}
val data = generator.generate()
keyUnlockData?.let {
if (authOption == AddSelfSignedScreenState.MdocAuthStateOption.ECDSA) {
credential.credentialSecureArea.sign(authKey.alias, SecureArea.ALGORITHM_ES256, data, it)
} else {
credential.credentialSecureArea.keyAgreement(authKey.alias, authKey.attestation.first().publicKey, it)
}
}
deviceResponseGenerator.addDocument(data)
authKey.increaseUsageCount()
ProvisioningUtil.getInstance(context).trackUsageTimestamp(credential)
} catch (lockedException: SecureArea.KeyLockedException) {
return if (credential.credentialSecureArea is AndroidKeystoreSecureArea) {
val keyInfo = credential.credentialSecureArea.getKeyInfo(authKey.alias) as AndroidKeystoreSecureArea.KeyInfo
val allowLskf = keyInfo.userAuthenticationType == USER_AUTHENTICATION_TYPE_LSKF
val allowBiometric = keyInfo.userAuthenticationType == USER_AUTHENTICATION_TYPE_BIOMETRIC
val allowBoth = keyInfo.userAuthenticationType == USER_AUTHENTICATION_TYPE_LSKF or USER_AUTHENTICATION_TYPE_BIOMETRIC
AddDocumentToResponseResult.UserAuthRequired(
keyAlias = authKey.alias,
allowLSKFUnlocking = allowLskf || allowBoth,
allowBiometricUnlocking = allowBiometric || allowBoth
)
} else {
AddDocumentToResponseResult.PassphraseRequired
}
val transcript = communication.getSessionTranscript() ?: byteArrayOf()
val authOption =
AddSelfSignedScreenState.MdocAuthStateOption.valueOf(documentInformation.mDocAuthOption)
try {
val generator = DocumentGenerator(docType, staticAuthData.issuerAuth, transcript)
.setIssuerNamespaces(mergedIssuerNamespaces)
if (authOption == AddSelfSignedScreenState.MdocAuthStateOption.ECDSA) {
generator.setDeviceNamespacesSignature(
NameSpacedData.Builder().build(),
authKey.secureArea,
authKey.alias,
keyUnlockData,
SecureArea.ALGORITHM_ES256
)
} else {
generator.setDeviceNamespacesMac(
NameSpacedData.Builder().build(),
authKey.secureArea,
authKey.alias,
keyUnlockData,
authKey.attestation.first().publicKey
)
}
val data = generator.generate()
deviceResponseGenerator.addDocument(data)
authKey.increaseUsageCount()
ProvisioningUtil.getInstance(context).trackUsageTimestamp(credential)
} catch (lockedException: SecureArea.KeyLockedException) {
return if (credential.credentialSecureArea is AndroidKeystoreSecureArea) {
val keyInfo =
credential.credentialSecureArea.getKeyInfo(authKey.alias) as AndroidKeystoreSecureArea.KeyInfo
val allowLskf = keyInfo.userAuthenticationType == USER_AUTHENTICATION_TYPE_LSKF
val allowBiometric =
keyInfo.userAuthenticationType == USER_AUTHENTICATION_TYPE_BIOMETRIC
val allowBoth =
keyInfo.userAuthenticationType == USER_AUTHENTICATION_TYPE_LSKF or USER_AUTHENTICATION_TYPE_BIOMETRIC
AddDocumentToResponseResult.UserAuthRequired(
keyAlias = authKey.alias,
allowLSKFUnlocking = allowLskf || allowBoth,
allowBiometricUnlocking = allowBiometric || allowBoth
)
} else {
AddDocumentToResponseResult.PassphraseRequired
}
}
return AddDocumentToResponseResult.DocumentAdded(signingKeyUsageLimitPassed)
Expand All @@ -274,7 +264,6 @@ class TransferManager private constructor(private val context: Context) {
fun destroy() {
qrCommunicationSetup = null
reversedQrCommunicationSetup = null
session = null
hasStarted = false
}

Expand Down

0 comments on commit 974da4c

Please sign in to comment.