Skip to content

Commit

Permalink
AndroidSecureArea keys creation enhancements (#365)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitrejcevski authored Sep 21, 2023
1 parent 034099e commit 4d8893d
Show file tree
Hide file tree
Showing 24 changed files with 1,082 additions and 262 deletions.
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

0 comments on commit 4d8893d

Please sign in to comment.