Skip to content

Commit 6c42fef

Browse files
committed
Start Differentiating Credential Types
Added structure for additional credential types by moving existing code to a credential package and creating abstract classes for Credential and SecureAreaBoundCredential, with MdocCredential as the first implementation. All tests pass + tested manually by provisioning and presenting in wallet and appholder.
1 parent a39789f commit 6c42fef

File tree

27 files changed

+591
-435
lines changed

27 files changed

+591
-435
lines changed

appholder/src/main/java/com/android/identity/wallet/GetCredentialActivity.kt

+6-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import androidx.biometric.BiometricPrompt
1010
import androidx.fragment.app.FragmentActivity
1111
import com.android.identity.android.mdoc.util.CredmanUtil
1212
import com.android.identity.android.securearea.AndroidKeystoreKeyUnlockData
13-
import com.android.identity.document.Credential
13+
import com.android.identity.credential.MdocCredential
1414
import com.android.identity.document.DocumentRequest
1515
import com.android.identity.document.NameSpacedData
1616
import com.android.identity.crypto.Algorithm
@@ -36,7 +36,7 @@ import java.security.PublicKey
3636
class GetCredentialActivity : FragmentActivity() {
3737

3838
fun addDeviceNamespaces(documentGenerator : DocumentGenerator,
39-
authKey : Credential,
39+
authKey : MdocCredential,
4040
unlockData: KeyUnlockData?) {
4141
documentGenerator.setDeviceNamespacesSignature(
4242
NameSpacedData.Builder().build(),
@@ -46,7 +46,7 @@ class GetCredentialActivity : FragmentActivity() {
4646
Algorithm.ES256)
4747
}
4848

49-
fun doBiometricAuth(authKey : Credential,
49+
fun doBiometricAuth(authKey : MdocCredential,
5050
forceLskf : Boolean,
5151
onBiometricAuthCompleted: (unlockData: KeyUnlockData?) -> Unit) {
5252
var title = "To share your credential we need to check that it's you."
@@ -164,11 +164,9 @@ class GetCredentialActivity : FragmentActivity() {
164164

165165
val credential = document.findCredential(
166166
ProvisioningUtil.CREDENTIAL_DOMAIN,
167+
MdocCredential.CREDENTIAL_TYPE,
167168
Timestamp.now()
168-
)
169-
if (credential == null) {
170-
throw IllegalStateException("No credential")
171-
}
169+
) as MdocCredential? ?: throw IllegalStateException("No credential")
172170
val staticAuthData = StaticAuthDataParser(credential.issuerProvidedData).parse()
173171
val mergedIssuerNamespaces = MdocUtil.mergeIssuerNamesSpaces(
174172
documentRequest, nameSpacedData, staticAuthData
@@ -197,7 +195,7 @@ class GetCredentialActivity : FragmentActivity() {
197195
}
198196
}
199197

200-
fun completeResponse(authKey: Credential,
198+
fun completeResponse(authKey: MdocCredential,
201199
deviceResponseGenerator: DeviceResponseGenerator,
202200
documentGenerator: DocumentGenerator,
203201
requesterIdentity: PublicKey,

appholder/src/main/java/com/android/identity/wallet/support/AndroidKeystoreSecureAreaSupport.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import com.android.identity.android.securearea.UserAuthenticationType
2121
import com.android.identity.android.securearea.userAuthenticationTypeSet
2222
import com.android.identity.cbor.Cbor
2323
import com.android.identity.cbor.CborMap
24-
import com.android.identity.document.Credential
24+
import com.android.identity.credential.MdocCredential
2525
import com.android.identity.crypto.Algorithm
2626
import com.android.identity.securearea.CreateKeySettings
2727
import com.android.identity.crypto.EcCurve
@@ -51,7 +51,7 @@ class AndroidKeystoreSecureAreaSupport(
5151
)
5252

5353
override fun Fragment.unlockKey(
54-
authKey: Credential,
54+
authKey: MdocCredential,
5555
onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
5656
onUnlockFailure: (wasCancelled: Boolean) -> Unit
5757
) {

appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupport.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import android.content.Context
44
import androidx.compose.runtime.Composable
55
import androidx.fragment.app.Fragment
66
import com.android.identity.android.securearea.AndroidKeystoreSecureArea
7-
import com.android.identity.document.Credential
7+
import com.android.identity.credential.MdocCredential
88
import com.android.identity.document.Document
99
import com.android.identity.securearea.CreateKeySettings
1010
import com.android.identity.securearea.KeyUnlockData
@@ -35,7 +35,7 @@ interface SecureAreaSupport {
3535
* there is a provided way to navigate using the [findNavController] function.
3636
*/
3737
fun Fragment.unlockKey(
38-
authKey: Credential,
38+
authKey: MdocCredential,
3939
onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
4040
onUnlockFailure: (wasCancelled: Boolean) -> Unit
4141
)

appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportNull.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import androidx.compose.runtime.remember
1111
import androidx.compose.ui.Modifier
1212
import androidx.compose.ui.unit.dp
1313
import androidx.fragment.app.Fragment
14-
import com.android.identity.document.Credential
14+
import com.android.identity.credential.MdocCredential
1515
import com.android.identity.securearea.CreateKeySettings
1616
import com.android.identity.securearea.KeyUnlockData
1717
import com.android.identity.util.Timestamp
@@ -40,7 +40,7 @@ class SecureAreaSupportNull : SecureAreaSupport {
4040
}
4141

4242
override fun Fragment.unlockKey(
43-
authKey: Credential,
43+
authKey: MdocCredential,
4444
onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
4545
onUnlockFailure: (wasCancelled: Boolean) -> Unit
4646
) {

appholder/src/main/java/com/android/identity/wallet/support/SoftwareKeystoreSecureAreaSupport.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import androidx.lifecycle.repeatOnLifecycle
2020
import androidx.navigation.fragment.findNavController
2121
import co.nstant.`in`.cbor.CborBuilder
2222
import com.android.identity.cbor.Cbor
23-
import com.android.identity.document.Credential
23+
import com.android.identity.credential.MdocCredential
2424
import com.android.identity.crypto.Algorithm
2525
import com.android.identity.crypto.CertificateChain
2626
import com.android.identity.crypto.Crypto
@@ -56,7 +56,7 @@ class SoftwareKeystoreSecureAreaSupport : SecureAreaSupport {
5656
private val screenState = SoftwareKeystoreSecureAreaSupportState()
5757

5858
override fun Fragment.unlockKey(
59-
authKey: Credential,
59+
authKey: MdocCredential,
6060
onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
6161
onUnlockFailure: (wasCancelled: Boolean) -> Unit
6262
) {
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.android.identity.wallet.transfer
22

3-
import com.android.identity.document.Credential
3+
import com.android.identity.credential.MdocCredential
44

55
sealed class AddDocumentToResponseResult {
66

@@ -9,6 +9,6 @@ sealed class AddDocumentToResponseResult {
99
) : AddDocumentToResponseResult()
1010

1111
data class DocumentLocked(
12-
val authKey: Credential
12+
val authKey: MdocCredential
1313
) : AddDocumentToResponseResult()
1414
}

appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import android.view.View
99
import android.widget.ImageView
1010
import androidx.lifecycle.LiveData
1111
import androidx.lifecycle.MutableLiveData
12-
import com.android.identity.document.Credential
12+
import com.android.identity.credential.MdocCredential
1313
import com.android.identity.document.DocumentRequest
1414
import com.android.identity.document.NameSpacedData
1515
import com.android.identity.mdoc.mso.StaticAuthDataParser
@@ -163,7 +163,7 @@ class TransferManager private constructor(private val context: Context) {
163163
docType: String,
164164
issuerSignedEntriesToRequest: MutableMap<String, Collection<String>>,
165165
deviceResponseGenerator: DeviceResponseGenerator,
166-
authKey: Credential?,
166+
authKey: MdocCredential?,
167167
authKeyUnlockData: KeyUnlockData?,
168168
) = suspendCancellableCoroutine { continuation ->
169169
var result: AddDocumentToResponseResult
@@ -181,11 +181,15 @@ class TransferManager private constructor(private val context: Context) {
181181

182182
val request = DocumentRequest(dataElements)
183183

184-
val credentialToUse: Credential
184+
val credentialToUse: MdocCredential
185185
if (authKey != null) {
186186
credentialToUse = authKey
187187
} else {
188-
credentialToUse = document.findCredential(ProvisioningUtil.CREDENTIAL_DOMAIN, Timestamp.now())
188+
credentialToUse = document.findCredential(
189+
ProvisioningUtil.CREDENTIAL_DOMAIN,
190+
MdocCredential.CREDENTIAL_TYPE,
191+
Timestamp.now()
192+
) as MdocCredential?
189193
?: throw IllegalStateException("No credential available")
190194
}
191195

appholder/src/main/java/com/android/identity/wallet/util/ProvisioningUtil.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.android.identity.cbor.Tagged
1414
import com.android.identity.cbor.toDataItem
1515
import com.android.identity.cose.Cose
1616
import com.android.identity.cose.CoseNumberLabel
17+
import com.android.identity.credential.MdocCredential
1718
import com.android.identity.document.Document
1819
import com.android.identity.document.DocumentUtil
1920
import com.android.identity.document.NameSpacedData
@@ -117,7 +118,8 @@ class ProvisioningUtil private constructor(
117118
validUntil
118119
)
119120

120-
val pendingCredsCount = DocumentUtil.managedCredentialHelper(
121+
val pendingCredsCount = DocumentUtil.managedSecureAreaBoundCredentialHelper(
122+
MdocCredential.CREDENTIAL_TYPE,
121123
document,
122124
secureArea,
123125
settings,
@@ -132,7 +134,8 @@ class ProvisioningUtil private constructor(
132134
return
133135
}
134136

135-
for (pendingCred in document.pendingCredentials) {
137+
for (pendingCred in document.pendingCredentials.filter { it.credentialType == MdocCredential.CREDENTIAL_TYPE }) {
138+
pendingCred as MdocCredential
136139
val msoGenerator = MobileSecurityObjectGenerator(
137140
"SHA-256",
138141
docType,

appholder/src/main/java/com/android/identity/wallet/viewmodel/TransferDocumentViewModel.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import androidx.lifecycle.AndroidViewModel
88
import androidx.lifecycle.LiveData
99
import androidx.lifecycle.MutableLiveData
1010
import androidx.lifecycle.viewModelScope
11-
import com.android.identity.document.Credential
11+
import com.android.identity.credential.MdocCredential
1212
import com.android.identity.mdoc.request.DeviceRequestParser
1313
import com.android.identity.mdoc.response.DeviceResponseGenerator
1414
import com.android.identity.securearea.KeyUnlockData
@@ -102,7 +102,7 @@ class TransferDocumentViewModel(val app: Application) : AndroidViewModel(app) {
102102

103103
fun sendResponseForSelection(
104104
onResultReady: (result: AddDocumentToResponseResult) -> Unit,
105-
authKey: Credential? = null,
105+
authKey: MdocCredential? = null,
106106
authKeyUnlockData: KeyUnlockData? = null
107107
) {
108108
val elementsToSend = signedElements.collect()

identity-android/src/androidTest/java/com/android/identity/android/document/AndroidKeystoreSecureAreaDocumentStoreTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.android.identity.android.securearea.AndroidKeystoreCreateKeySettings
2121
import com.android.identity.android.securearea.AndroidKeystoreSecureArea
2222
import com.android.identity.android.securearea.UserAuthenticationType
2323
import com.android.identity.android.storage.AndroidStorageEngine
24+
import com.android.identity.credential.MdocCredential
2425
import com.android.identity.document.Document
2526
import com.android.identity.document.DocumentStore
2627
import com.android.identity.crypto.javaX509Certificate
@@ -65,7 +66,8 @@ class AndroidKeystoreSecureAreaDocumentStoreTest {
6566

6667
// Create pending credential and check its attestation
6768
val authKeyChallenge = byteArrayOf(20, 21, 22)
68-
val pendingCredential = document.createCredential(
69+
val pendingCredential = document.createSecureAreaBoundCredential(
70+
MdocCredential.CREDENTIAL_TYPE,
6971
CREDENTIAL_DOMAIN,
7072
secureArea,
7173
AndroidKeystoreCreateKeySettings.Builder(authKeyChallenge)

identity-android/src/androidTest/java/com/android/identity/android/mdoc/deviceretrieval/DeviceRetrievalHelperTest.kt

+11-9
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ import com.android.identity.cose.Cose
3232
import com.android.identity.cose.Cose.coseSign1Sign
3333
import com.android.identity.cose.CoseLabel
3434
import com.android.identity.cose.CoseNumberLabel
35-
import com.android.identity.document.Credential
35+
import com.android.identity.credential.MdocCredential
36+
import com.android.identity.credential.SecureAreaBoundCredential
3637
import com.android.identity.document.Document
3738
import com.android.identity.document.DocumentStore
3839
import com.android.identity.document.NameSpacedData
@@ -96,7 +97,7 @@ class DeviceRetrievalHelperTest {
9697
private lateinit var mSecureArea: SecureArea
9798
private lateinit var mSecureAreaRepository: SecureAreaRepository
9899
private lateinit var mDocument: Document
99-
private lateinit var mCredential: Credential
100+
private lateinit var mMdocCredential: SecureAreaBoundCredential
100101
private lateinit var mTimeSigned: Timestamp
101102
private lateinit var mTimeValidityBegin: Timestamp
102103
private lateinit var mTimeValidityEnd: Timestamp
@@ -135,21 +136,22 @@ class DeviceRetrievalHelperTest {
135136
mTimeSigned = ofEpochMilli(nowMillis)
136137
mTimeValidityBegin = ofEpochMilli(nowMillis + 3600 * 1000)
137138
mTimeValidityEnd = ofEpochMilli(nowMillis + 10 * 86400 * 1000)
138-
mCredential = mDocument.createCredential(
139+
mMdocCredential = mDocument.createSecureAreaBoundCredential(
140+
MdocCredential.CREDENTIAL_TYPE,
139141
CREDENTIAL_DOMAIN,
140142
mSecureArea,
141143
SoftwareCreateKeySettings.Builder(ByteArray(0))
142144
.setKeyPurposes(setOf(KeyPurpose.SIGN, KeyPurpose.AGREE_KEY))
143145
.build(),
144146
null
145147
)
146-
Assert.assertFalse(mCredential.isCertified)
148+
Assert.assertFalse(mMdocCredential.isCertified)
147149

148150
// Generate an MSO and issuer-signed data for this credential.
149151
val msoGenerator = MobileSecurityObjectGenerator(
150152
"SHA-256",
151153
MDL_DOCTYPE,
152-
mCredential.attestation.certificates[0].publicKey
154+
mMdocCredential.attestation.certificates[0].publicKey
153155
)
154156
msoGenerator.setValidityInfo(mTimeSigned, mTimeValidityBegin, mTimeValidityEnd, null)
155157
val issuerNameSpaces = generateIssuerNameSpaces(
@@ -213,7 +215,7 @@ class DeviceRetrievalHelperTest {
213215
).generate()
214216

215217
// Now that we have issuer-provided authentication data we certify the credential.
216-
mCredential.certify(
218+
mMdocCredential.certify(
217219
issuerProvidedAuthenticationData,
218220
mTimeValidityBegin,
219221
mTimeValidityEnd
@@ -377,7 +379,7 @@ class DeviceRetrievalHelperTest {
377379
val generator = DeviceResponseGenerator(
378380
Constants.DEVICE_RESPONSE_STATUS_OK
379381
)
380-
val staticAuthData = StaticAuthDataParser(mCredential.issuerProvidedData)
382+
val staticAuthData = StaticAuthDataParser(mMdocCredential.issuerProvidedData)
381383
.parse()
382384
val deviceSignedData = NameSpacedData.Builder().build()
383385
val mergedIssuerNamespaces: Map<String, List<ByteArray>> =
@@ -395,8 +397,8 @@ class DeviceRetrievalHelperTest {
395397
.setIssuerNamespaces(mergedIssuerNamespaces)
396398
.setDeviceNamespacesSignature(
397399
deviceSignedData,
398-
mCredential.secureArea,
399-
mCredential.alias,
400+
mMdocCredential.secureArea,
401+
mMdocCredential.alias,
400402
null,
401403
Algorithm.ES256
402404
)

identity-mdoc/src/test/java/com/android/identity/mdoc/response/DeviceResponseGeneratorTest.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import com.android.identity.cbor.toDataItem
2424
import com.android.identity.cose.Cose
2525
import com.android.identity.cose.CoseLabel
2626
import com.android.identity.cose.CoseNumberLabel
27-
import com.android.identity.document.Credential
27+
import com.android.identity.credential.MdocCredential
28+
import com.android.identity.credential.SecureAreaBoundCredential
2829
import com.android.identity.document.Document
2930
import com.android.identity.document.DocumentRequest
3031
import com.android.identity.document.DocumentRequest.DataElement
@@ -62,7 +63,7 @@ class DeviceResponseGeneratorTest {
6263
private lateinit var secureArea: SecureArea
6364
private lateinit var secureAreaRepository: SecureAreaRepository
6465

65-
private lateinit var authKey: Credential
66+
private lateinit var authKey: SecureAreaBoundCredential
6667
private lateinit var document: Document
6768
private lateinit var timeSigned: Timestamp
6869
private lateinit var timeValidityBegin: Timestamp
@@ -83,6 +84,7 @@ class DeviceResponseGeneratorTest {
8384

8485
// This isn't really used, we only use a single domain.
8586
private val AUTH_KEY_DOMAIN = "domain"
87+
private val MDOC_CREDENTIAL_IDENTIFIER = "MdocCredential"
8688

8789
@Throws(Exception::class)
8890
private fun provisionDocument() {
@@ -117,7 +119,8 @@ class DeviceResponseGeneratorTest {
117119
timeSigned = Timestamp.ofEpochMilli(nowMillis)
118120
timeValidityBegin = Timestamp.ofEpochMilli(nowMillis + 3600 * 1000)
119121
timeValidityEnd = Timestamp.ofEpochMilli(nowMillis + 10 * 86400 * 1000)
120-
authKey = document.createCredential(
122+
authKey = document.createSecureAreaBoundCredential(
123+
MdocCredential.CREDENTIAL_TYPE,
121124
AUTH_KEY_DOMAIN,
122125
secureArea,
123126
SoftwareCreateKeySettings.Builder(ByteArray(0))

0 commit comments

Comments
 (0)