Skip to content

Commit e87e9ac

Browse files
committed
Display DeviceKey security details in credential info screen.
Enhance `CredentialInfoScreen` to inspect and display security properties of `KeyInfo` specializations for device keys. For `AndroidKeystoreKeyInfo`, display StrongBox backing status, user authentication requirement, authentication timeout (e.g. 0 for every use), permitted authentication types, attest key alias, and key validity bounds. For `SoftwareKeyInfo` and `CloudKeyInfo`, display passphrase protection status and user authentication requirements/types. Test: Manually tested. Test: Executed `./gradlew :androidApp:assembleDebug :androidApp:testDebugUnitTest` successfully. Signed-off-by: David Zeuthen <zeuthen@gmail.com>
1 parent 8cae470 commit e87e9ac

1 file changed

Lines changed: 88 additions & 4 deletions

File tree

androidApp/src/main/java/org/multipaz/wallet/android/ui/document/CredentialInfoScreen.kt

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ import org.multipaz.mdoc.credential.MdocCredential
6868
import org.multipaz.sdjwt.credential.SdJwtVcCredential
6969
import org.multipaz.util.Logger
7070
import org.multipaz.util.toHex
71+
import kotlin.time.Duration
72+
import org.multipaz.securearea.AndroidKeystoreKeyInfo
73+
import org.multipaz.securearea.software.SoftwareKeyInfo
74+
import org.multipaz.securearea.cloud.CloudKeyInfo
7175
import org.multipaz.wallet.android.ui.Note
7276

7377
private const val TAG = "CredentialInfoScreen"
@@ -220,6 +224,7 @@ private fun CredentialInfoSection(
220224
}
221225

222226
if (credentialInfo.credential is SecureAreaBoundCredential) {
227+
val keyInfo = credentialInfo.keyInfo
223228
FloatingItemHeadingAndText(
224229
"Secure Area",
225230
(credentialInfo.credential as SecureAreaBoundCredential).secureArea.displayName
@@ -228,10 +233,89 @@ private fun CredentialInfoSection(
228233
"Secure Area Identifier",
229234
(credentialInfo.credential as SecureAreaBoundCredential).secureArea.identifier
230235
)
231-
FloatingItemHeadingAndText(
232-
"Device Key Algorithm",
233-
credentialInfo.keyInfo!!.algorithm.description
234-
)
236+
if (keyInfo != null) {
237+
FloatingItemHeadingAndText(
238+
"Device Key Algorithm",
239+
keyInfo.algorithm.description
240+
)
241+
when (keyInfo) {
242+
is AndroidKeystoreKeyInfo -> {
243+
FloatingItemHeadingAndText(
244+
"Device Key in StrongBox",
245+
if (keyInfo.isStrongBoxBacked) "Yes" else "No"
246+
)
247+
FloatingItemHeadingAndText(
248+
"Device Key User Auth Required",
249+
if (keyInfo.isUserAuthenticationRequired) "Yes" else "No"
250+
)
251+
if (keyInfo.isUserAuthenticationRequired) {
252+
val timeoutText = if (keyInfo.userAuthenticationTimeout == Duration.ZERO) {
253+
"0 (Authenticate for every use)"
254+
} else {
255+
"${keyInfo.userAuthenticationTimeout.inWholeSeconds} s"
256+
}
257+
FloatingItemHeadingAndText("Device Key User Auth Timeout", timeoutText)
258+
val typesText = if (keyInfo.userAuthenticationTypes.isEmpty()) {
259+
"None"
260+
} else {
261+
keyInfo.userAuthenticationTypes.joinToString(", ") { it.name }
262+
}
263+
FloatingItemHeadingAndText("Device Key User Auth Types", typesText)
264+
}
265+
if (keyInfo.attestKeyAlias != null) {
266+
FloatingItemHeadingAndText("Device Key Attest Key Alias", keyInfo.attestKeyAlias!!)
267+
}
268+
if (keyInfo.validFrom != null) {
269+
FloatingItemHeadingAndText("Device Key Valid From", formattedDateTime(keyInfo.validFrom!!))
270+
}
271+
if (keyInfo.validUntil != null) {
272+
FloatingItemHeadingAndText("Device Key Valid Until", formattedDateTime(keyInfo.validUntil!!))
273+
}
274+
}
275+
is SoftwareKeyInfo -> {
276+
FloatingItemHeadingAndText(
277+
"Device Key Passphrase Protected",
278+
if (keyInfo.isPassphraseProtected) "Yes" else "No"
279+
)
280+
FloatingItemHeadingAndText(
281+
"Device Key User Auth Required",
282+
if (keyInfo.isUserAuthenticationRequired) "Yes" else "No"
283+
)
284+
if (keyInfo.isUserAuthenticationRequired) {
285+
val typesText = if (keyInfo.userAuthenticationTypes.isEmpty()) {
286+
"None"
287+
} else {
288+
keyInfo.userAuthenticationTypes.joinToString(", ") { it.name }
289+
}
290+
FloatingItemHeadingAndText("Device Key User Auth Types", typesText)
291+
}
292+
}
293+
is CloudKeyInfo -> {
294+
FloatingItemHeadingAndText(
295+
"Device Key Passphrase Required",
296+
if (keyInfo.isPassphraseRequired) "Yes" else "No"
297+
)
298+
FloatingItemHeadingAndText(
299+
"Device Key User Auth Required",
300+
if (keyInfo.isUserAuthenticationRequired) "Yes" else "No"
301+
)
302+
if (keyInfo.isUserAuthenticationRequired) {
303+
val typesText = if (keyInfo.userAuthenticationTypes.isEmpty()) {
304+
"None"
305+
} else {
306+
keyInfo.userAuthenticationTypes.joinToString(", ") { it.name }
307+
}
308+
FloatingItemHeadingAndText("Device Key User Auth Types", typesText)
309+
}
310+
if (keyInfo.validFrom != null) {
311+
FloatingItemHeadingAndText("Device Key Valid From", formattedDateTime(keyInfo.validFrom!!))
312+
}
313+
if (keyInfo.validUntil != null) {
314+
FloatingItemHeadingAndText("Device Key Valid Until", formattedDateTime(keyInfo.validUntil!!))
315+
}
316+
}
317+
}
318+
}
235319
FloatingItemHeadingAndText("Device Key Invalidated",
236320
buildAnnotatedString {
237321
if (credentialInfo.keyInvalidated) {

0 commit comments

Comments
 (0)