Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AndroidSecureArea keys creation improvements #365

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class DocumentAdapter : ListAdapter<DocumentInformation, RecyclerView.ViewHolder
}

private fun navigateToDetail(document: DocumentInformation, view: View) {
val direction = SelectDocumentFragmentDirections.toDocumentDetail(document.userVisibleName)
val direction = SelectDocumentFragmentDirections.toDocumentDetail(document.docName)
if (view.findNavController().currentDestination?.id == R.id.wallet) {
view.findNavController().navigate(direction)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.android.identity.android.securearea.AndroidKeystoreSecureArea
import com.android.identity.securearea.SecureArea.ALGORITHM_ES256
import com.android.mdl.app.R
import com.android.mdl.app.authprompt.UserAuthPromptBuilder
import com.android.mdl.app.theme.HolderAppTheme
Expand Down Expand Up @@ -112,26 +114,38 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {

private fun sendResponse() {
isSendingInProgress.value = true
when (viewModel.sendResponseForSelection()) {
is AddDocumentToResponseResult.UserAuthRequired -> requestUserAuth(false)
is AddDocumentToResponseResult.PassphraseRequired -> requestPassphrase()
else -> findNavController().navigateUp()
}
val result = viewModel.sendResponseForSelection()
onSendResponseResult(result)
}

private fun requestUserAuth(forceLskf: Boolean) {
private fun requestUserAuth(
allowLskfUnlock: Boolean,
allowBiometricUnlock: Boolean,
forceLskf: Boolean = !allowBiometricUnlock
) {
val userAuthRequest = UserAuthPromptBuilder.requestUserAuth(this)
.withTitle(getString(R.string.bio_auth_title))
.withNegativeButton(getString(R.string.bio_auth_use_pin))
.withSuccessCallback { authenticationSucceeded() }
.withCancelledCallback { retryForcingPinUse() }
.withCancelledCallback {
if (allowLskfUnlock) {
retryForcingPinUse(allowLskfUnlock, allowBiometricUnlock)
} else {
cancelAuthorization()
}
}
.withFailureCallback { authenticationFailed() }
.setForceLskf(forceLskf)
.build()
val cryptoObject = viewModel.getCryptoObject()
userAuthRequest.authenticate(cryptoObject)
if (allowLskfUnlock) {
userAuthRequest.withNegativeButton(getString(R.string.bio_auth_use_pin))
} else {
userAuthRequest.withNegativeButton("Cancel")
}
val cryptoObject = androidKeyUnlockData?.getCryptoObjectForSigning(ALGORITHM_ES256)
userAuthRequest.build().authenticate(cryptoObject)
}

private var androidKeyUnlockData: AndroidKeystoreSecureArea.KeyUnlockData? = null

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

private fun authenticationSucceeded(passphrase: String) {
viewModel.sendResponseForSelection(true, passphrase)
viewModel.sendResponseForSelection()
findNavController().navigateUp()
}

private fun authenticationSucceeded() {
try {
viewModel.sendResponseForSelection(true)
findNavController().navigateUp()
val result = viewModel.sendResponseForSelection(keyUnlockData = androidKeyUnlockData)
onSendResponseResult(result)
} catch (e: Exception) {
val message = "Send response error: ${e.message}"
log(message, e)
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
toast(message)
}
}

private fun retryForcingPinUse() {
val runnable = { requestUserAuth(true) }
private fun onSendResponseResult(result: AddDocumentToResponseResult) {
when (result) {
is AddDocumentToResponseResult.UserAuthRequired -> {
val keyUnlockData = AndroidKeystoreSecureArea.KeyUnlockData(result.keyAlias)
androidKeyUnlockData = keyUnlockData
requestUserAuth(
result.allowLSKFUnlocking,
result.allowBiometricUnlocking
)
}

is AddDocumentToResponseResult.PassphraseRequired -> {
requestPassphrase()
}

is AddDocumentToResponseResult.DocumentAdded -> {
if (result.signingKeyUsageLimitPassed) {
toast("Using previously used Auth Key")
}
findNavController().navigateUp()
}
}
}

private fun retryForcingPinUse(allowLsfk: Boolean, allowBiometric: Boolean) {
val runnable = { requestUserAuth(allowLsfk, allowBiometric, true) }
// Without this delay, the prompt won't reshow
Handler(Looper.getMainLooper()).postDelayed(runnable, 100)
}
Expand All @@ -177,4 +215,8 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
.openPassphrasePrompt()
findNavController().navigate(destination)
}

private fun toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(requireContext(), message, duration).show()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.android.identity.mdoc.request.DeviceRequestParser
data class RequestedDocumentData(
val userReadableName: String,
val identityCredentialName: String,
val needsAuth: Boolean,
val requestedElements: ArrayList<RequestedElement>,
val requestedDocument: DeviceRequestParser.DocumentRequest
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ class SignedDocumentData(
private val signedElements: List<RequestedElement>,
val identityCredentialName: String,
val documentType: String,
val readerAuth: ByteArray?,
val itemsRequest: ByteArray
) {

fun issuerSignedEntries(): MutableMap<String, Collection<String>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ class SignedElementsCollection {
return requestedDocuments.keys.map { namespace ->
val document = requestedDocuments.getValue(namespace)
SignedDocumentData(
signedElements,
document.userReadableName,
document.requestedDocument.docType,
document.requestedDocument.readerAuth,
document.requestedDocument.itemsRequest
signedElements = signedElements,
identityCredentialName = document.identityCredentialName,
documentType = document.requestedDocument.docType,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.android.mdl.app.composables

import android.content.res.Configuration
import androidx.compose.ui.tooling.preview.Preview

@Preview(name = "Light", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
annotation class PreviewLightDark
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package com.android.mdl.app.document

data class DocumentInformation(
val userVisibleName: String,
val docName: String,
val docType: String,
val dateProvisioned: String,
val selfSigned: Boolean,
val documentColor: Int,
val maxUsagesPerKey: Int,
val lastTimeUsed: String,
val mDocAuthOption: String,
val authKeys: List<KeyData>
) {

Expand All @@ -15,7 +18,10 @@ data class DocumentInformation(
val validFrom: String,
val validUntil: String,
val issuerDataBytesCount: Int,
val usagesCount: Int
val usagesCount: Int,
val keyPurposes: Int,
val ecCurve: Int,
val isHardwareBacked: Boolean
)
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import com.android.identity.*
import com.android.identity.android.legacy.*
import com.android.identity.credential.Credential
import com.android.identity.credential.NameSpacedData
import com.android.mdl.app.keystore.CredentialUtil
import com.android.mdl.app.keystore.CredentialUtil.Companion.toDocumentInformation
import com.android.mdl.app.util.ProvisioningUtil
import com.android.mdl.app.util.ProvisioningUtil.Companion.toDocumentInformation
import com.android.mdl.app.selfsigned.SelfSignedDocumentData
import com.android.mdl.app.util.DocumentData
import com.android.mdl.app.util.DocumentData.EU_PID_DOCTYPE
Expand Down Expand Up @@ -40,22 +40,22 @@ class DocumentManager private constructor(private val context: Context) {
}

fun getDocumentInformation(documentName: String): DocumentInformation? {
val credentialStore = CredentialUtil.getInstance(context).credentialStore
val credentialStore = ProvisioningUtil.getInstance(context).credentialStore
val credential = credentialStore.lookupCredential(documentName)
return credential.toDocumentInformation()
}

fun getCredentialByName(documentName: String): Credential? {
val documentInfo = getDocumentInformation(documentName)
documentInfo?.let {
val credentialStore = CredentialUtil.getInstance(context).credentialStore
val credentialStore = ProvisioningUtil.getInstance(context).credentialStore
return credentialStore.lookupCredential(documentName)
}
return null
}

fun getDocuments(): List<DocumentInformation> {
val credentialStore = CredentialUtil.getInstance(context).credentialStore
val credentialStore = ProvisioningUtil.getInstance(context).credentialStore
return credentialStore.listCredentials().mapNotNull { documentName ->
val credential = credentialStore.lookupCredential(documentName)
credential.toDocumentInformation()
Expand All @@ -65,7 +65,7 @@ class DocumentManager private constructor(private val context: Context) {
fun deleteCredentialByName(documentName: String) {
val document = getDocumentInformation(documentName)
document?.let {
val credentialStore = CredentialUtil.getInstance(context).credentialStore
val credentialStore = ProvisioningUtil.getInstance(context).credentialStore
credentialStore.deleteCredential(documentName)
}
}
Expand Down Expand Up @@ -95,7 +95,7 @@ class DocumentManager private constructor(private val context: Context) {
docName: String = documentData.provisionInfo.docName,
count: Int = 1
): String {
val store = CredentialUtil.getInstance(context).credentialStore
val store = ProvisioningUtil.getInstance(context).credentialStore
store.listCredentials().forEach { name ->
if (name == docName) {
return getUniqueDocumentName(documentData, "$docName ($count)", count + 1)
Expand Down Expand Up @@ -246,7 +246,7 @@ class DocumentManager private constructor(private val context: Context) {
)
.build()

CredentialUtil.getInstance(context)
ProvisioningUtil.getInstance(context)
.provisionSelfSigned(nameSpacedData, documentData.provisionInfo)
}

Expand Down Expand Up @@ -304,7 +304,7 @@ class DocumentManager private constructor(private val context: Context) {
.putEntryString(MVR_NAMESPACE, "vin", "1M8GDM9AXKP042788")
.build()

CredentialUtil.getInstance(context)
ProvisioningUtil.getInstance(context)
.provisionSelfSigned(nameSpacedData, documentData.provisionInfo)
}

Expand Down Expand Up @@ -468,7 +468,7 @@ class DocumentManager private constructor(private val context: Context) {
)
.build()

CredentialUtil.getInstance(context)
ProvisioningUtil.getInstance(context)
.provisionSelfSigned(nameSpacedData, documentData.provisionInfo)
}

Expand Down Expand Up @@ -630,7 +630,7 @@ class DocumentManager private constructor(private val context: Context) {
)
.build()

CredentialUtil.getInstance(context)
ProvisioningUtil.getInstance(context)
.provisionSelfSigned(nameSpacedData, documentData.provisionInfo)
}

Expand All @@ -643,6 +643,6 @@ class DocumentManager private constructor(private val context: Context) {
fun refreshAuthKeys(documentName: String) {
val documentInformation = requireNotNull(getDocumentInformation(documentName))
val credential = requireNotNull(getCredentialByName(documentName))
CredentialUtil.getInstance(context).refreshAuthKeys(credential, documentInformation)
ProvisioningUtil.getInstance(context).refreshAuthKeys(credential, documentInformation)
}
}
Loading
Loading