Skip to content

Commit

Permalink
Remove CredentialKey.
Browse files Browse the repository at this point in the history
CredentialKey is a construct tied to one specific issuance protocol (or family
of protocols) and it's not inconceivable that other issuance protocols may want
to use different constructs. In fact, in our self-signed credentials in appholder,
CredentialKey isn't used at all.

Applications wishing to continue using a CredentialKey can simply just create it
manually using a SecureArea implementation and use Credential.applicationData to
store the alias.

This removal affects the migration code in KeystoreIdentityCredentialStore and
replaces the migrateToCredentialStore() method with getNameSpacedData() and
getCredentialKeyAlias() methods. This change actually gives the application
more flexibility insofar that it now easily maintain both the old credential
and the new credential since the old one isn't deleted at migration time.

Also change how to create keys in AndroidKeystoreSecureArea for an existing
alias.

Test: All unit tests pass.
Signed-off-by: David Zeuthen <[email protected]>
  • Loading branch information
davidz25 committed Dec 9, 2023
1 parent a5d6e47 commit b040314
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 867 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.android.identity.wallet.document

import com.android.identity.wallet.support.CurrentSecureArea

data class DocumentInformation(
val userVisibleName: String,
val docName: String,
Expand All @@ -11,7 +9,6 @@ data class DocumentInformation(
val documentColor: Int,
val maxUsagesPerKey: Int,
val lastTimeUsed: String,
val currentSecureArea: CurrentSecureArea,
val authKeys: List<KeyData>
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,6 @@ private fun DocumentInfoScreenContent(
modifier = Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
LabeledValue(
label = stringResource(id = R.string.label_credential_shape),
value = "mdoc"
)
LabeledValue(
label = stringResource(id = R.string.label_document_name),
value = screenState.documentName
Expand All @@ -149,10 +145,6 @@ private fun DocumentInfoScreenContent(
label = stringResource(id = R.string.label_issuer),
value = if (screenState.isSelfSigned) "Self-Signed on Device" else "N/A"
)
LabeledValue(
label = stringResource(id = R.string.txt_keystore_implementation),
value = screenState.currentSecureArea.displayName
)
}
}
val pagerState = rememberPagerState(pageCount = { screenState.authKeys.size })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ data class DocumentInfoScreenState(
val provisioningDate: String = "",
val lastTimeUsedDate: String = "",
val isSelfSigned: Boolean = false,
val currentSecureArea: CurrentSecureArea = ProvisioningUtil.defaultSecureArea.toSecureAreaState(),
val authKeys: List<KeyInformation> = emptyList(),
val isDeletingPromptShown: Boolean = false,
val isDeleted: Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ class DocumentInfoViewModel(
documentType = documentInformation.docType,
documentColor = documentInformation.documentColor.toCardArt(),
provisioningDate = documentInformation.dateProvisioned,
currentSecureArea = documentInformation.currentSecureArea,
isSelfSigned = documentInformation.selfSigned,
lastTimeUsedDate = documentInformation.lastTimeUsed,
authKeys = documentInformation.authKeys.asScreenStateKeys()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,8 @@ class ProvisioningUtil private constructor(
nameSpacedData: NameSpacedData,
provisionInfo: ProvisionInfo,
) {

// CredentialKey is never used for self-signed credential (there's no Issuer to
// communicate with) so just use Android Keystore and a P-256 key w/o authentication.
//
val credentialKeySecureArea: SecureArea = secureAreaRepository.getImplementation(
AndroidKeystoreSecureArea.SECURE_AREA_IDENTIFIER)!!
val credentialKeySettings = SecureArea.CreateKeySettings("".toByteArray())
val credential = credentialStore.createCredential(
provisionInfo.credentialName(),
credentialKeySecureArea,
credentialKeySettings
)
provisionInfo.credentialName())
credential.applicationData.setNameSpacedData("credentialData", nameSpacedData)

val authKeySecureArea: SecureArea = provisionInfo.currentSecureArea.secureArea
Expand Down Expand Up @@ -310,7 +300,6 @@ class ProvisioningUtil private constructor(
documentColor = it.applicationData.getNumber(CARD_ART).toInt(),
selfSigned = it.applicationData.getBoolean(IS_SELF_SIGNED),
maxUsagesPerKey = it.applicationData.getNumber(MAX_USAGES_PER_KEY).toInt(),
currentSecureArea = it.credentialSecureArea.toSecureAreaState(),
lastTimeUsed = lastTimeUsed,
authKeys = authKeys
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,8 @@ public void testBasic() throws IOException {
byte[] credentialKeyAttestationChallenge = new byte[] {10, 11, 12};

Credential credential = credentialStore.createCredential(
"testCredential",
mSecureArea,
new AndroidKeystoreSecureArea.CreateKeySettings.Builder(credentialKeyAttestationChallenge).build());
"testCredential");
Assert.assertEquals("testCredential", credential.getName());
List<X509Certificate> certChain = credential.getAttestation();

// Check the attestation extension
AndroidAttestationExtensionParser parser = new AndroidAttestationExtensionParser(certChain.get(0));
Assert.assertArrayEquals(credentialKeyAttestationChallenge, parser.getAttestationChallenge());
AndroidAttestationExtensionParser.SecurityLevel securityLevel = parser.getKeymasterSecurityLevel();
Assert.assertEquals(AndroidAttestationExtensionParser.SecurityLevel.TRUSTED_ENVIRONMENT, securityLevel);

// Create pending authentication key and check its attestation
byte[] authKeyChallenge = new byte[] {20, 21, 22};
Expand All @@ -95,7 +86,8 @@ public void testBasic() throws IOException {
| AndroidKeystoreSecureArea.USER_AUTHENTICATION_TYPE_BIOMETRIC)
.build(),
null);
parser = new AndroidAttestationExtensionParser(pendingAuthenticationKey.getAttestation().get(0));
AndroidAttestationExtensionParser parser =
new AndroidAttestationExtensionParser(pendingAuthenticationKey.getAttestation().get(0));
Assert.assertArrayEquals(authKeyChallenge,
parser.getAttestationChallenge());
Assert.assertEquals(AndroidAttestationExtensionParser.SecurityLevel.TRUSTED_ENVIRONMENT,
Expand All @@ -105,23 +97,13 @@ public void testBasic() throws IOException {
credential = credentialStore.lookupCredential("testCredential");
Assert.assertNotNull(credential);
Assert.assertEquals("testCredential", credential.getName());
List<X509Certificate> certChain2 = credential.getAttestation();
Assert.assertEquals(certChain.size(), certChain2.size());
for (int n = 0; n < certChain.size(); n++) {
Assert.assertEquals(certChain.get(n), certChain2.get(n));
}

Assert.assertNull(credentialStore.lookupCredential("nonExistingCredential"));

// Check creating a credential with an existing name overwrites the existing one
credential = credentialStore.createCredential(
"testCredential",
mSecureArea,
new AndroidKeystoreSecureArea.CreateKeySettings.Builder(credentialKeyAttestationChallenge).build());
"testCredential");
Assert.assertEquals("testCredential", credential.getName());
// At least the leaf certificate should be different
List<X509Certificate> certChain3 = credential.getAttestation();
Assert.assertNotEquals(certChain3.get(0), certChain2.get(0));

credential = credentialStore.lookupCredential("testCredential");
Assert.assertNotNull(credential);
Expand Down
Loading

0 comments on commit b040314

Please sign in to comment.