Skip to content

Commit

Permalink
Fix/bouncy castle support (#375)
Browse files Browse the repository at this point in the history
* AndroidSecureArea keys creation enhancements

* Add support for BoucyCastle SecureArea when creating self signed doc

* Default auth timeout to 0sec, fix presentation bug with keys with 0 timeout
  • Loading branch information
mitrejcevski authored Oct 4, 2023
1 parent f8b3596 commit f2293ea
Show file tree
Hide file tree
Showing 15 changed files with 352 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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.BouncyCastleSecureArea
import com.android.identity.securearea.SecureArea.ALGORITHM_ES256
import com.android.mdl.app.R
import com.android.mdl.app.authprompt.UserAuthPromptBuilder
Expand All @@ -37,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 @@ -72,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 @@ -144,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 @@ -160,9 +160,10 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
}
}

private fun authenticationSucceeded(passphrase: String) {
viewModel.sendResponseForSelection()
findNavController().navigateUp()
private fun onPassphraseProvided(passphrase: String) {
val unlockData = BouncyCastleSecureArea.KeyUnlockData(passphrase)
val result = viewModel.sendResponseForSelection(unlockData)
onSendResponseResult(result)
}

private fun authenticationSucceeded() {
Expand All @@ -179,16 +180,15 @@ 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
)
}

is AddDocumentToResponseResult.PassphraseRequired -> {
requestPassphrase()
requestPassphrase(result.attemptedWithIncorrectPassword)
}

is AddDocumentToResponseResult.DocumentAdded -> {
Expand All @@ -210,10 +210,13 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
viewModel.closeConnection()
}

private fun requestPassphrase() {
val destination = AuthConfirmationFragmentDirections
.openPassphrasePrompt()
findNavController().navigate(destination)
private fun requestPassphrase(attemptedWithIncorrectPassword: Boolean) {
val destination = AuthConfirmationFragmentDirections.openPassphrasePrompt(
showIncorrectPassword = attemptedWithIncorrectPassword
)
val runnable = { findNavController().navigate(destination) }
// The system needs a little time to get back to this screen
Handler(Looper.getMainLooper()).postDelayed(runnable, 500)
}

private fun toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
Expand All @@ -23,17 +24,21 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.android.mdl.app.R
import com.android.mdl.app.composables.PreviewLightDark
import com.android.mdl.app.theme.HolderAppTheme

class PassphrasePrompt : DialogFragment() {

private val args by navArgs<PassphrasePromptArgs>()
private val viewModel by activityViewModels<PassphrasePromptViewModel>()

override fun onCreateView(
Expand All @@ -45,6 +50,7 @@ class PassphrasePrompt : DialogFragment() {
setContent {
HolderAppTheme {
PassphrasePromptUI(
showIncorrectPassword = args.showIncorrectPassword,
onDone = { passphrase ->
viewModel.authorize(userPassphrase = passphrase)
findNavController().navigateUp()
Expand All @@ -58,6 +64,7 @@ class PassphrasePrompt : DialogFragment() {

@Composable
private fun PassphrasePromptUI(
showIncorrectPassword: Boolean,
onDone: (passphrase: String) -> Unit
) {
var value by remember { mutableStateOf("") }
Expand All @@ -82,8 +89,23 @@ private fun PassphrasePromptUI(
modifier = Modifier.fillMaxWidth(),
value = value,
onValueChange = { value = it },
textStyle = MaterialTheme.typography.bodyMedium
textStyle = MaterialTheme.typography.bodyMedium,
visualTransformation = PasswordVisualTransformation(),
placeholder = {
Text(
text = stringResource(id = R.string.passphrase_prompt_hint),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
)
}
)
if (showIncorrectPassword) {
Text(
text = stringResource(id = R.string.passphrase_prompt_incorrect_passphrase),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.error
)
}
TextButton(
modifier = Modifier
.align(Alignment.End),
Expand All @@ -95,9 +117,23 @@ private fun PassphrasePromptUI(
}

@Composable
@Preview
@PreviewLightDark
private fun PreviewPassphrasePrompt() {
HolderAppTheme {
PassphrasePromptUI(onDone = {})
PassphrasePromptUI(
showIncorrectPassword = false,
onDone = {}
)
}
}

@Composable
@PreviewLightDark
private fun PreviewPassphrasePromptWithIncorrectPassword() {
HolderAppTheme {
PassphrasePromptUI(
showIncorrectPassword = true,
onDone = {}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ data class DocumentInformation(
val maxUsagesPerKey: Int,
val lastTimeUsed: String,
val mDocAuthOption: String,
val secureAreaImplementationState: SecureAreaImplementationState,
val authKeys: List<KeyData>
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class DocumentInfoViewModel(
documentType = documentInformation.docType,
documentColor = documentInformation.documentColor.toCardArt(),
provisioningDate = documentInformation.dateProvisioned,
secureAreaImplementationState = documentInformation.secureAreaImplementationState,
isSelfSigned = documentInformation.selfSigned,
lastTimeUsedDate = documentInformation.lastTimeUsed,
authKeys = documentInformation.authKeys.asScreenStateKeys()
Expand Down
Loading

0 comments on commit f2293ea

Please sign in to comment.