,
- documentData: SelfSignedDocumentData
- ): DataItem {
- val mapBuilder = CborBuilder().addMap()
- for (field in fields) {
- when (field.fieldType) {
- is DocumentAttributeType.Date, DocumentAttributeType.DateTime -> {
- val date = UnicodeString(field.getValueString())
- date.setTag(1004)
- mapBuilder.put(
- UnicodeString(field.name),
- date
- )
- }
-
- is DocumentAttributeType.Boolean -> {
- mapBuilder.put(
- field.name,
- field.getValueBoolean()
- )
- }
-
- is DocumentAttributeType.Picture -> {
- val bitmap = field.getValueBitmap()
- val baos = ByteArrayOutputStream()
- bitmap.compress(Bitmap.CompressFormat.JPEG, 50, baos)
- val bytes = baos.toByteArray()
- mapBuilder.put(field.name, bytes)
- }
-
- is DocumentAttributeType.IntegerOptions,
- is DocumentAttributeType.Number -> {
- if (field.value != "") {
- mapBuilder.put(
- field.name,
- field.getValueLong()
- )
- }
- }
-
- is DocumentAttributeType.ComplexType -> {
- val dataItem = when (field.isArray) {
- true -> {
- createArrayDataItem(field, documentData)
- }
-
- false -> {
- createMapDataItem(field, documentData)
- }
- }
- mapBuilder.put(UnicodeString(field.name), dataItem)
- }
-
- else -> {
- mapBuilder.put(field.name, field.value as String)
- }
- }
- }
-
- return mapBuilder.end().build()[0]
- }
-
- fun refreshCredentials(documentName: String) {
- val documentInformation = requireNotNull(getDocumentInformation(documentName))
- val document = requireNotNull(getDocumentByName(documentName))
- ProvisioningUtil.getInstance(context).refreshCredentials(document, documentInformation.docType)
- }
-}
diff --git a/appholder/src/main/java/com/android/identity/wallet/document/KeysAndCertificates.kt b/appholder/src/main/java/com/android/identity/wallet/document/KeysAndCertificates.kt
deleted file mode 100644
index a06a881e5..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/document/KeysAndCertificates.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.android.identity.wallet.document
-
-import android.content.Context
-import com.android.identity.wallet.R
-import org.bouncycastle.jce.provider.BouncyCastleProvider
-import java.io.InputStream
-import java.nio.charset.StandardCharsets
-import java.security.KeyFactory
-import java.security.KeyPair
-import java.security.PrivateKey
-import java.security.PublicKey
-import java.security.cert.CertificateFactory
-import java.security.cert.X509Certificate
-import java.security.spec.PKCS8EncodedKeySpec
-import java.security.spec.X509EncodedKeySpec
-import java.util.Base64
-object KeysAndCertificates {
-
- private fun getCertificate(context: Context, resourceId: Int): X509Certificate {
- val certInputStream = context.resources.openRawResource(resourceId)
- val factory: CertificateFactory = CertificateFactory.getInstance("X509")
- return factory.generateCertificate(certInputStream) as X509Certificate
- }
-
- private fun getKeyBytes(keyInputStream: InputStream): ByteArray {
- val keyBytes = keyInputStream.readBytes()
- val publicKeyPEM = String(keyBytes, StandardCharsets.US_ASCII)
- .replace("-----BEGIN PUBLIC KEY-----", "")
- .replace("-----BEGIN PRIVATE KEY-----", "")
- .replace("\r", "")
- .replace("\n", "")
- .replace("-----END PUBLIC KEY-----", "")
- .replace("-----END PRIVATE KEY-----", "")
-
- return Base64.getDecoder().decode(publicKeyPEM)
- }
-
- private fun getPrivateKey(context: Context, resourceId: Int): PrivateKey {
- val keyBytes: ByteArray = getKeyBytes(context.resources.openRawResource(resourceId))
- val spec = PKCS8EncodedKeySpec(keyBytes)
- val kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME)
- return kf.generatePrivate(spec)
- }
-
- private fun getPublicKey(context: Context, resourceId: Int): PublicKey {
- val keyBytes: ByteArray = getKeyBytes(context.resources.openRawResource(resourceId))
- val spec = X509EncodedKeySpec(keyBytes)
- val kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME)
- return kf.generatePublic(spec)
- }
-
- fun getMdlDsKeyPair(context: Context) =
- KeyPair(
- getPublicKey(context, R.raw.google_mdl_ds_cert_iaca_2_pubkey),
- getPrivateKey(context, R.raw.google_mdl_ds_cert_iaca_2_privkey)
- )
-
- fun getMekbDsKeyPair(context: Context) =
- KeyPair(
- getPublicKey(context, R.raw.google_mekb_ds_pubkey),
- getPrivateKey(context, R.raw.google_mekb_ds_privkey)
- )
-
- fun getMicovDsKeyPair(context: Context) =
- KeyPair(
- getPublicKey(context, R.raw.google_micov_ds_pubkey),
- getPrivateKey(context, R.raw.google_micov_ds_privkey)
- )
-
- fun getMdlDsCertificate(context: Context) = getCertificate(context, R.raw.google_mdl_ds_cert_iaca_2)
-
- fun getMekbDsCertificate(context: Context) = getCertificate(context, R.raw.google_mekb_ds_cert)
-
- fun getMicovDsCertificate(context: Context) = getCertificate(context, R.raw.google_micov_ds_cert)
-
- fun getTrustedReaderCertificates(context: Context) =
- listOf(
- getCertificate(context, R.raw.bdr_iaca_cert),
- getCertificate(context, R.raw.bdr_reader_ca_cert),
- getCertificate(context, R.raw.credenceid_mdl_reader_cert),
- getCertificate(context, R.raw.fast_reader_auth_cer),
- getCertificate(context, R.raw.google_reader_ca),
- getCertificate(context, R.raw.hid_test_reader_ca_mdl_cert),
- getCertificate(context, R.raw.hidtestiacamdl_cert),
- getCertificate(context, R.raw.iaca_zetes),
- getCertificate(context, R.raw.idemia_brisbane_interop_readerauthca),
- getCertificate(context, R.raw.louisiana_department_of_motor_vehicles_cert),
- getCertificate(context, R.raw.nist_reader_ca_cer),
- getCertificate(context, R.raw.reader_ca_nec_reader_ca_cer),
- getCertificate(context, R.raw.samsung_iaca_test_cert),
- getCertificate(context, R.raw.scytales_root_ca),
- getCertificate(context, R.raw.spruce_iaca_cert),
- getCertificate(context, R.raw.ul_cert_ca_01),
- getCertificate(context, R.raw.ul_cert_ca_01_cer),
- getCertificate(context, R.raw.ul_cert_ca_02),
- getCertificate(context, R.raw.ul_cert_ca_03_cer),
- getCertificate(context, R.raw.ul_cert_ca_02_cer),
- getCertificate(context, R.raw.utms_reader_ca),
- getCertificate(context, R.raw.utms_reader_ca_cer),
- getCertificate(context, R.raw.zetes_reader_ca),
- getCertificate(context, R.raw.zetes_reader_ca_cer),
- getCertificate(context, R.raw.owf_identity_credential_reader_cert),
- )
-
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/DocumentDataReader.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/DocumentDataReader.kt
deleted file mode 100644
index 6b3f28727..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/DocumentDataReader.kt
+++ /dev/null
@@ -1,261 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import android.graphics.BitmapFactory
-import co.nstant.`in`.cbor.CborDecoder
-import co.nstant.`in`.cbor.CborException
-import co.nstant.`in`.cbor.model.AbstractFloat
-import co.nstant.`in`.cbor.model.Array
-import co.nstant.`in`.cbor.model.ByteString
-import co.nstant.`in`.cbor.model.DataItem
-import co.nstant.`in`.cbor.model.DoublePrecisionFloat
-import co.nstant.`in`.cbor.model.MajorType
-import co.nstant.`in`.cbor.model.Map
-import co.nstant.`in`.cbor.model.NegativeInteger
-import co.nstant.`in`.cbor.model.SimpleValue
-import co.nstant.`in`.cbor.model.SimpleValueType
-import co.nstant.`in`.cbor.model.UnicodeString
-import co.nstant.`in`.cbor.model.UnsignedInteger
-import com.android.identity.document.NameSpacedData
-import com.android.identity.wallet.util.DocumentData.EU_PID_DOCTYPE
-import com.android.identity.wallet.util.DocumentData.EU_PID_NAMESPACE
-import com.android.identity.wallet.util.DocumentData.MDL_DOCTYPE
-import com.android.identity.wallet.util.DocumentData.MDL_NAMESPACE
-import com.android.identity.wallet.util.DocumentData.MICOV_ATT_NAMESPACE
-import com.android.identity.wallet.util.DocumentData.MICOV_DOCTYPE
-import java.io.ByteArrayInputStream
-import java.math.BigInteger
-import java.text.DecimalFormat
-import java.text.DecimalFormatSymbols
-import java.util.Locale
-
-class DocumentDataReader(private val docType: String) {
-
- fun read(nameSpacedData: NameSpacedData): DocumentElements {
- val builder = StringBuilder()
- var portraitBytes: ByteArray? = null
- var signatureBytes: ByteArray? = null
- var fingerprintBytes: ByteArray? = null
-
- nameSpacedData.nameSpaceNames.forEach { namespace ->
- val dataElementNames = nameSpacedData.getDataElementNames(namespace)
- builder.append("
")
- builder.append("Namespace: $namespace
")
- builder.append("")
- dataElementNames.forEach { entryName ->
- val byteArray: ByteArray = nameSpacedData.getDataElement(namespace, entryName)
- byteArray.let { value ->
- val valueStr: String
- if (isPortraitElement(docType, namespace, entryName)) {
- valueStr = String.format("(%d bytes, shown above)", value.size)
- portraitBytes = nameSpacedData.getDataElementByteString(namespace, entryName)
- } else if (docType == MICOV_DOCTYPE && namespace == MICOV_ATT_NAMESPACE && entryName == "fac") {
- valueStr = String.format("(%d bytes, shown above)", value.size)
- portraitBytes = nameSpacedData.getDataElement(namespace, entryName)
- } else if (docType == MDL_DOCTYPE && namespace == MDL_NAMESPACE && entryName == "extra") {
- valueStr = String.format("%d bytes extra data", value.size)
- } else if (docType == MDL_DOCTYPE && namespace == MDL_NAMESPACE && entryName == "signature_usual_mark") {
- valueStr = String.format("(%d bytes, shown below)", value.size)
- signatureBytes = nameSpacedData.getDataElementByteString(namespace, entryName)
- } else if (docType == EU_PID_DOCTYPE && namespace == EU_PID_NAMESPACE && entryName == "biometric_template_finger") {
- valueStr = String.format("(%d bytes, not shown)", value.size)
- fingerprintBytes = nameSpacedData.getDataElementByteString(namespace, entryName)
- } else {
- valueStr = cborPrettyPrint(value)
- }
- builder.append("$entryName -> $valueStr
")
- }
- builder.append("
")
- }
- }
- val portrait = portraitBytes?.let { bytes ->
- BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
- }
- val signature = signatureBytes?.let { bytes ->
- BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
- }
- val fingerprint = fingerprintBytes?.let { bytes ->
- BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
- }
-
- return DocumentElements(
- text = builder.toString(),
- portrait = portrait,
- signature = signature,
- fingerprint = fingerprint
- )
- }
-
- private fun isPortraitElement(
- docType: String,
- namespace: String?,
- entryName: String?
- ): Boolean {
- val hasPortrait = docType == MDL_DOCTYPE || docType == EU_PID_DOCTYPE
- val namespaceContainsPortrait = namespace == MDL_NAMESPACE || namespace == EU_PID_NAMESPACE
- return hasPortrait && namespaceContainsPortrait && entryName == "portrait"
- }
-
- private fun cborPrettyPrint(encodedBytes: ByteArray): String {
- val newLine = "
"
- val sb = java.lang.StringBuilder()
- val bais = ByteArrayInputStream(encodedBytes)
- val dataItems = try {
- CborDecoder(bais).decode()
- } catch (e: CborException) {
- throw java.lang.IllegalStateException(e)
- }
- for ((count, dataItem) in dataItems.withIndex()) {
- if (count > 0) {
- sb.append(",$newLine")
- }
- cborPrettyPrintDataItem(sb, 0, dataItem)
- }
- return sb.toString()
- }
-
- private fun cborPrettyPrintDataItem(
- sb: java.lang.StringBuilder, indent: Int,
- dataItem: DataItem
- ) {
- val space = " "
- val newLine = "
"
- val indentBuilder = java.lang.StringBuilder()
- for (n in 0 until indent) {
- indentBuilder.append(space)
- }
- val indentString = indentBuilder.toString()
- if (dataItem.hasTag()) {
- sb.append(String.format("tag %d ", dataItem.tag.value))
- }
- when (dataItem.majorType) {
- MajorType.INVALID -> // TODO: throw
- sb.append("**invalid**")
-
- MajorType.UNSIGNED_INTEGER -> {
- // Major type 0: an unsigned integer.
- val value: BigInteger = (dataItem as UnsignedInteger).value
- sb.append(value)
- }
-
- MajorType.NEGATIVE_INTEGER -> {
- // Major type 1: a negative integer.
- val value: BigInteger = (dataItem as NegativeInteger).value
- sb.append(value)
- }
-
- MajorType.BYTE_STRING -> {
- // Major type 2: a byte string.
- val value = (dataItem as ByteString).bytes
- sb.append("[")
- for ((count, b) in value.withIndex()) {
- if (count > 0) {
- sb.append(", ")
- }
- sb.append(String.format("0x%02x", b))
- }
- sb.append("]")
- }
-
- MajorType.UNICODE_STRING -> {
- // Major type 3: string of Unicode characters that is encoded as UTF-8 [RFC3629].
- val value = (dataItem as UnicodeString).string
- // TODO: escape ' in |value|
- sb.append("'$value'")
- }
-
- MajorType.ARRAY -> {
-
- // Major type 4: an array of data items.
- val items = (dataItem as Array).dataItems
- if (items.size == 0) {
- sb.append("[]")
- } else if (cborAreAllDataItemsNonCompound(items)) {
- // The case where everything fits on one line.
- sb.append("[")
- for ((count, item) in items.withIndex()) {
- cborPrettyPrintDataItem(sb, indent, item)
- if (count + 1 < items.size) {
- sb.append(", ")
- }
- }
- sb.append("]")
- } else {
- sb.append("[$newLine$indentString")
- for ((count, item) in items.withIndex()) {
- sb.append("$space$space")
- cborPrettyPrintDataItem(sb, indent + 2, item)
- if (count + 1 < items.size) {
- sb.append(",")
- }
- sb.append("$newLine $indentString")
- }
- sb.append("]")
- }
- }
-
- MajorType.MAP -> {
- // Major type 5: a map of pairs of data items.
- val keys = (dataItem as Map).keys
- if (keys.isEmpty()) {
- sb.append("{}")
- } else {
- sb.append("{$newLine$indentString")
- for ((count, key) in keys.withIndex()) {
- sb.append("$space$space")
- val value = dataItem[key]
- cborPrettyPrintDataItem(sb, indent + 2, key)
- sb.append(" : ")
- cborPrettyPrintDataItem(sb, indent + 2, value)
- if (count + 1 < keys.size) {
- sb.append(",")
- }
- sb.append("$newLine $indentString")
- }
- sb.append("}")
- }
- }
-
- MajorType.TAG -> throw java.lang.IllegalStateException("Semantic tag data item not expected")
- MajorType.SPECIAL -> // Major type 7: floating point numbers and simple data types that need no
- // content, as well as the "break" stop code.
- if (dataItem is SimpleValue) {
- when (dataItem.simpleValueType) {
- SimpleValueType.FALSE -> sb.append("false")
- SimpleValueType.TRUE -> sb.append("true")
- SimpleValueType.NULL -> sb.append("null")
- SimpleValueType.UNDEFINED -> sb.append("undefined")
- SimpleValueType.RESERVED -> sb.append("reserved")
- SimpleValueType.UNALLOCATED -> sb.append("unallocated")
- }
- } else if (dataItem is DoublePrecisionFloat) {
- val df = DecimalFormat(
- "0",
- DecimalFormatSymbols.getInstance(Locale.ENGLISH)
- )
- df.maximumFractionDigits = 340
- sb.append(df.format(dataItem.value))
- } else if (dataItem is AbstractFloat) {
- val df = DecimalFormat(
- "0",
- DecimalFormatSymbols.getInstance(Locale.ENGLISH)
- )
- df.maximumFractionDigits = 340
- sb.append(df.format(dataItem.value))
- } else {
- sb.append("break")
- }
- }
- }
-
- // Returns true iff all elements in |items| are not compound (e.g. an array or a map).
- private fun cborAreAllDataItemsNonCompound(items: List): Boolean {
- for (item in items) {
- when (item.majorType) {
- MajorType.ARRAY, MajorType.MAP -> return false
- else -> {
- }
- }
- }
- return true
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/DocumentElements.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/DocumentElements.kt
deleted file mode 100644
index 322eff249..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/DocumentElements.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import android.graphics.Bitmap
-
-data class DocumentElements(
- val text: String = "",
- val portrait: Bitmap? = null,
- val signature: Bitmap? = null,
- val fingerprint: Bitmap? = null,
- val requestUserAuthorization: Boolean = false,
- val passphrase: String = ""
-)
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/DrivingLicense.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/DrivingLicense.kt
deleted file mode 100644
index d0f477a85..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/DrivingLicense.kt
+++ /dev/null
@@ -1,225 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import com.android.identity.documenttype.DocumentAttributeType
-import com.android.identity.documenttype.StringOption
-
-object DrivingLicense {
- const val MDL_NAMESPACE = "org.iso.18013.5.1"
- const val AAMVA_NAMESPACE = "org.iso.18013.5.1.aamva"
- fun getMdocComplexTypes() = MdocComplexTypes.Builder("org.iso.18013.5.1.mDL")
- .addDefinition(
- MDL_NAMESPACE,
- hashSetOf("driving_privileges"),
- true,
- "vehicle_category_code",
- "Vehicle Category Code",
- DocumentAttributeType.StringOptions(
- listOf(
- StringOption(null, "(not set)"),
- StringOption("A", "Motorcycles (A)"),
- StringOption("AEU", "Motorcycles (AEU)"),
- StringOption("B", "Light vehicles (B"),
- StringOption("C", "Goods vehicles (C)"),
- StringOption("D", "Passenger vehicles (D)"),
- StringOption("BE", "Light vehicles with trailers (BE)"),
- StringOption("CE", "Goods vehicles with trailers (CE)"),
- StringOption("DE", "Passenger vehicles with trailers (DE)"),
- StringOption("AM", "Mopeds (AM)"),
- StringOption("A1", "Light motorcycles (A1)"),
- StringOption("A1EU", "Light motorcycles (A1EU)"),
- StringOption("A2", "Medium motorcycles (A2)"),
- StringOption("B1", "Light vehicles (B1)"),
- StringOption("B1EU", "Light vehicles (B1EU)"),
- StringOption("C1", "Medium sized goods vehicles (C1)"),
- StringOption("D1", "Medium sized passenger vehicles (e.g. minibuses) (D1)"),
- )
- )
- )
- .addDefinition(
- MDL_NAMESPACE,
- hashSetOf("driving_privileges"),
- true,
- "issue_date",
- "Date of Issue",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MDL_NAMESPACE,
- hashSetOf("driving_privileges"),
- true,
- "expiry_date",
- "Date of Expiry",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MDL_NAMESPACE,
- hashSetOf("driving_privileges"),
- true,
- "codes",
- "Codes of Driving Privileges",
- DocumentAttributeType.ComplexType,
- )
- // details of DrivingPrivilege.codes
- .addDefinition(
- MDL_NAMESPACE,
- hashSetOf("codes"),
- true,
- "code",
- "Code",
- DocumentAttributeType.StringOptions(
- listOf(
- StringOption(null, "(not set)"),
- StringOption(
- "01",
- "Licence holder requires eye sight correction and/or protection"
- ),
- StringOption(
- "03",
- "Licence holder requires prosthetic device for the limbs"
- ),
- StringOption(
- "78",
- "Licence holder restricted to vehicles with automatic transmission"
- ),
- StringOption(
- "S01",
- "The vehicle's maximum authorized mass (kg) shall be"
- ),
- StringOption(
- "S02",
- "The vehicle's authorized passenger seats, excluding the driver's seat, shall be"
- ),
- StringOption(
- "S03",
- "The vehicle's cylinder capacity (cm3) shall be"
- ),
- StringOption(
- "S04",
- "The vehicle's power (kW) shall be"
- ),
- StringOption(
- "S05",
- "Licence holder restricted to vehicles adapted for physically disabled"
- )
- )
- )
- )
- .addDefinition(
- MDL_NAMESPACE,
- hashSetOf("codes"),
- true,
- "sign",
- "Sign",
- DocumentAttributeType.StringOptions(
- listOf(
- StringOption(null, "(not set)"),
- StringOption("=", "Equals (=)"),
- StringOption(">", "Greater than (>)"),
- StringOption("<", "Less than (<)"),
- StringOption(">=", "Greater than or equal to (≥)"),
- StringOption("<=", "Less than or equal to (≤)")
- )
- )
- )
- .addDefinition(
- MDL_NAMESPACE,
- hashSetOf("codes"),
- true,
- "value",
- "Value",
- DocumentAttributeType.String
- ).
- // details of domestic_driving_privileges
- addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_driving_privileges"),
- true,
- "domestic_vehicle_class",
- "Domestic Vehicle Class",
- DocumentAttributeType.ComplexType,
- )
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_driving_privileges"),
- true,
- "domestic_vehicle_restrictions",
- "Domestic Vehicle Restrictions",
- DocumentAttributeType.ComplexType
- )
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_driving_privileges"),
- true,
- "domestic_vehicle_endorsements",
- "Domestic Vehicle Endorsements",
- DocumentAttributeType.ComplexType
- )
- // details of DomesticDrivingPrivilege.domestic_vehicle_class
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_class"),
- false,
- "domestic_vehicle_class_code",
- "Domestic Vehicle Class Code",
- DocumentAttributeType.String
- )
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_class"),
- false,
- "domestic_vehicle_class_description",
- "Domestic Vehicle Class Description",
- DocumentAttributeType.String
- )
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_class"),
- false,
- "issue_date",
- "Date of Issue",
- DocumentAttributeType.Date
- )
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_class"),
- false,
- "expiry_date",
- "Date of Expiry",
- DocumentAttributeType.Date
- )
- // details of DomesticDrivingPrivilege.domestic_vehicle_restrictions
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_restrictions"),
- true,
- "domestic_vehicle_restriction_code",
- "Restriction Code",
- DocumentAttributeType.String
- )
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_restrictions"),
- true,
- "domestic_vehicle_restriction_description",
- "Vehicle Category Description",
- DocumentAttributeType.String
- )
- // details of DomesticDrivingPrivilege.domestic_vehicle_endorsements
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_endorsements"),
- true,
- "domestic_vehicle_endorsement_code",
- "Endorsement Code",
- DocumentAttributeType.String
- )
- .addDefinition(
- AAMVA_NAMESPACE,
- hashSetOf("domestic_vehicle_endorsements"),
- true,
- "domestic_vehicle_endorsement_description",
- "Vehicle Endorsement Description",
- DocumentAttributeType.String
- )
- .build()
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypeDefinition.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypeDefinition.kt
deleted file mode 100644
index d5026e6fd..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypeDefinition.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import com.android.identity.documenttype.DocumentAttributeType
-
-data class MdocComplexTypeDefinition(
- val parentIdentifiers: HashSet,
- val partOfArray: Boolean,
- val identifier: String,
- val displayName: String,
- val type: DocumentAttributeType
-)
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypeRepository.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypeRepository.kt
deleted file mode 100644
index 3e48f8830..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypeRepository.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-object MdocComplexTypeRepository {
- private val allComplexTypes: MutableMap = mutableMapOf()
-
- init {
- addComplexTypes(DrivingLicense.getMdocComplexTypes())
- addComplexTypes(VehicleRegistration.getMdocComplexTypes())
- addComplexTypes(VaccinationDocument.getMdocComplexTypes())
- }
-
- fun addComplexTypes(complexTypes: MdocComplexTypes) {
- allComplexTypes[complexTypes.docType] = complexTypes
- }
-
- fun getComplexTypes(docType: String): MdocComplexTypes? {
- return allComplexTypes[docType]
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypes.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypes.kt
deleted file mode 100644
index a52e608e9..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocComplexTypes.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import com.android.identity.documenttype.DocumentAttributeType
-
-class MdocComplexTypes private constructor(
- val docType: String,
- val namespaces: List
-) {
-
- data class Builder(
- val docType: String,
- val namespaces: MutableMap = mutableMapOf()
- ) {
- fun addDefinition(
- namespace: String,
- parentIdentifiers: HashSet,
- partOfArray: Boolean,
- identifier: String,
- displayName: String,
- type: DocumentAttributeType
- ) = apply {
- if (!namespaces.containsKey(namespace)) {
- namespaces[namespace] = MdocNamespaceComplexTypes.Builder(namespace)
- }
- namespaces[namespace]!!.addDefinition(parentIdentifiers, partOfArray, identifier, displayName, type)
- }
-
- fun build() = MdocComplexTypes(docType, namespaces.values.map { it.build() })
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocNamespaceComplexTypes.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocNamespaceComplexTypes.kt
deleted file mode 100644
index ae68f8021..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/MdocNamespaceComplexTypes.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import com.android.identity.documenttype.DocumentAttributeType
-
-class MdocNamespaceComplexTypes(
- val namespace: String,
- val dataElements: List
-) {
- data class Builder(
- val namespace: String,
- val dataElements: MutableList = mutableListOf()
- ) {
- fun addDefinition(
- parentIdentifiers: HashSet,
- partOfArray: Boolean,
- identifier: String,
- displayName: String,
- type: DocumentAttributeType
- ) = apply {
- dataElements.add(MdocComplexTypeDefinition(parentIdentifiers, partOfArray, identifier, displayName, type))
- }
-
- fun build() = MdocNamespaceComplexTypes(namespace, dataElements)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/ShowDocumentDataFragment.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/ShowDocumentDataFragment.kt
deleted file mode 100644
index 148ad9fa0..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/ShowDocumentDataFragment.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.core.text.HtmlCompat
-import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
-import androidx.fragment.app.Fragment
-import androidx.navigation.fragment.navArgs
-import com.android.identity.wallet.databinding.FragmentShowDocumentDataBinding
-import com.android.identity.wallet.transfer.TransferManager
-
-class ShowDocumentDataFragment : Fragment() {
-
- private val arguments by navArgs()
- private var _binding: FragmentShowDocumentDataBinding? = null
- private val binding get() = _binding!!
- private lateinit var transferManager: TransferManager
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- transferManager = TransferManager.getInstance(requireContext())
- }
-
- override fun onDestroy() {
- super.onDestroy()
- transferManager.destroy()
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentShowDocumentDataBinding.inflate(inflater, container, false)
- return binding.root
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- loadDocumentElements()
- }
-
- private fun loadDocumentElements() {
- transferManager.readDocumentEntries(arguments.documentName).let { result ->
- binding.tvResults.text = HtmlCompat.fromHtml(result.text, FROM_HTML_MODE_LEGACY)
- result.portrait?.let { portrait ->
- binding.ivPortrait.setImageBitmap(portrait)
- binding.ivPortrait.visibility = View.VISIBLE
- }
- result.signature?.let { signature ->
- binding.ivSignature.setImageBitmap(signature)
- binding.ivSignature.visibility = View.VISIBLE
- }
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/VaccinationDocument.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/VaccinationDocument.kt
deleted file mode 100644
index bd4798ee3..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/VaccinationDocument.kt
+++ /dev/null
@@ -1,236 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import com.android.identity.documenttype.DocumentAttributeType
-import com.android.identity.documenttype.knowntypes.Options
-
-object VaccinationDocument {
- const val MICOV_ATT_NAMESPACE = "org.micov.attestation.1"
- const val MICOV_VTR_NAMESPACE = "org.micov.vtr.1"
- fun getMdocComplexTypes() = MdocComplexTypes.Builder("org.micov.1")
-
- .addDefinition(
- MICOV_ATT_NAMESPACE,
- hashSetOf("RA01_test"),
- false,
- "Result",
- "Test Result",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_ATT_NAMESPACE,
- hashSetOf("RA01_test"),
- false,
- "TypeOfTest",
- "Type of Test",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_ATT_NAMESPACE,
- hashSetOf("RA01_test"),
- false,
- "TimeOfTest",
- "Time of Test",
- DocumentAttributeType.DateTime
- )
- .addDefinition(
- MICOV_ATT_NAMESPACE,
- hashSetOf("safeEntry_Leisure"),
- false,
- "SeCondFulfilled",
- "Second Fulfilled",
- DocumentAttributeType.Boolean
- )
- .addDefinition(
- MICOV_ATT_NAMESPACE,
- hashSetOf("safeEntry_Leisure"),
- false,
- "SeCondType",
- "Second Type",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_ATT_NAMESPACE,
- hashSetOf("safeEntry_Leisure"),
- false,
- "SeCondExpiry",
- "Second Expiry",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "tg",
- "Disease or Agent Targeted",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "vp",
- "Vaccine or Prophylaxis",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "mp",
- "Vaccine Medicinal Product",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "br",
- "Vaccine Brand",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "ma",
- "Manufacturer",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "bn",
- "Batch/Lot Number of the Vaccine",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "dn",
- "Dose Number",
- DocumentAttributeType.Number
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "sd",
- "Total Series of Doses",
- DocumentAttributeType.Number
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "dt",
- "Date of Vaccination",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "co",
- "Country of Vaccination",
- DocumentAttributeType.StringOptions(Options.COUNTRY_ISO_3166_1_ALPHA_2)
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "ao",
- "Administering Organization",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "ap",
- "Administering Professional",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "nx",
- "Due Date of Next Dose",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "is",
- "Certificate Issuer",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "ci",
- "Unique Certificate Identifier (UVCI)",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "pd",
- "Protection Duration",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "vf",
- "Valid From",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("v_RA01_1", "v_RA01_2"),
- false,
- "vu",
- "Valid Until",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("pid_PPN", "pid_DL"),
- false,
- "pty",
- "Type of Person Identifier",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("pid_PPN", "pid_DL"),
- false,
- "pnr",
- "Unique number for the PTY/PIC/(PIA) combination",
- DocumentAttributeType.String
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("pid_PPN", "pid_DL"),
- false,
- "pic",
- "Issuing Country of the PTY",
- DocumentAttributeType.StringOptions(Options.COUNTRY_ISO_3166_1_ALPHA_2)
- )
- .addDefinition(
- MICOV_VTR_NAMESPACE,
- hashSetOf("pid_PPN", "pid_DL"),
- false,
- "pia",
- "Issuing Authority of the PTY",
- DocumentAttributeType.String,
- )
- .build()
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentdata/VehicleRegistration.kt b/appholder/src/main/java/com/android/identity/wallet/documentdata/VehicleRegistration.kt
deleted file mode 100644
index 3b3c64556..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentdata/VehicleRegistration.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-package com.android.identity.wallet.documentdata
-
-import com.android.identity.documenttype.DocumentAttributeType
-import com.android.identity.documenttype.knowntypes.Options
-
-object VehicleRegistration {
- const val MVR_NAMESPACE = "nl.rdw.mekb.1"
- fun getMdocComplexTypes() = MdocComplexTypes.Builder("nl.rdw.mekb.1")
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("registration_info"),
- false,
- "issuingCountry",
- "Country Code",
- DocumentAttributeType.StringOptions(Options.COUNTRY_ISO_3166_1_ALPHA_2)
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("registration_info"),
- false,
- "competentAuthority",
- "Competent Authority",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("registration_info"),
- false,
- "registrationNumber",
- "UN/EU Element A",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("registration_info"),
- false,
- "validFrom",
- "Custom EKB Element, Valid From",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("registration_info"),
- false,
- "validUntil",
- "Custom EKB Element, Valid Until",
- DocumentAttributeType.Date
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("registration_holder"),
- false,
- "holderInfo",
- "Personal Data",
- DocumentAttributeType.ComplexType
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("registration_holder"),
- false,
- "ownershipStatus",
- "Ownership Status",
- DocumentAttributeType.Number
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("holderInfo"),
- false,
- "name",
- "Name of the Vehicle Owner",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("holderInfo"),
- false,
- "address",
- "Address of the Vehicle Owner",
- DocumentAttributeType.ComplexType
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("address"),
- false,
- "streetName",
- "Street Name",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("address"),
- false,
- "houseNumber",
- "House Number",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("address"),
- false,
- "houseNumberSuffix",
- "House Number Suffix",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("address"),
- false,
- "postalCode",
- "Postal Code",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("address"),
- false,
- "placeOfResidence",
- "Place of Residence",
- DocumentAttributeType.String
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("basic_vehicle_info"),
- false,
- "vehicle",
- "Vehicle",
- DocumentAttributeType.ComplexType
- )
- .addDefinition(
- MVR_NAMESPACE,
- hashSetOf("vehicle"),
- false,
- "make",
- "Make of the Vehicle",
- DocumentAttributeType.String,
- )
-
- .build()
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoScreen.kt b/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoScreen.kt
deleted file mode 100644
index 9581661d0..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoScreen.kt
+++ /dev/null
@@ -1,458 +0,0 @@
-package com.android.identity.wallet.documentinfo
-
-import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.PagerState
-import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Delete
-import androidx.compose.material.icons.filled.Key
-import androidx.compose.material.icons.filled.Refresh
-import androidx.compose.material.icons.filled.RemoveRedEye
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.Divider
-import androidx.compose.material3.Icon
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedButton
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Alignment.Companion.CenterHorizontally
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.SpanStyle
-import androidx.compose.ui.text.buildAnnotatedString
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.text.withStyle
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.android.identity.crypto.EcCurve
-import com.android.identity.securearea.KeyPurpose
-import com.android.identity.wallet.R
-import com.android.identity.wallet.composables.LoadingIndicator
-import com.android.identity.wallet.composables.ShowToast
-import com.android.identity.wallet.composables.gradientFor
-import com.android.identity.wallet.theme.HolderAppTheme
-
-@Composable
-fun DocumentInfoScreen(
- viewModel: DocumentInfoViewModel,
- onNavigateUp: () -> Unit,
- onNavigateToDocumentDetails: () -> Unit
-) {
- val state by viewModel.screenState.collectAsState()
- if (state.isDeleted) {
- ShowToast(message = stringResource(id = R.string.delete_document_deleted_message))
- onNavigateUp()
- }
-
- DocumentInfoScreenContent(
- screenState = state,
- onRefreshCredentials = viewModel::refreshCredentials,
- onShowDocumentElements = { onNavigateToDocumentDetails() },
- onDeleteDocument = { viewModel.promptDocumentDelete() },
- onConfirmDocumentDelete = viewModel::confirmDocumentDelete,
- onCancelDocumentDelete = viewModel::cancelDocumentDelete
- )
-}
-
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-private fun DocumentInfoScreenContent(
- modifier: Modifier = Modifier,
- screenState: DocumentInfoScreenState,
- onRefreshCredentials: () -> Unit,
- onShowDocumentElements: () -> Unit,
- onDeleteDocument: () -> Unit,
- onConfirmDocumentDelete: () -> Unit,
- onCancelDocumentDelete: () -> Unit,
-) {
- Scaffold(
- modifier = modifier
- ) { paddingValues ->
- Column(
- modifier = Modifier.padding(paddingValues),
- verticalArrangement = Arrangement.spacedBy(12.dp),
- horizontalAlignment = CenterHorizontally
- ) {
- if (screenState.isLoading) {
- LoadingIndicator(
- modifier = Modifier.fillMaxSize()
- )
- } else {
- Box(modifier = Modifier.fillMaxSize()) {
- Column(modifier = Modifier.fillMaxWidth()) {
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp)
- .border(
- 2.dp,
- gradientFor(screenState.documentColor),
- RoundedCornerShape(12.dp)
- )
- ) {
- Column(
- modifier = Modifier.padding(12.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- LabeledValue(
- label = stringResource(id = R.string.label_document_name),
- value = screenState.documentName
- )
- LabeledValue(
- label = stringResource(id = R.string.label_document_type),
- value = screenState.documentType
- )
- LabeledValue(
- label = stringResource(id = R.string.label_date_provisioned),
- value = screenState.provisioningDate
- )
- LabeledValue(
- label = stringResource(id = R.string.label_last_time_used),
- value = screenState.lastTimeUsedDate.ifBlank { "N/A" }
- )
- LabeledValue(
- label = stringResource(id = R.string.label_issuer),
- value = if (screenState.isSelfSigned) "Self-Signed on Device" else "N/A"
- )
- }
- }
- val pagerState = rememberPagerState(pageCount = { screenState.authKeys.size })
- HorizontalPager(
- modifier = Modifier
- .fillMaxWidth(),
- state = pagerState,
- ) { page ->
- val key = screenState.authKeys[page]
- AuthenticationKeyInfo(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp)
- .clip(RoundedCornerShape(12.dp))
- .background(MaterialTheme.colorScheme.secondaryContainer),
- authKeyInfo = key
- )
- }
- PagerIndicators(
- modifier = Modifier
- .height(24.dp)
- .fillMaxWidth()
- .align(CenterHorizontally),
- pagerState = pagerState,
- itemsCount = screenState.authKeys.size,
- )
- Divider(modifier = Modifier.padding(top = 12.dp))
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- horizontalArrangement = Arrangement.SpaceEvenly,
- ) {
- Column(
- modifier = Modifier
- .clip(RoundedCornerShape(8.dp))
- .weight(1f)
- .clickable { onRefreshCredentials() },
- horizontalAlignment = CenterHorizontally
- ) {
- Column(
- modifier = Modifier.padding(12.dp),
- horizontalAlignment = CenterHorizontally
- ) {
- Icon(
- imageVector = Icons.Default.Refresh,
- contentDescription = stringResource(id = R.string.bt_refresh_auth_keys),
- tint = MaterialTheme.colorScheme.primary
- )
- Text(
- text = stringResource(id = R.string.bt_refresh_auth_keys),
- style = MaterialTheme.typography.bodySmall
- )
- }
- }
- Column(
- modifier = Modifier
- .clip(RoundedCornerShape(8.dp))
- .weight(1f)
- .clickable { onShowDocumentElements() },
- horizontalAlignment = CenterHorizontally
- ) {
- Column(
- modifier = Modifier.padding(12.dp),
- horizontalAlignment = CenterHorizontally
- ) {
- Icon(
- imageVector = Icons.Default.RemoveRedEye,
- contentDescription = stringResource(id = R.string.bt_show_data),
- tint = MaterialTheme.colorScheme.primary
- )
- Text(
- text = stringResource(id = R.string.bt_show_data),
- style = MaterialTheme.typography.bodySmall
- )
- }
- }
- }
- }
- OutlinedButton(
- modifier = Modifier
- .fillMaxWidth()
- .padding(start = 16.dp, end = 16.dp, bottom = 24.dp)
- .align(Alignment.BottomCenter),
- onClick = onDeleteDocument,
- border = BorderStroke(1.dp, MaterialTheme.colorScheme.error),
- colors = ButtonDefaults.outlinedButtonColors(
- containerColor = MaterialTheme.colorScheme.errorContainer,
- contentColor = MaterialTheme.colorScheme.error
- )
- ) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.spacedBy(4.dp)
- ) {
- Icon(
- imageVector = Icons.Default.Delete,
- contentDescription = stringResource(id = R.string.bt_delete),
- )
- Text(text = stringResource(id = R.string.bt_delete))
- }
- }
- if (screenState.isDeletingPromptShown) {
- DeleteDocumentPrompt(
- onConfirm = onConfirmDocumentDelete,
- onCancel = onCancelDocumentDelete
- )
- }
- }
- }
- }
- }
-}
-
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-private fun PagerIndicators(
- modifier: Modifier = Modifier,
- pagerState: PagerState,
- itemsCount: Int,
-) {
- Row(
- modifier,
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically
- ) {
- repeat(itemsCount) { iteration ->
- val color = if (pagerState.currentPage == iteration) {
- MaterialTheme.colorScheme.primary
- } else {
- MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = .2f)
- }
- Box(
- modifier = Modifier
- .padding(2.dp)
- .clip(CircleShape)
- .background(color)
- .size(12.dp)
- )
- }
- }
-}
-
-@Composable
-private fun AuthenticationKeyInfo(
- modifier: Modifier = Modifier,
- authKeyInfo: DocumentInfoScreenState.KeyInformation
-) {
- Row(
- modifier = modifier,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(
- modifier = Modifier
- .size(48.dp)
- .padding(horizontal = 8.dp),
- imageVector = Icons.Default.Key,
- contentDescription = "${authKeyInfo.counter}",
- tint = MaterialTheme.colorScheme.primary.copy(alpha = .5f)
- )
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 8.dp, vertical = 12.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- LabeledValue(
- label = stringResource(id = R.string.txt_keystore_implementation),
- value = authKeyInfo.secureAreaDisplayName
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_counter),
- value = "${authKeyInfo.counter}"
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_domain),
- value = authKeyInfo.domain
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_valid_from),
- value = authKeyInfo.validFrom
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_valid_until),
- value = authKeyInfo.validUntil
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_issuer_data),
- value = stringResource(
- id = R.string.document_info_issuer_data_bytes,
- authKeyInfo.issuerDataBytesCount
- )
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_usage_count),
- value = "${authKeyInfo.usagesCount}"
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_key_purposes),
- value = authKeyInfo.keyPurposes.toString()
- )
- LabeledValue(
- label = stringResource(id = R.string.document_info_ec_curve),
- value = authKeyInfo.ecCurve.toString()
- )
- }
- }
-}
-
-@Composable
-private fun DeleteDocumentPrompt(
- onConfirm: () -> Unit,
- onCancel: () -> Unit
-) {
- AlertDialog(
- onDismissRequest = onCancel,
- title = {
- Text(text = stringResource(id = R.string.delete_document_prompt_title))
- },
- text = {
- Text(text = stringResource(id = R.string.delete_document_prompt_message))
- },
- confirmButton = {
- TextButton(onClick = onConfirm) {
- Text(text = stringResource(id = R.string.bt_delete))
- }
- },
- dismissButton = {
- TextButton(onClick = onCancel) {
- Text(text = stringResource(id = R.string.bt_cancel))
- }
- }
- )
-}
-
-@Composable
-private fun LabeledValue(
- modifier: Modifier = Modifier,
- label: String,
- value: String
-) {
- val textValue = buildAnnotatedString {
- withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
- append(label)
- append(": ")
- }
- append(value)
- }
- Text(
- modifier = modifier,
- text = textValue,
- style = MaterialTheme.typography.titleMedium,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis
- )
-}
-
-@Composable
-@Preview
-private fun PreviewDocumentInfoScreenLoading() {
- HolderAppTheme {
- DocumentInfoScreenContent(
- screenState = DocumentInfoScreenState(
- isLoading = true
- ),
- onRefreshCredentials = {},
- onShowDocumentElements = {},
- onDeleteDocument = {},
- onConfirmDocumentDelete = {}
- ) {}
- }
-}
-
-@Composable
-@Preview
-private fun PreviewDocumentInfoScreen() {
- HolderAppTheme {
- DocumentInfoScreenContent(
- screenState = DocumentInfoScreenState(
- documentName = "Erica's Driving Licence",
- documentType = "org.iso.18013.5.1.mDL",
- provisioningDate = "16-07-2023",
- isSelfSigned = true,
- authKeys = listOf(
- DocumentInfoScreenState.KeyInformation(
- counter = 1,
- validFrom = "16-07-2023",
- validUntil = "23-07-2023",
- domain = "Domain",
- usagesCount = 1,
- issuerDataBytesCount = "Issuer 1".toByteArray().count(),
- keyPurposes = KeyPurpose.AGREE_KEY,
- ecCurve = EcCurve.P256,
- isHardwareBacked = false,
- secureAreaDisplayName = "Secure Area Name"
- ),
- DocumentInfoScreenState.KeyInformation(
- counter = 2,
- validFrom = "16-07-2023",
- validUntil = "23-07-2023",
- domain = "Domain",
- usagesCount = 0,
- issuerDataBytesCount = "Issuer 2".toByteArray().count(),
- keyPurposes = KeyPurpose.SIGN,
- ecCurve = EcCurve.ED25519,
- isHardwareBacked = true,
- secureAreaDisplayName = "Secure Area Name"
-
- )
- )
- ),
- onRefreshCredentials = {},
- onShowDocumentElements = {},
- onDeleteDocument = {},
- onConfirmDocumentDelete = {}
- ) {}
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoScreenState.kt b/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoScreenState.kt
deleted file mode 100644
index cde989dff..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoScreenState.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.android.identity.wallet.documentinfo
-
-import androidx.compose.runtime.Immutable
-import com.android.identity.crypto.EcCurve
-import com.android.identity.securearea.KeyPurpose
-import com.android.identity.wallet.document.DocumentColor
-
-@Immutable
-data class DocumentInfoScreenState(
- val isLoading: Boolean = false,
- val documentName: String = "",
- val documentType: String = "",
- val documentColor: DocumentColor = DocumentColor.Green,
- val provisioningDate: String = "",
- val lastTimeUsedDate: String = "",
- val isSelfSigned: Boolean = false,
- val authKeys: List = emptyList(),
- val isDeletingPromptShown: Boolean = false,
- val isDeleted: Boolean = false
-) {
-
- @Immutable
- data class KeyInformation(
- val counter: Int,
- val validFrom: String,
- val validUntil: String,
- val domain: String,
- val issuerDataBytesCount: Int,
- val usagesCount: Int,
- val keyPurposes: KeyPurpose,
- val ecCurve: EcCurve,
- val isHardwareBacked: Boolean,
- val secureAreaDisplayName: String
- )
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoViewModel.kt
deleted file mode 100644
index b5a27a4de..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/documentinfo/DocumentInfoViewModel.kt
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.android.identity.wallet.documentinfo
-
-import androidx.lifecycle.SavedStateHandle
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.createSavedStateHandle
-import androidx.lifecycle.viewModelScope
-import androidx.lifecycle.viewmodel.initializer
-import androidx.lifecycle.viewmodel.viewModelFactory
-import com.android.identity.wallet.composables.toCardArt
-import com.android.identity.wallet.document.DocumentInformation
-import com.android.identity.wallet.document.DocumentManager
-import com.android.identity.wallet.fragment.DocumentDetailFragmentArgs
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.update
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class DocumentInfoViewModel(
- private val documentManager: DocumentManager,
- savedStateHandle: SavedStateHandle
-) : ViewModel() {
-
- private val args = DocumentDetailFragmentArgs.fromSavedStateHandle(savedStateHandle)
- private val _state = MutableStateFlow(DocumentInfoScreenState())
- val screenState: StateFlow = _state.asStateFlow()
-
- fun loadDocument(documentName: String) {
- _state.update { it.copy(isLoading = true) }
- viewModelScope.launch {
- val documentInfo = withContext(Dispatchers.IO) {
- documentManager.getDocumentInformation(documentName)
- }
- onDocumentInfoLoaded(documentInfo)
- }
- }
-
- fun promptDocumentDelete() {
- _state.update { it.copy(isDeletingPromptShown = true) }
- }
-
- fun confirmDocumentDelete() {
- documentManager.deleteCredentialByName(args.documentName)
- _state.update { it.copy(isDeleted = true, isDeletingPromptShown = false) }
- }
-
- fun cancelDocumentDelete() {
- _state.update { it.copy(isDeletingPromptShown = false) }
- }
-
- fun refreshCredentials() {
- _state.update { it.copy(isLoading = true) }
- viewModelScope.launch {
- withContext(Dispatchers.IO) {
- documentManager.refreshCredentials(args.documentName)
- }
- loadDocument(args.documentName)
- }
- }
-
- private fun onDocumentInfoLoaded(documentInformation: DocumentInformation?) {
- documentInformation?.let {
- _state.update {
- it.copy(
- isLoading = false,
- documentName = documentInformation.userVisibleName,
- documentType = documentInformation.docType,
- documentColor = documentInformation.documentColor.toCardArt(),
- provisioningDate = documentInformation.dateProvisioned,
- isSelfSigned = documentInformation.selfSigned,
- lastTimeUsedDate = documentInformation.lastTimeUsed,
- authKeys = documentInformation.authKeys.asScreenStateKeys()
- )
- }
- }
- }
-
- private fun List.asScreenStateKeys(): List {
- return map { keyData ->
- DocumentInfoScreenState.KeyInformation(
- counter = keyData.counter,
- validFrom = keyData.validFrom,
- validUntil = keyData.validUntil,
- domain = keyData.domain,
- issuerDataBytesCount = keyData.issuerDataBytesCount,
- usagesCount = keyData.usagesCount,
- keyPurposes = keyData.keyPurposes,
- ecCurve = keyData.ecCurve,
- isHardwareBacked = keyData.isHardwareBacked,
- secureAreaDisplayName = keyData.secureAreaDisplayName
- )
- }
- }
-
- companion object {
- fun Factory(documentManager: DocumentManager): ViewModelProvider.Factory =
- viewModelFactory {
- initializer {
- DocumentInfoViewModel(documentManager, createSavedStateHandle())
- }
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/fragment/DocumentDetailFragment.kt b/appholder/src/main/java/com/android/identity/wallet/fragment/DocumentDetailFragment.kt
deleted file mode 100644
index 36e001cf0..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/fragment/DocumentDetailFragment.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.android.identity.wallet.fragment
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.ui.platform.ComposeView
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.findNavController
-import androidx.navigation.fragment.navArgs
-import com.android.identity.wallet.document.DocumentManager
-import com.android.identity.wallet.documentinfo.DocumentInfoScreen
-import com.android.identity.wallet.documentinfo.DocumentInfoViewModel
-import com.android.identity.wallet.theme.HolderAppTheme
-
-class DocumentDetailFragment : Fragment() {
-
- private val args: DocumentDetailFragmentArgs by navArgs()
- private val viewModel by viewModels {
- val documentManager = DocumentManager.getInstance(requireContext())
- DocumentInfoViewModel.Factory(documentManager)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return ComposeView(requireContext()).apply {
- setContent {
- HolderAppTheme {
- DocumentInfoScreen(
- viewModel = viewModel,
- onNavigateUp = { findNavController().navigateUp() },
- onNavigateToDocumentDetails = { onShowData(args.documentName) }
- )
- }
- }
- viewModel.loadDocument(args.documentName)
- }
- }
-
- private fun onShowData(documentName: String) {
- val direction = DocumentDetailFragmentDirections
- .navigateToDocumentData(documentName)
- findNavController().navigate(direction)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/fragment/ReverseEngagementFragment.kt b/appholder/src/main/java/com/android/identity/wallet/fragment/ReverseEngagementFragment.kt
deleted file mode 100644
index 22ce77a2a..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/fragment/ReverseEngagementFragment.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.android.identity.wallet.fragment
-
-import android.Manifest
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Bundle
-import android.provider.Settings
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.mdoc.origininfo.OriginInfo
-import com.android.identity.wallet.databinding.FragmentReverseEngagementBinding
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.util.logWarning
-import com.android.identity.wallet.viewmodel.ShareDocumentViewModel
-import com.budiyev.android.codescanner.CodeScanner
-import com.budiyev.android.codescanner.DecodeCallback
-
-class ReverseEngagementFragment : Fragment() {
-
- private var _binding: FragmentReverseEngagementBinding? = null
- private var codeScanner: CodeScanner? = null
-
- private val binding get() = _binding!!
- private val vm: ShareDocumentViewModel by viewModels()
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentReverseEngagementBinding.inflate(inflater)
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- binding.btCancel.setOnClickListener {
- findNavController().navigate(
- ReverseEngagementFragmentDirections.actionReverseEngagementFragmentToSelectDocumentFragment()
- )
- }
-
- codeScanner = CodeScanner(requireContext(), binding.csScanner)
- codeScanner?.decodeCallback = DecodeCallback { result ->
- requireActivity().runOnUiThread {
- val qrText = result.text
- log("qrText: $qrText")
- val uri = Uri.parse(qrText)
- if (uri.scheme.equals("mdoc")) {
- vm.startPresentationReverseEngagement(qrText, emptyList())
- findNavController().navigate(
- ReverseEngagementFragmentDirections.actionReverseEngagementFragmentToTransferDocumentFragment()
- )
- } else {
- logWarning("Ignoring QR code with scheme " + uri.scheme)
- }
- }
- }
- binding.csScanner.setOnClickListener { codeScanner?.startPreview() }
- }
-
- override fun onResume() {
- super.onResume()
- enableReader()
- }
-
- override fun onPause() {
- super.onPause()
- disableReader()
- }
-
- private fun enableReader() {
- if (isAllPermissionsGranted()) {
- codeScanner?.startPreview()
- } else {
- shouldRequestPermission()
- }
- }
-
- private fun disableReader() {
- codeScanner?.releaseResources()
- }
-
- private val appPermissions: List
- get() = mutableListOf(Manifest.permission.CAMERA)
-
- private fun shouldRequestPermission() {
- val permissionsNeeded = appPermissions.filter { permission ->
- ContextCompat.checkSelfPermission(
- requireContext(),
- permission
- ) != PackageManager.PERMISSION_GRANTED
- }
-
- if (permissionsNeeded.isNotEmpty()) {
- permissionsLauncher.launch(
- permissionsNeeded.toTypedArray()
- )
- }
- }
-
- private fun isAllPermissionsGranted(): Boolean {
- // If any permission is not granted return false
- return appPermissions.none { permission ->
- ContextCompat.checkSelfPermission(
- requireContext(),
- permission
- ) != PackageManager.PERMISSION_GRANTED
- }
- }
-
- private val permissionsLauncher =
- registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
- permissions.entries.forEach {
- log("permissionsLauncher ${it.key} = ${it.value}")
- // Open settings if user denied any required permission
- if (!it.value && !shouldShowRequestPermissionRationale(it.key)) {
- openSettings()
- return@registerForActivityResult
- }
- }
- }
-
- private fun openSettings() {
- val intent = Intent()
- intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
- intent.data = Uri.fromParts("package", requireContext().packageName, null)
- startActivity(intent)
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/fragment/SelfSignedDetailsFragment.kt b/appholder/src/main/java/com/android/identity/wallet/fragment/SelfSignedDetailsFragment.kt
deleted file mode 100644
index 0a92ebd7e..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/fragment/SelfSignedDetailsFragment.kt
+++ /dev/null
@@ -1,558 +0,0 @@
-package com.android.identity.wallet.fragment
-
-import android.Manifest
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.Canvas
-import android.graphics.Matrix
-import android.graphics.Typeface
-import android.icu.text.SimpleDateFormat
-import android.net.Uri
-import android.os.Bundle
-import android.os.Environment
-import android.provider.MediaStore
-import android.text.Editable
-import android.text.InputType
-import android.util.TypedValue
-import android.view.LayoutInflater
-import android.view.View
-import android.view.View.NOT_FOCUSABLE
-import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.widget.ArrayAdapter
-import android.widget.CheckBox
-import android.widget.EditText
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.Spinner
-import android.widget.TextView
-import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
-import androidx.activity.result.contract.ActivityResultContracts.TakePicture
-import androidx.core.content.FileProvider
-import androidx.core.widget.addTextChangedListener
-import androidx.exifinterface.media.ExifInterface
-import androidx.exifinterface.media.ExifInterface.ORIENTATION_UNDEFINED
-import androidx.exifinterface.media.ExifInterface.TAG_ORIENTATION
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.findNavController
-import androidx.navigation.fragment.navArgs
-import com.android.identity.documenttype.DocumentAttributeType
-import com.android.identity.documenttype.IntegerOption
-import com.android.identity.documenttype.StringOption
-import com.android.identity.wallet.databinding.FragmentSelfSignedDetailsBinding
-import com.android.identity.wallet.util.Field
-import com.android.identity.wallet.util.FormatUtil.fullDateStringToMilliseconds
-import com.android.identity.wallet.util.FormatUtil.millisecondsToFullDateString
-import com.android.identity.wallet.selfsigned.ProvisionInfo
-import com.android.identity.wallet.selfsigned.SelfSignedDocumentData
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.util.logError
-import com.android.identity.wallet.viewmodel.SelfSignedViewModel
-import com.google.android.material.datepicker.MaterialDatePicker
-import java.io.File
-import java.io.IOException
-import java.util.Date
-import kotlin.math.max
-import kotlin.math.min
-
-class SelfSignedDetailsFragment : Fragment() {
-
- private val vm: SelfSignedViewModel by viewModels()
- private val args: SelfSignedDetailsFragmentArgs by navArgs()
- private val nameElements = listOf("given_name", "name", "gn")
-
- private var _binding: FragmentSelfSignedDetailsBinding? = null
- private val binding get() = _binding!!
-
- private lateinit var provisionInfo: ProvisionInfo
- private lateinit var documentNameEditText: EditText
- private lateinit var holderNameEditText: EditText
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- provisionInfo = args.provisionInfo
- }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentSelfSignedDetailsBinding.inflate(inflater)
- binding.fragment = this
- bindUI()
- return binding.root
- }
-
- private fun bindUI() {
- // Create all fields in the screen
- val documentName = getDocumentNameValue()
- addField(
- Field(
- 0,
- "Document Name",
- provisionInfo.docName,
- DocumentAttributeType.String,
- documentName
- )
- )
- documentNameEditText = binding.layoutSelfSignedDetails.findViewById(0)
- vm.getFields(provisionInfo.docType).forEach { field ->
- addField(field)
- if (field.name in nameElements) {
- holderNameEditText = binding.layoutSelfSignedDetails.findViewById(field.id)
- }
- }
- setupTextChangeListener()
-
- vm.loading.observe(viewLifecycleOwner) {
- binding.loadingProgress.visibility = it
- }
-
- vm.created.observe(viewLifecycleOwner) {
- Toast.makeText(
- requireContext(), "Document created successfully!",
- Toast.LENGTH_SHORT
- ).show()
- findNavController().navigate(
- SelfSignedDetailsFragmentDirections.actionSelfSignedDetailsToSelectDocumentFragment()
- )
- }
- }
-
- private fun getDocumentNameValue(): String {
- val value = vm.getFields(provisionInfo.docType).find { it.name in nameElements }?.value
- val name = value?.toString() ?: ""
- val docName = provisionInfo.docName
- return if (name.isBlank()) docName else "$name's $docName"
- }
-
- private fun setupTextChangeListener() {
- holderNameEditText.addTextChangedListener { newValue ->
- documentNameEditText.setText("$newValue's ${provisionInfo.docName}")
- }
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- updateList()
- }
-
- private fun updateList() {
- provisionInfo.docName = documentNameEditText.text.toString()
- vm.getFields(provisionInfo.docType).forEachIndexed { index, field ->
- vm.getFields(provisionInfo.docType)[index] = getField(field)
- }
- }
-
- private fun getField(field: Field): Field {
- return when (field.fieldType) {
- is DocumentAttributeType.Picture -> {
- Field(
- field.id,
- field.label,
- field.name,
- field.fieldType,
- getImageViewValue(field.id),
- namespace = field.namespace,
- parentId = field.parentId,
- stringOptions = field.stringOptions,
- integerOptions = field.integerOptions
- )
- }
-
- is DocumentAttributeType.Boolean -> {
- Field(
- field.id,
- field.label,
- field.name,
- field.fieldType,
- getViewValue(field.id),
- namespace = field.namespace,
- parentId = field.parentId,
- stringOptions = field.stringOptions,
- integerOptions = field.integerOptions
- )
- }
-
- else -> {
- Field(
- field.id,
- field.label,
- field.name,
- field.fieldType,
- getViewValue(field.id),
- namespace = field.namespace,
- isArray = field.isArray,
- parentId = field.parentId,
- stringOptions = field.stringOptions,
- integerOptions = field.integerOptions
- )
- }
- }
- }
-
- private fun addField(field: Field) {
- when (field.fieldType) {
- is DocumentAttributeType.Picture -> {
- binding.layoutSelfSignedDetails.addView(
- getTextView(field.id + 500, field.label)
- )
- binding.layoutSelfSignedDetails.addView(getImageView(
- field.id, field.value as Bitmap
- ) { dispatchTakePictureIntent(field.id) })
- }
-
- is DocumentAttributeType.Boolean -> {
- binding.layoutSelfSignedDetails.addView(
- getTextView(field.id + 500, field.label)
- )
- binding.layoutSelfSignedDetails.addView(
- checkBox(field.id, field.value as Boolean)
- )
- }
-
- is DocumentAttributeType.String, DocumentAttributeType.Number -> {
- binding.layoutSelfSignedDetails.addView(
- getTextView(field.id + 500, field.label)
- )
- binding.layoutSelfSignedDetails.addView(
- getEditView(field.id, field.value.toString(), null)
- )
- }
-
- is DocumentAttributeType.Date, DocumentAttributeType.DateTime -> {
- binding.layoutSelfSignedDetails.addView(
- getTextView(field.id + 500, field.label)
- )
- binding.layoutSelfSignedDetails.addView(
- getEditView(field.id, field.value as String, picker(field.id, field.id + 500))
- )
- }
-
- is DocumentAttributeType.IntegerOptions -> {
- binding.layoutSelfSignedDetails.addView(
- getTextView(field.id + 500, field.label)
- )
- binding.layoutSelfSignedDetails.addView(
- integerOptionsSpinner(
- field.integerOptions!!, field.id, field.value
- )
- )
- }
-
- is DocumentAttributeType.StringOptions -> {
- binding.layoutSelfSignedDetails.addView(
- getTextView(field.id + 500, field.label)
- )
- binding.layoutSelfSignedDetails.addView(
- stringOptionsSpinner(
- field.stringOptions!!, field.id, field.value
- )
- )
- }
-
- is DocumentAttributeType.ComplexType -> {
- binding.layoutSelfSignedDetails.addView(
- getTitleView(field.id + 500, field.label)
- )
- }
-
- is DocumentAttributeType.Blob -> {
- binding.layoutSelfSignedDetails.addView(
- getTextView(field.id + 500, field.label)
- )
- binding.layoutSelfSignedDetails.addView(
- getEditView(field.id, field.value.toString(), null)
- )
- }
- }
- }
-
- private fun getImageView(
- id: Int, bitmap: Bitmap, onClickListener: View.OnClickListener?
- ): View {
- val imageView = ImageView(requireContext())
- imageView.id = id
- imageView.setImageBitmap(bitmap)
-
- imageView.layoutParams = LinearLayout.LayoutParams(bitmap.width, bitmap.height).also {
- it.setMargins(16, 16, 16, 0)
- }
- onClickListener?.let {
- imageView.setOnClickListener(it)
- }
- return imageView
- }
-
- private fun getTextView(id: Int, value: String): View {
- val textView = TextView(requireContext())
- textView.id = id
- textView.text = value
- textView.layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).also {
- it.setMargins(16, 16, 16, 0)
- }
- return textView
- }
-
- private fun getTitleView(id: Int, value: String): View {
- val textView = TextView(requireContext())
- textView.id = id
- textView.text = value
- textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18f)
- textView.setTypeface(textView.typeface, Typeface.BOLD)
- textView.layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).also {
- it.setMargins(16, 32, 16, 16)
- }
- return textView
- }
-
- private fun getEditView(id: Int, value: String, onClickListener: View.OnClickListener?): View {
- val editText = EditText(requireContext())
- editText.id = id
- editText.text = Editable.Factory.getInstance().newEditable(value)
- editText.layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).also {
- it.setMargins(16, 0, 16, 0)
- }
- onClickListener?.let {
- editText.setOnClickListener(it)
- // avoid open keyboard
- editText.inputType = InputType.TYPE_NULL
- editText.focusable = NOT_FOCUSABLE
- }
- return editText
- }
-
- fun onCreateSelfSigned() {
- updateList()
- val dData = SelfSignedDocumentData(
- provisionInfo, vm.getFields(provisionInfo.docType)
- )
- vm.createSelfSigned(dData)
- binding.loadingProgress.visibility = View.VISIBLE
- }
-
- private fun getImageViewValue(id: Int): Bitmap {
- val imageView = binding.layoutSelfSignedDetails.findViewById(id)
- val bitmap = Bitmap.createBitmap(
- imageView.width, imageView.height, Bitmap.Config.ARGB_8888
- )
- val canvas = Canvas(bitmap)
- imageView.draw(canvas)
- return bitmap
- }
-
- private fun getViewValue(id: Int): Any? {
- return when (val view = binding.layoutSelfSignedDetails.findViewById(id)) {
- is CheckBox -> {
- view.isChecked
- }
-
- is TextView -> {
- view.text.toString()
- }
-
- is Spinner -> {
- when (view.selectedItem) {
- is StringOption -> (view.selectedItem as StringOption).value
- is IntegerOption -> (view.selectedItem as IntegerOption).value
- else -> view.selectedItem.toString()
- }
- }
-
-
- else -> {
- String()
- }
- }
- }
-
- private fun setViewValue(id: Int, value: String) {
- val view = binding.layoutSelfSignedDetails.findViewById(id)
- if (view is TextView) {
- view.text = value
- }
- }
-
- /**
- * OnClickListener for date picker
- */
- private fun picker(id: Int, idLabel: Int) = View.OnClickListener {
- val titleText = getViewValue(idLabel) as String
- val dateText = getViewValue(id) as String
- log("$dateText - ${fullDateStringToMilliseconds(dateText)}")
- val datePicker = MaterialDatePicker.Builder.datePicker().setTitleText(titleText)
- .setSelection(fullDateStringToMilliseconds(dateText)).build()
- datePicker.addOnPositiveButtonClickListener {
- log("$it - ${millisecondsToFullDateString(it)}")
- setViewValue(id, millisecondsToFullDateString(it))
- }
- datePicker.show(parentFragmentManager, view?.tag?.toString())
- }
-
- private fun stringOptionsSpinner(
- options: List, id: Int, value: Any?
- ): View {
- val spinner = Spinner(context)
- spinner.id = id
- val adapter =
- ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, options)
- spinner.adapter = adapter
- val selected = options.find { (it.value == null && value == null) || it.value == value }
- if (selected != null) {
- spinner.setSelection(options.indexOf(selected))
- }
- return spinner
- }
-
- private fun integerOptionsSpinner(
- options: List, id: Int, value: Any?
- ): View {
- val spinner = Spinner(context)
- spinner.id = id
- val adapter =
- ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, options)
- spinner.adapter = adapter
- val selected = options.find { (it.value == null && value == null) || it.value == value }
- if (selected != null) {
- spinner.setSelection(options.indexOf(selected))
- }
- return spinner
- }
-
- private fun checkBox(id: Int, value: Boolean): View {
- val checkBox = CheckBox(context)
- checkBox.id = id
- checkBox.isChecked = value
- return checkBox
- }
-
- // Following to enable take picture
- private lateinit var photoUri: Uri
- private lateinit var currentPhotoPath: String
- private var imageViewId: Int? = null
- private val takePicture = registerForActivityResult(TakePicture()) { isSuccess ->
- if (isSuccess) {
- val rotation = calculateDegrees()
- setPic(rotation)
- }
- }
- private val cameraLauncher = registerForActivityResult(RequestPermission()) { granted ->
- if (granted) {
- proceedTakingPhoto()
- }
- }
-
- private fun dispatchTakePictureIntent(viewId: Int) {
- imageViewId = viewId
- if (!hasCameraAvailable()) return
- if (!canTakePhoto()) return
- cameraLauncher.launch(Manifest.permission.CAMERA)
- }
-
- private fun proceedTakingPhoto() {
- try {
- val imageFile = createImageFile()
- photoUri = getUriForFile(imageFile)
- takePicture.launch(photoUri)
- } catch (exception: IOException) {
- log("Error capturing image", exception)
- }
- }
-
- private fun hasCameraAvailable(): Boolean {
- val packageManager = requireContext().packageManager
- if (!packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
- val errorMessage = "This device does not have a camera."
- Toast.makeText(activity, errorMessage, Toast.LENGTH_SHORT).show()
- return false
- }
- return true
- }
-
- private fun canTakePhoto(): Boolean {
- val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
- // Ensure that there's a camera activity to handle the intent
- if (takePictureIntent.resolveActivity(requireContext().packageManager) == null) {
- val errorMessage = "Could not find camera activity."
- Toast.makeText(activity, errorMessage, Toast.LENGTH_SHORT).show()
- return false
- }
- return true
- }
-
- @Throws(IOException::class)
- private fun createImageFile(): File {
- // Create an image file name
- val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
- val storageDir: File? = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
- return File.createTempFile(
- "JPEG_${timeStamp}_", /* prefix */
- ".jpg", /* suffix */
- storageDir /* directory */
- ).apply {
- currentPhotoPath = absolutePath
- }
- }
-
- private fun getUriForFile(file: File): Uri {
- val authority = "${requireContext().packageName}.fileprovider"
- return FileProvider.getUriForFile(requireContext(), authority, file)
- }
-
- private fun calculateDegrees(): Float {
- val inputStream = requireContext().contentResolver.openInputStream(photoUri)
- val exifInterface = ExifInterface(inputStream!!)
- return when (exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_UNDEFINED)) {
- ExifInterface.ORIENTATION_ROTATE_90 -> 90f
- ExifInterface.ORIENTATION_ROTATE_180 -> 180f
- ExifInterface.ORIENTATION_ROTATE_270 -> 270f
- else -> 0f
- }.apply {
- inputStream.close()
- }
- }
-
- private fun setPic(rotation: Float) {
- val id = imageViewId
- if (id == null) {
- logError("No image view id, impossible to set picture")
- return
- }
-
- val imageView = binding.layoutSelfSignedDetails.findViewById(id)
-
- // Get the dimensions of the View
- val targetW: Int = imageView.width
- val targetH: Int = imageView.height
-
- val bmOptions = BitmapFactory.Options().apply {
- // Get the dimensions of the bitmap
- inJustDecodeBounds = true
-
- val photoW: Int = outWidth
- val photoH: Int = outHeight
-
- // Determine how much to scale down the image
- val scaleFactor: Int = max(1, min(photoW / targetW, photoH / targetH))
-
- // Decode the image file into a Bitmap sized to fill the View
- inJustDecodeBounds = false
- inSampleSize = scaleFactor
- inPurgeable = true
- }
-
- val original = BitmapFactory.decodeFile(currentPhotoPath, bmOptions)
- val rotated = if (rotation != 0f) {
- val matrix = Matrix()
- matrix.postRotate(rotation)
- Bitmap.createBitmap(original, 0, 0, original.width, original.height, matrix, true)
- } else original
- imageView.setImageBitmap(rotated)
- }
-}
-
diff --git a/appholder/src/main/java/com/android/identity/wallet/fragment/ShareDocumentFragment.kt b/appholder/src/main/java/com/android/identity/wallet/fragment/ShareDocumentFragment.kt
deleted file mode 100644
index 9f45bf667..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/fragment/ShareDocumentFragment.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.android.identity.wallet.fragment
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.activity.OnBackPressedCallback
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.wallet.databinding.FragmentShareDocumentBinding
-import com.android.identity.wallet.util.TransferStatus
-import com.android.identity.wallet.viewmodel.ShareDocumentViewModel
-
-class ShareDocumentFragment : Fragment() {
-
- private val viewModel: ShareDocumentViewModel by viewModels()
-
- private var _binding: FragmentShareDocumentBinding? = null
- private val binding get() = _binding!!
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentShareDocumentBinding.inflate(inflater)
- binding.vm = viewModel
- binding.fragment = this
- requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackCallback)
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- viewModel.message.set("Scan QR code with mdoc verifier device")
- viewModel.getTransferStatus().observe(viewLifecycleOwner) {
- when (it) {
- TransferStatus.CONNECTED -> {
- viewModel.message.set("Connected!")
- val destination = ShareDocumentFragmentDirections.toTransferDocumentFragment()
- findNavController().navigate(destination)
- }
-
- TransferStatus.REQUEST -> {
- viewModel.message.set("Request received!")
- }
-
- TransferStatus.DISCONNECTED -> {
- viewModel.message.set("Disconnected!")
- findNavController().navigateUp()
- }
-
- TransferStatus.ERROR -> {
- viewModel.message.set("Error on presentation!")
- }
-
- TransferStatus.ENGAGEMENT_DETECTED -> {
- viewModel.message.set("Engagement detected!")
- }
-
- TransferStatus.CONNECTING -> {
- viewModel.message.set("Connecting...")
- }
-
- else -> {}
- }
- }
- }
-
- override fun onResume() {
- super.onResume()
- viewModel.triggerQrEngagement()
- viewModel.showQrCode()
- }
-
- private val onBackCallback = object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- onCancel()
- }
- }
-
- fun onCancel() {
- viewModel.cancelPresentation()
- findNavController().navigateUp()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/fragment/TransferDocumentFragment.kt b/appholder/src/main/java/com/android/identity/wallet/fragment/TransferDocumentFragment.kt
deleted file mode 100644
index e12b6fb29..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/fragment/TransferDocumentFragment.kt
+++ /dev/null
@@ -1,207 +0,0 @@
-package com.android.identity.wallet.fragment
-
-import android.content.Context
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Toast
-import androidx.activity.OnBackPressedCallback
-import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.crypto.javaX509Certificate
-import com.android.identity.wallet.HolderApp
-import com.android.identity.wallet.R
-import com.android.identity.wallet.databinding.FragmentTransferDocumentBinding
-import com.android.identity.wallet.document.DocumentInformation
-import com.android.identity.wallet.transfer.TransferManager
-import com.android.identity.wallet.trustmanagement.CustomValidators
-import com.android.identity.wallet.trustmanagement.getCommonName
-import com.android.identity.wallet.util.PreferencesHelper
-import com.android.identity.wallet.util.TransferStatus
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.viewmodel.TransferDocumentViewModel
-import java.security.cert.X509Certificate
-
-class TransferDocumentFragment : Fragment() {
- private var _binding: FragmentTransferDocumentBinding? = null
- private val binding get() = _binding!!
-
- private val viewModel: TransferDocumentViewModel by activityViewModels()
-
- override fun onAttach(context: Context) {
- super.onAttach(context)
- val backPressedCallback = object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- onDone()
- }
- }
- requireActivity().onBackPressedDispatcher.addCallback(this, backPressedCallback)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentTransferDocumentBinding.inflate(inflater)
- binding.fragment = this
- binding.vm = viewModel
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- viewModel.getTransferStatus().observe(viewLifecycleOwner) { transferStatus ->
- when (transferStatus) {
- TransferStatus.CONNECTED -> log("Connected")
- TransferStatus.REQUEST -> onTransferRequested()
- TransferStatus.REQUEST_SERVED -> onRequestServed()
- TransferStatus.DISCONNECTED -> onTransferDisconnected()
- TransferStatus.ERROR -> onTransferError()
- else -> {}
- }
- }
- viewModel.connectionClosedLiveData.observe(viewLifecycleOwner) {
- onCloseConnection(
- sendSessionTerminationMessage = true,
- useTransportSpecificSessionTermination = false
- )
- }
- viewModel.authConfirmationState.observe(viewLifecycleOwner) { cancelled ->
- if (cancelled == true) {
- viewModel.onAuthenticationCancellationConsumed()
- onDone()
- findNavController().navigateUp()
- }
- }
- }
-
- private fun onRequestServed() {
- log("Request Served")
- }
-
- private fun onTransferRequested() {
- log("Request")
- var commonName = ""
- var trusted = false
- try {
- val requestedDocuments = viewModel.getRequestedDocuments()
- requestedDocuments.forEach { reqDoc ->
- val docs = viewModel.getDocuments().filter { reqDoc.docType == it.docType }
- if (!viewModel.getSelectedDocuments().any { reqDoc.docType == it.docType }) {
- if (docs.isEmpty()) {
- binding.txtDocuments.append("- No document found for ${reqDoc.docType}\n")
- return@forEach
- } else if (docs.size == 1) {
- viewModel.getSelectedDocuments().add(docs[0])
- } else {
- showDocumentSelection(docs)
- return
- }
- }
- val doc = viewModel.getSelectedDocuments().first { reqDoc.docType == it.docType }
- if (reqDoc.readerAuth != null && reqDoc.readerAuthenticated) {
- var certChain: List =
- reqDoc.readerCertificateChain!!.certificates.map { it.javaX509Certificate }
- .toList()
- val customValidators = CustomValidators.getByDocType(doc.docType)
- val result = HolderApp.trustManagerInstance.verify(
- chain = certChain,
- customValidators = customValidators
- )
- trusted = result.isTrusted
- if (result.trustChain.any()) {
- certChain = result.trustChain
- }
- commonName = certChain.last().issuerX500Principal.getCommonName("")
-
- // Add some information about the reader certificate used
- if (result.isTrusted) {
- binding.txtDocuments.append("- Trusted reader auth used: ($commonName)\n")
- } else {
- binding.txtDocuments.append("- Not trusted reader auth used: ($commonName)\n")
- if (result.error != null) {
- binding.txtDocuments.append("- TrustManager Error: (${result.error})\n")
- }
- }
- }
- binding.txtDocuments.append("- ${doc.userVisibleName} (${doc.docType})\n")
- }
- if (viewModel.getSelectedDocuments().isNotEmpty()) {
- viewModel.createSelectedItemsList()
- val direction = TransferDocumentFragmentDirections
- .navigateToConfirmation(commonName, trusted)
- findNavController().navigate(direction)
- } else {
- // Send response with 0 documents
- viewModel.sendResponseForSelection(
- onResultReady = {
- }
- )
- }
- // TODO: this is kind of a hack but we really need to move the sending of the
- // message to here instead of in the auth confirmation dialog
- if (PreferencesHelper.isConnectionAutoCloseEnabled()) {
- hideButtons()
- }
- } catch (e: Exception) {
- val message = "On request received error: ${e.message}"
- log(message, e)
- Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
- binding.txtConnectionStatus.append("\n$message")
- }
- }
-
- private fun showDocumentSelection(doc: List) {
- val alertDialogBuilder = AlertDialog.Builder(requireContext())
- alertDialogBuilder.setTitle("Select which document to share")
- val listItems = doc.map { it.userVisibleName }.toTypedArray()
- alertDialogBuilder.setSingleChoiceItems(listItems, -1) { dialogInterface, i ->
- viewModel.getSelectedDocuments().add(doc[i])
- onTransferRequested()
- dialogInterface.dismiss()
- }
- val mDialog = alertDialogBuilder.create()
- mDialog.show()
- }
-
- private fun onTransferDisconnected() {
- log("Disconnected")
- hideButtons()
- TransferManager.getInstance(requireContext()).disconnect()
- }
-
- private fun onTransferError() {
- Toast.makeText(requireContext(), "An error occurred.", Toast.LENGTH_SHORT).show()
- hideButtons()
- TransferManager.getInstance(requireContext()).disconnect()
- }
-
- private fun hideButtons() {
- binding.txtConnectionStatus.text = getString(R.string.connection_mdoc_closed)
- binding.btCloseConnection.visibility = View.GONE
- binding.btCloseTerminationMessage.visibility = View.GONE
- binding.btCloseTransportSpecific.visibility = View.GONE
- binding.btOk.visibility = View.VISIBLE
- }
-
- fun onCloseConnection(
- sendSessionTerminationMessage: Boolean,
- useTransportSpecificSessionTermination: Boolean
- ) {
- viewModel.cancelPresentation(
- sendSessionTerminationMessage,
- useTransportSpecificSessionTermination
- )
- hideButtons()
- }
-
- fun onDone() {
- onCloseConnection(
- sendSessionTerminationMessage = true,
- useTransportSpecificSessionTermination = false
- )
- findNavController().navigateUp()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedDocumentScreen.kt b/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedDocumentScreen.kt
deleted file mode 100644
index b0588e39b..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedDocumentScreen.kt
+++ /dev/null
@@ -1,424 +0,0 @@
-package com.android.identity.wallet.selfsigned
-
-import androidx.annotation.StringRes
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.Button
-import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import com.android.identity.wallet.R
-import com.android.identity.wallet.composables.CounterInput
-import com.android.identity.wallet.composables.DropDownIndicator
-import com.android.identity.wallet.composables.LabeledUserInput
-import com.android.identity.wallet.composables.OutlinedContainerHorizontal
-import com.android.identity.wallet.composables.TextDropDownRow
-import com.android.identity.wallet.composables.ValueLabel
-import com.android.identity.wallet.composables.gradientFor
-import com.android.identity.wallet.document.DocumentColor
-import com.android.identity.wallet.support.CurrentSecureArea
-import com.android.identity.wallet.support.SecureAreaSupport
-import com.android.identity.wallet.support.SecureAreaSupportState
-import com.android.identity.wallet.support.toSecureAreaState
-import com.android.identity.wallet.util.ProvisioningUtil
-
-@Composable
-fun AddSelfSignedDocumentScreen(
- viewModel: AddSelfSignedViewModel,
- onNext: () -> Unit
-) {
- val screenState by viewModel.screenState.collectAsState()
-
- AddSelfSignedDocumentScreenContent(
- modifier = Modifier.fillMaxSize(),
- screenState = screenState,
- documentItems = viewModel.documentItems,
- onDocumentTypeChanged = viewModel::updateDocumentType,
- onCardArtSelected = viewModel::updateCardArt,
- onDocumentNameChanged = viewModel::updateDocumentName,
- onKeystoreImplementationChanged = viewModel::updateKeystoreImplementation,
- onSecureAreaSupportStateUpdated = viewModel::updateSecureAreaSupportState,
- onNumberOfMsoChanged = viewModel::updateNumberOfMso,
- onMaxUseOfMsoChanged = viewModel::updateMaxUseOfMso,
- onValidityInDaysChanged = viewModel::updateValidityInDays,
- onMinValidityInDaysChanged = viewModel::updateMinValidityInDays,
- onNext = onNext
- )
-}
-
-@Composable
-private fun AddSelfSignedDocumentScreenContent(
- modifier: Modifier,
- screenState: AddSelfSignedScreenState,
- documentItems: List,
- onDocumentTypeChanged: (newType: String, newName: String) -> Unit,
- onCardArtSelected: (newCardArt: DocumentColor) -> Unit,
- onDocumentNameChanged: (newValue: String) -> Unit,
- onKeystoreImplementationChanged: (newImplementation: CurrentSecureArea) -> Unit,
- onSecureAreaSupportStateUpdated: (newState: SecureAreaSupportState) -> Unit,
- onNumberOfMsoChanged: (newValue: Int) -> Unit,
- onMaxUseOfMsoChanged: (newValue: Int) -> Unit,
- onValidityInDaysChanged: (newValue: Int) -> Unit,
- onMinValidityInDaysChanged: (newValue: Int) -> Unit,
- onNext: () -> Unit
-) {
- Scaffold(modifier = modifier) { paddingValues ->
- val scrollState = rememberScrollState()
- Column(
- modifier = Modifier
- .padding(paddingValues)
- .verticalScroll(scrollState),
- verticalArrangement = Arrangement.spacedBy(16.dp)
- ) {
- Spacer(modifier = Modifier.fillMaxWidth())
- DocumentTypeChooser(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- documentItems = documentItems,
- currentDocumentType = screenState.documentType,
- onDocumentTypeSelected = onDocumentTypeChanged
- )
- CardArtChooser(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- currentCardArt = screenState.cardArt,
- onCardArtSelected = onCardArtSelected
- )
- DocumentNameInput(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- value = screenState.documentName,
- onValueChanged = onDocumentNameChanged
- )
- KeystoreImplementationChooser(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- currentImplementation = screenState.currentSecureArea,
- onKeystoreImplementationChanged = onKeystoreImplementationChanged
- )
- SecureAreaSupport.getInstance(
- LocalContext.current,
- screenState.currentSecureArea
- ).SecureAreaAuthUi(onUiStateUpdated = onSecureAreaSupportStateUpdated)
- CounterInput(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- label = stringResource(id = R.string.txt_number_mso),
- value = screenState.numberOfMso,
- onValueChange = onNumberOfMsoChanged
- )
- CounterInput(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- label = stringResource(id = R.string.txt_max_use_mso),
- value = screenState.maxUseOfMso,
- onValueChange = onMaxUseOfMsoChanged
- )
- CounterInput(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- label = stringResource(id = R.string.validity_in_days),
- value = screenState.validityInDays,
- onValueChange = onValidityInDaysChanged
- )
- CounterInput(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- label = stringResource(id = R.string.minimum_validity_in_days),
- value = screenState.minValidityInDays,
- onValueChange = onMinValidityInDaysChanged
- )
- Button(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- onClick = onNext
- ) {
- Text(text = "Next")
- }
- }
- }
-}
-
-@Composable
-private fun DocumentTypeChooser(
- modifier: Modifier = Modifier,
- documentItems: List,
- currentDocumentType: String,
- onDocumentTypeSelected: (newType: String, newName: String) -> Unit
-) {
- LabeledUserInput(
- modifier = modifier,
- label = stringResource(id = R.string.txt_document_type)
- ) {
- var expanded by remember { mutableStateOf(false) }
- OutlinedContainerHorizontal(
- modifier = Modifier
- .fillMaxWidth()
- .clickable { expanded = true }
- ) {
- documentItems.find {it.docType == currentDocumentType}?.let {
- ValueLabel(
- modifier = Modifier.weight(1f),
- label = it.displayName
- )
- }
- DropDownIndicator()
- }
- DropdownMenu(
- modifier = Modifier
- .fillMaxWidth(0.8f),
- expanded = expanded,
- onDismissRequest = { expanded = false }
- ) {
- documentItems.forEach{
- TextDropDownRow(
- label = it.displayName,
- onSelected = {
- onDocumentTypeSelected(it.docType, it.displayName)
- expanded = false
- }
- )
- }
- }
- }
-}
-
-@Composable
-private fun CardArtChooser(
- modifier: Modifier,
- currentCardArt: DocumentColor,
- onCardArtSelected: (newCardArt: DocumentColor) -> Unit
-) {
- LabeledUserInput(
- modifier = modifier,
- label = stringResource(id = R.string.txt_card_art)
- ) {
- var expanded by remember { mutableStateOf(false) }
- OutlinedContainerHorizontal(
- modifier = Modifier
- .fillMaxWidth()
- .clickable { expanded = true },
- outlineBrush = gradientFor(currentCardArt)
- ) {
- Box(
- modifier = Modifier
- .size(32.dp)
- .clip(RoundedCornerShape(8.dp))
- .background(gradientFor(currentCardArt), RoundedCornerShape(8.dp)),
- )
- ValueLabel(
- modifier = Modifier
- .padding(horizontal = 12.dp)
- .weight(1f),
- label = stringResource(id = colorNameFor(currentCardArt))
- )
- DropDownIndicator()
- }
- DropdownMenu(
- modifier = Modifier
- .fillMaxWidth(0.8f),
- expanded = expanded,
- onDismissRequest = { expanded = false }
- ) {
- CardArtDropDownRow(
- cardArt = DocumentColor.Green,
- onSelected = {
- onCardArtSelected(DocumentColor.Green)
- expanded = false
- }
- )
- CardArtDropDownRow(
- cardArt = DocumentColor.Yellow,
- onSelected = {
- onCardArtSelected(DocumentColor.Yellow)
- expanded = false
- }
- )
- CardArtDropDownRow(
- cardArt = DocumentColor.Blue,
- onSelected = {
- onCardArtSelected(DocumentColor.Blue)
- expanded = false
- }
- )
- CardArtDropDownRow(
- cardArt = DocumentColor.Red,
- onSelected = {
- onCardArtSelected(DocumentColor.Red)
- expanded = false
- }
- )
- }
- }
-}
-
-@Composable
-private fun DocumentNameInput(
- modifier: Modifier = Modifier,
- value: String,
- onValueChanged: (newValue: String) -> Unit
-) {
- LabeledUserInput(
- modifier = modifier,
- label = stringResource(id = R.string.txt_document_name)
- ) {
- OutlinedContainerHorizontal(modifier = Modifier.fillMaxWidth()) {
- BasicTextField(
- modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = 10.dp),
- textStyle = MaterialTheme.typography.labelMedium.copy(
- color = MaterialTheme.colorScheme.onSurface,
- ),
- value = value,
- onValueChange = onValueChanged,
- cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface)
- )
- }
- }
-}
-
-@Composable
-private fun KeystoreImplementationChooser(
- modifier: Modifier = Modifier,
- currentImplementation: CurrentSecureArea,
- onKeystoreImplementationChanged: (newImplementation: CurrentSecureArea) -> Unit
-) {
- LabeledUserInput(
- modifier = modifier,
- label = stringResource(id = R.string.txt_keystore_implementation)
- ) {
- var expanded by remember { mutableStateOf(false) }
- OutlinedContainerHorizontal(
- modifier = Modifier
- .fillMaxWidth()
- .clickable { expanded = true }
- ) {
- ValueLabel(
- modifier = Modifier.weight(1f),
- label = currentImplementation.displayName
- )
- DropDownIndicator()
- }
- DropdownMenu(
- modifier = Modifier
- .fillMaxWidth(0.8f),
- expanded = expanded,
- onDismissRequest = { expanded = false }
- ) {
- ProvisioningUtil.getInstance(LocalContext.current)
- .secureAreaRepository.implementations.forEach { implementation ->
- TextDropDownRow(
- label = implementation.displayName,
- onSelected = {
- onKeystoreImplementationChanged(implementation.toSecureAreaState())
- expanded = false
- }
- )
- }
- }
- }
-}
-
-@Composable
-private fun CardArtDropDownRow(
- modifier: Modifier = Modifier,
- cardArt: DocumentColor,
- onSelected: () -> Unit
-) {
- DropdownMenuItem(
- text = {
- Row(
- modifier = modifier,
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.spacedBy(12.dp)
- ) {
- Box(
- modifier = Modifier
- .size(32.dp)
- .clip(RoundedCornerShape(8.dp))
- .background(gradientFor(cardArt), RoundedCornerShape(8.dp)),
- )
- ValueLabel(label = stringResource(id = colorNameFor(cardArt)))
- }
- },
- onClick = onSelected
- )
-}
-
-@Composable
-fun OutlinedContainerVertical(
- modifier: Modifier = Modifier,
- outlineBorderWidth: Dp = 2.dp,
- outlineBrush: Brush? = null,
- content: @Composable ColumnScope.() -> Unit
-) {
- val brush = outlineBrush ?: SolidColor(MaterialTheme.colorScheme.outline)
- Row(
- modifier = modifier
- .heightIn(48.dp)
- .clip(RoundedCornerShape(12.dp))
- .border(outlineBorderWidth, brush, RoundedCornerShape(12.dp))
- .background(MaterialTheme.colorScheme.inverseOnSurface),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column(
- modifier = Modifier.padding(horizontal = 12.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- content()
- }
- }
-}
-
-@StringRes
-private fun colorNameFor(cardArt: DocumentColor): Int {
- return when (cardArt) {
- is DocumentColor.Green -> R.string.document_color_green
- is DocumentColor.Yellow -> R.string.document_color_yellow
- is DocumentColor.Blue -> R.string.document_color_blue
- is DocumentColor.Red -> R.string.document_color_red
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedFragment.kt b/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedFragment.kt
deleted file mode 100644
index b642f0a14..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedFragment.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.android.identity.wallet.selfsigned
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.ui.platform.ComposeView
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.wallet.theme.HolderAppTheme
-
-class AddSelfSignedFragment : Fragment() {
-
- private val viewModel: AddSelfSignedViewModel by viewModels()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return ComposeView(requireContext()).apply {
- setContent {
- HolderAppTheme {
- AddSelfSignedDocumentScreen(
- viewModel = viewModel,
- onNext = { onNext() }
- )
- }
- }
- }
- }
-
- private fun onNext() {
- val state = viewModel.screenState.value
- val secureAreaScreenState = requireNotNull(state.secureAreaSupportState)
- val provisionInfo = ProvisionInfo(
- docType = state.documentType,
- docName = state.documentName,
- docColor = state.cardArt.value,
- currentSecureArea = state.currentSecureArea,
- secureAreaSupportState = secureAreaScreenState,
- validityInDays = state.validityInDays,
- minValidityInDays = state.minValidityInDays,
- numberMso = state.numberOfMso,
- maxUseMso = state.maxUseOfMso
- )
- val destination = AddSelfSignedFragmentDirections
- .actionAddSelfSignedToSelfSignedDetails(provisionInfo)
- findNavController().navigate(destination)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedScreenState.kt b/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedScreenState.kt
deleted file mode 100644
index 84e36cc1b..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedScreenState.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.identity.wallet.selfsigned
-
-import android.os.Parcelable
-import com.android.identity.wallet.document.DocumentColor
-import com.android.identity.wallet.support.CurrentSecureArea
-import com.android.identity.wallet.support.SecureAreaSupportState
-import com.android.identity.wallet.support.toSecureAreaState
-import com.android.identity.wallet.util.DocumentData
-import com.android.identity.wallet.util.ProvisioningUtil
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class AddSelfSignedScreenState(
- val documentType: String = DocumentData.MDL_DOCTYPE,
- val cardArt: DocumentColor = DocumentColor.Green,
- val documentName: String = "Driving License",
- val currentSecureArea: CurrentSecureArea = ProvisioningUtil.defaultSecureArea.toSecureAreaState(),
- val numberOfMso: Int = 3,
- val maxUseOfMso: Int = 1,
- val validityInDays: Int = 30,
- val minValidityInDays: Int = 10,
- val secureAreaSupportState: SecureAreaSupportState? = null,
-) : Parcelable
diff --git a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedViewModel.kt
deleted file mode 100644
index fdc52d65a..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/selfsigned/AddSelfSignedViewModel.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.android.identity.wallet.selfsigned
-
-import androidx.lifecycle.SavedStateHandle
-import androidx.lifecycle.ViewModel
-import com.android.identity.wallet.HolderApp
-import com.android.identity.wallet.document.DocumentColor
-import com.android.identity.wallet.support.CurrentSecureArea
-import com.android.identity.wallet.support.SecureAreaSupportState
-import com.android.identity.wallet.util.getState
-import com.android.identity.wallet.util.updateState
-import kotlinx.coroutines.flow.StateFlow
-import java.lang.Integer.max
-
-class AddSelfSignedViewModel(
- private val savedStateHandle: SavedStateHandle
-) : ViewModel() {
-
- val screenState: StateFlow = savedStateHandle.getState(
- AddSelfSignedScreenState()
- )
-
- val documentItems: List =
- HolderApp.documentTypeRepositoryInstance.documentTypes.filter { it.mdocDocumentType != null }
- .map { DocumentItem(it.mdocDocumentType!!.docType, it.displayName) }
-
-
- fun updateDocumentType(newValue: String, newName: String) {
- savedStateHandle.updateState {
- it.copy(documentType = newValue, documentName = newName)
- }
- }
-
- fun updateCardArt(newValue: DocumentColor) {
- savedStateHandle.updateState {
- it.copy(cardArt = newValue)
- }
- }
-
- fun updateDocumentName(newValue: String) {
- savedStateHandle.updateState {
- it.copy(documentName = newValue)
- }
- }
-
- fun updateKeystoreImplementation(newValue: CurrentSecureArea) {
- savedStateHandle.updateState {
- it.copy(currentSecureArea = newValue)
- }
- }
-
- fun updateSecureAreaSupportState(newValue: SecureAreaSupportState) {
- savedStateHandle.updateState {
- it.copy(secureAreaSupportState = newValue)
- }
- }
-
- fun updateValidityInDays(newValue: Int) {
- val state = savedStateHandle.getState(AddSelfSignedScreenState())
- if (newValue < state.value.minValidityInDays) return
- savedStateHandle.updateState {
- it.copy(validityInDays = newValue)
- }
- }
-
- fun updateMinValidityInDays(newValue: Int) {
- if (newValue <= 0) return
- savedStateHandle.updateState {
- val validityDays = max(newValue, it.validityInDays)
- it.copy(minValidityInDays = newValue, validityInDays = validityDays)
- }
- }
-
-
- fun updateNumberOfMso(newValue: Int) {
- if (newValue <= 0) return
- savedStateHandle.updateState {
- it.copy(numberOfMso = newValue)
- }
- }
-
- fun updateMaxUseOfMso(newValue: Int) {
- if (newValue <= 0) return
- savedStateHandle.updateState {
- it.copy(maxUseOfMso = newValue)
- }
- }
-}
diff --git a/appholder/src/main/java/com/android/identity/wallet/selfsigned/DocumentItem.kt b/appholder/src/main/java/com/android/identity/wallet/selfsigned/DocumentItem.kt
deleted file mode 100644
index 1009df77f..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/selfsigned/DocumentItem.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.android.identity.wallet.selfsigned
-
-data class DocumentItem (
- val docType: String,
- val displayName: String
-)
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/selfsigned/SelfSignedDocumentData.kt b/appholder/src/main/java/com/android/identity/wallet/selfsigned/SelfSignedDocumentData.kt
deleted file mode 100644
index 80e34352f..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/selfsigned/SelfSignedDocumentData.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.android.identity.wallet.selfsigned
-
-import android.os.Parcelable
-import com.android.identity.wallet.support.CurrentSecureArea
-import com.android.identity.wallet.util.Field
-import com.android.identity.wallet.support.SecureAreaSupportState
-import kotlinx.parcelize.Parcelize
-
-data class SelfSignedDocumentData(
- val provisionInfo: ProvisionInfo,
- val fields: List
-)
-
-@Parcelize
-data class ProvisionInfo(
- val docType: String,
- var docName: String,
- var docColor: Int,
- val currentSecureArea: CurrentSecureArea,
- val secureAreaSupportState: SecureAreaSupportState,
- val validityInDays: Int,
- val minValidityInDays: Int,
- val numberMso: Int,
- val maxUseMso: Int
-) : Parcelable
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificateDetailsFragment.kt b/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificateDetailsFragment.kt
deleted file mode 100644
index 75ab58dc8..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificateDetailsFragment.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.android.identity.wallet.settings
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.platform.ComposeView
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.wallet.theme.HolderAppTheme
-
-class CaCertificateDetailsFragment : Fragment() {
- private val viewModel: CaCertificatesViewModel by activityViewModels {
- CaCertificatesViewModel.factory()
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return ComposeView(requireContext()).apply {
- setContent {
- val state by viewModel.currentCertificateItem.collectAsState()
- HolderAppTheme {
- CaCertificateDetailsScreen(certificateItem = state,
- onDeleteCertificate = { deleteCertificate() })
- }
- }
- }
- }
-
- private fun deleteCertificate() {
- viewModel.deleteCertificate()
- viewModel.loadCertificates()
- findNavController().popBackStack()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificateDetailsScreen.kt b/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificateDetailsScreen.kt
deleted file mode 100644
index 03ad6e22f..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificateDetailsScreen.kt
+++ /dev/null
@@ -1,160 +0,0 @@
-package com.android.identity.wallet.settings
-
-import android.content.res.Configuration
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.android.identity.wallet.theme.HolderAppTheme
-import java.time.LocalDateTime
-import java.time.ZoneOffset
-import java.util.Date
-
-@Composable
-fun CaCertificateDetailsScreen(
- certificateItem: CertificateItem?,
- onDeleteCertificate: () -> Unit = {}
-) {
- if (certificateItem == null) {
- Title(title = "No certificate provided")
- } else {
- val scrollState = rememberScrollState()
-
- Column(
- modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
-
- Column(
- modifier = Modifier
- .verticalScroll(scrollState)
- .weight(1f)
- .padding(16.dp),
- verticalArrangement = Arrangement.spacedBy(16.dp),
- ) {
- Title(title = certificateItem.title)
- Subtitle(title = "Issued to")
- Line(
- modifier = Modifier,
- text = "Common Name (CN) " + certificateItem.commonNameSubject
- )
- Line(
- modifier = Modifier,
- text = "Organisation (O) " + certificateItem.organisationSubject
- )
- Line(
- modifier = Modifier,
- text = "Organisational Unit (OU) " + certificateItem.organisationalUnitSubject
- )
- Subtitle(title = "Issued by")
- Line(
- modifier = Modifier,
- text = "Common Name (CN) " + certificateItem.commonNameIssuer
- )
- Line(
- modifier = Modifier,
- text = "Organisation (O) " + certificateItem.organisationIssuer
- )
- Line(
- modifier = Modifier,
- text = "Organisational Unit (OU) " + certificateItem.organisationalUnitIssuer
- )
- Subtitle(title = "Fingerprints")
- Line(modifier = Modifier, "SHA-256 fingerprint")
- Line(modifier = Modifier.padding(16.dp), certificateItem.sha255Fingerprint)
- Line(modifier = Modifier, "SHA-1 fingerprint")
- Line(modifier = Modifier.padding(16.dp), certificateItem.sha1Fingerprint)
- if (certificateItem.docTypes.isNotEmpty()){
- Subtitle(title = "Supported mdoc types")
- LazyColumn(
- modifier = Modifier
- .fillMaxWidth()
- .weight(1f),
- contentPadding = PaddingValues(horizontal = 16.dp, vertical = 0.dp),
- verticalArrangement = Arrangement.spacedBy(16.dp)
- ) {
- items(certificateItem.docTypes) { docType ->
- Line(modifier = Modifier, text = docType)
- }
- }
- }
- }
- if (certificateItem.supportsDelete) {
- Button(onClick = onDeleteCertificate) {
- Text(text = "Delete")
- }
- }
- }
- }
-}
-
-@Composable
-fun Title(title: String) {
- Text(
- modifier = Modifier.fillMaxWidth(),
- text = title,
- style = MaterialTheme.typography.titleLarge,
- color = MaterialTheme.colorScheme.onSurface
- )
-}
-
-@Composable
-fun Subtitle(title: String) {
- Text(
- modifier = Modifier.fillMaxWidth(),
- text = title,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface
- )
-}
-
-@Composable
-fun Line(modifier: Modifier, text: String) {
- Text(
- modifier = modifier.fillMaxWidth(),
- text = text,
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onSurface
- )
-}
-
-@Preview(showBackground = true)
-@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-private fun PreviewCaCertificatesScreen() {
- HolderAppTheme {
- CaCertificateDetailsScreen(
- certificateItem = CertificateItem(
- title = "Test 1",
- commonNameSubject = "*.google.com",
- organisationSubject = "",
- organisationalUnitSubject = "",
- commonNameIssuer = "GTS CA 1C3",
- organisationIssuer = "Google Trust Services LLC",
- organisationalUnitIssuer = "",
- notBefore = Date.from(LocalDateTime.now().minusDays(365).toInstant(ZoneOffset.UTC)),
- notAfter = Date.from(LocalDateTime.now().plusDays(365).toInstant(ZoneOffset.UTC)),
- sha255Fingerprint = "03 5C 31 E7 A9 F3 71 2B 27 1C 5A 8D 82 E5 6C 5B 92 BC FC 28 7F72D7 4A B6 9D 61 BF 53 EF 3E 67",
- sha1Fingerprint = "9D 80 9B CF 63 AA86 29 E9 3C 78 9A EA DA 15 56 7E BF 56 D8",
- docTypes = listOf("Doc type 1", "Doc type 2"),
- supportsDelete = true,
- trustPoint = null
- ),
- onDeleteCertificate = {}
- )
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesFragment.kt b/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesFragment.kt
deleted file mode 100644
index 06b201530..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesFragment.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-package com.android.identity.wallet.settings
-
-import android.content.ClipboardManager
-import android.content.Context
-import android.net.Uri
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.compose.runtime.collectAsState
-import androidx.compose.ui.platform.ComposeView
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.crypto.X509Cert
-import com.android.identity.trustmanagement.TrustPoint
-import com.android.identity.wallet.HolderApp
-import com.android.identity.wallet.theme.HolderAppTheme
-import com.android.identity.wallet.trustmanagement.getSubjectKeyIdentifier
-import com.google.android.material.R
-import com.google.android.material.snackbar.Snackbar
-import java.io.ByteArrayInputStream
-import java.security.cert.CertificateException
-import java.security.cert.CertificateFactory
-import java.security.cert.X509Certificate
-
-
-class CaCertificatesFragment : Fragment() {
-
- private val viewModel: CaCertificatesViewModel by activityViewModels {
- CaCertificatesViewModel.factory()
- }
-
- private val browseCertificateLauncher =
- registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris ->
- uris.forEach { uri -> importCertificate(uri) }
- viewModel.loadCertificates()
- }
-
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return ComposeView(requireContext()).apply {
- setContent {
- val state = viewModel.screenState.collectAsState().value
- viewModel.loadCertificates()
- HolderAppTheme {
- CaCertificatesScreen(
- screenState = state,
- onSelectCertificate = {
- viewModel.setCurrentCertificateItem(it)
- openDetails()
- },
- onImportCertificate = { fileDialog() },
- onPasteCertificate = { pasteCertificate() }
- )
- }
- }
- }
- }
-
- private fun openDetails() {
- val destination = CaCertificatesFragmentDirections.toCaCertificateDetails()
- findNavController().navigate(destination)
- }
-
- private fun fileDialog() {
- browseCertificateLauncher.launch(arrayOf("*/*"))
- }
-
- private fun importCertificate(uri: Uri) {
- try {
- this.requireContext().contentResolver.openInputStream(uri).use { inputStream ->
- if (inputStream != null) {
- val certificate = parseCertificate(inputStream.readBytes())
- HolderApp.trustManagerInstance.addTrustPoint(TrustPoint(X509Cert(certificate.encoded)))
- HolderApp.certificateStorageEngineInstance.put(
- certificate.getSubjectKeyIdentifier(),
- certificate.encoded
- )
- }
- }
- } catch (e: Throwable) {
- showException(e)
- }
- }
-
- private fun pasteCertificate() {
- try {
- val clipboard =
- activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
- if (!clipboard.hasPrimaryClip()
- || clipboard.primaryClip?.itemCount == 0
- || clipboard.primaryClip?.getItemAt(0)?.text == null
- ) {
- showMessage("Nothing found to paste")
- return
- }
- val text = clipboard.primaryClip?.getItemAt(0)?.text!!
- val certificate = parseCertificate(text.toString().toByteArray())
- HolderApp.trustManagerInstance.addTrustPoint(TrustPoint(X509Cert(certificate.encoded)))
- HolderApp.certificateStorageEngineInstance.put(
- certificate.getSubjectKeyIdentifier(),
- certificate.encoded
- )
- } catch (e: Throwable) {
- showException(e)
- } finally {
- viewModel.loadCertificates()
- }
- }
-
- private fun showException(exception: Throwable) {
- val message = when (exception) {
- is FileAlreadyExistsException -> "The certificate is already in the mDoc Issuer Trust Store"
- is CertificateException -> "The certificate could not be parsed correctly"
- else -> exception.message
- }
- showMessage(message.toString())
- }
-
- private fun showMessage(message: String) {
- val snackbar = Snackbar.make(
- this.requireView(),
- message,
- Snackbar.LENGTH_LONG
- )
- val snackTextView = snackbar.view.findViewById(R.id.snackbar_text) as TextView
- snackTextView.maxLines = 4
- snackbar.show()
- }
-
- /**
- * Parse a byte array an X509 certificate
- */
- private fun parseCertificate(certificateBytes: ByteArray): X509Certificate {
- return CertificateFactory.getInstance("X509")
- .generateCertificate(ByteArrayInputStream(certificateBytes)) as X509Certificate
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesScreen.kt b/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesScreen.kt
deleted file mode 100644
index 863176bfa..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesScreen.kt
+++ /dev/null
@@ -1,144 +0,0 @@
-package com.android.identity.wallet.settings
-
-import android.content.res.Configuration
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.android.identity.wallet.theme.HolderAppTheme
-import java.time.LocalDateTime
-import java.time.ZoneOffset
-import java.util.Date
-
-@Composable
-fun CaCertificatesScreen(
- screenState: CaCertificatesScreenState,
- onSelectCertificate: (item: CertificateItem) -> Unit,
- onImportCertificate: () -> Unit,
- onPasteCertificate: () -> Unit
-) {
- Column(
- modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- val scrollState = rememberScrollState()
- Column(
- modifier = Modifier
- .verticalScroll(scrollState)
- .weight(1f)
- ) {
- if (screenState.certificates.isEmpty()) {
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .weight(1f)
- .padding(16.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- Text(
- text = "No certificates provided",
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface
- )
- }
- } else {
- LazyColumn(
- modifier = Modifier
- .fillMaxWidth()
- .weight(1f),
- contentPadding = PaddingValues(16.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- items(screenState.certificates) { certificateItem ->
- Text(
- modifier = Modifier.clickable { onSelectCertificate(certificateItem) },
- text = certificateItem.title,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface
- )
- }
- }
- }
- }
- Button(onClick = onImportCertificate) {
- Text(text = "Import")
- }
- Button(onClick = onPasteCertificate) {
- Text(text = "Paste")
- }
- }
-}
-
-@Preview(showBackground = true)
-@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-private fun PreviewCaCertificatesScreen() {
- HolderAppTheme {
- CaCertificatesScreen(
- screenState = CaCertificatesScreenState(
- listOf(
- CertificateItem(
- title = "Test 1",
- commonNameSubject = "*.google.com",
- organisationSubject = "",
- organisationalUnitSubject = "",
- commonNameIssuer = "GTS CA 1C3",
- organisationIssuer = "Google Trust Services LLC",
- organisationalUnitIssuer = "",
- notBefore = Date.from(
- LocalDateTime.now().minusDays(365).toInstant(ZoneOffset.UTC)
- ),
- notAfter = Date.from(
- LocalDateTime.now().plusDays(365).toInstant(ZoneOffset.UTC)
- ),
- sha255Fingerprint = "03 5C 31 E7 A9 F3 71 2B 27 1C 5A 8D 82 E5 6C 5B 92 BC FC 28 7F72D7 4A B6 9D 61 BF 53 EF 3E 67",
- sha1Fingerprint = "9D 80 9B CF 63 AA86 29 E9 3C 78 9A EA DA 15 56 7E BF 56 D8",
- docTypes = emptyList(),
- supportsDelete = false,
- trustPoint = null
- ),
- CertificateItem(
- title = "Test 2",
- commonNameSubject = "*.google.com",
- organisationSubject = "",
- organisationalUnitSubject = "",
- commonNameIssuer = "GTS CA 1C3",
- organisationIssuer = "Google Trust Services LLC",
- organisationalUnitIssuer = "",
- notBefore = Date.from(
- LocalDateTime.now().minusDays(100).toInstant(
- ZoneOffset.UTC
- )
- ),
- notAfter = Date.from(
- LocalDateTime.now().plusDays(100).toInstant(ZoneOffset.UTC)
- ),
- sha255Fingerprint = "03 5C 31 E7 A9 F3 71 2B 27 1C 5A 8D 82 E5 6C 5B 92 BC FC 28 7F72D7 4A B6 9D 61 BF 53 EF 3E 67",
- sha1Fingerprint = "9D 80 9B CF 63 AA86 29 E9 3C 78 9A EA DA 15 56 7E BF 56 D8",
- docTypes = emptyList(),
- supportsDelete = false,
- trustPoint = null
- )
- )
- ),
- onSelectCertificate = {},
- onImportCertificate = {},
- onPasteCertificate = {}
- )
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesScreenState.kt b/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesScreenState.kt
deleted file mode 100644
index b6b8cc21e..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesScreenState.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.identity.wallet.settings
-
-data class CaCertificatesScreenState (
- val certificates: List = emptyList()
-) {
-
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesViewModel.kt
deleted file mode 100644
index abffacc97..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/CaCertificatesViewModel.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.android.identity.wallet.settings
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.viewmodel.initializer
-import androidx.lifecycle.viewmodel.viewModelFactory
-import com.android.identity.crypto.javaX509Certificate
-import com.android.identity.wallet.HolderApp
-import com.android.identity.wallet.trustmanagement.getSubjectKeyIdentifier
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.update
-
-class CaCertificatesViewModel() : ViewModel() {
-
- private val _screenState = MutableStateFlow(CaCertificatesScreenState())
- val screenState: StateFlow = _screenState.asStateFlow()
-
- private val _currentCertificateItem = MutableStateFlow(null)
- val currentCertificateItem = _currentCertificateItem.asStateFlow()
- fun loadCertificates() {
- val certificates =
- HolderApp.trustManagerInstance.getAllTrustPoints().map { it.toCertificateItem() }
- _screenState.update { it.copy(certificates = certificates) }
- }
-
- fun setCurrentCertificateItem(certificateItem: CertificateItem) {
- _currentCertificateItem.update { certificateItem }
- }
-
- fun deleteCertificate() {
- _currentCertificateItem.value?.trustPoint?.let {
- HolderApp.trustManagerInstance.removeTrustPoint(it)
- HolderApp.certificateStorageEngineInstance.delete(
- it.certificate.javaX509Certificate.getSubjectKeyIdentifier()
- )
- }
- }
-
- companion object {
- fun factory(): ViewModelProvider.Factory {
- return viewModelFactory {
- initializer { CaCertificatesViewModel() }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/CertificateItem.kt b/appholder/src/main/java/com/android/identity/wallet/settings/CertificateItem.kt
deleted file mode 100644
index e7d9b1de9..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/CertificateItem.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.android.identity.wallet.settings
-
-import com.android.identity.trustmanagement.TrustPoint
-import java.util.Date
-
-data class CertificateItem(
- val title: String,
- val commonNameSubject: String,
- val organisationSubject: String,
- val organisationalUnitSubject: String,
- val commonNameIssuer: String,
- val organisationIssuer: String,
- val organisationalUnitIssuer: String,
- val notBefore: Date,
- val notAfter: Date,
- val sha255Fingerprint: String,
- val sha1Fingerprint: String,
- val docTypes: List,
- val supportsDelete: Boolean,
- val trustPoint: TrustPoint?
-) {
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/Mappers.kt b/appholder/src/main/java/com/android/identity/wallet/settings/Mappers.kt
deleted file mode 100644
index ed12ad0b5..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/Mappers.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.android.identity.wallet.settings
-
-import com.android.identity.crypto.javaX509Certificate
-import com.android.identity.trustmanagement.TrustPoint
-import com.android.identity.wallet.HolderApp
-import com.android.identity.wallet.trustmanagement.getCommonName
-import com.android.identity.wallet.trustmanagement.getOrganisation
-import com.android.identity.wallet.trustmanagement.getSubjectKeyIdentifier
-import com.android.identity.wallet.trustmanagement.organisationalUnit
-import java.lang.StringBuilder
-import java.security.MessageDigest
-
-fun TrustPoint.toCertificateItem(docTypes: List = emptyList()): CertificateItem {
- val subject = this.certificate.javaX509Certificate.subjectX500Principal
- val issuer = this.certificate.javaX509Certificate.issuerX500Principal
- val sha255Fingerprint = hexWithSpaces(
- MessageDigest.getInstance("SHA-256").digest(
- this.certificate.encodedCertificate
- )
- )
- val sha1Fingerprint = hexWithSpaces(
- MessageDigest.getInstance("SHA-1").digest(
- this.certificate.encodedCertificate
- )
- )
- val defaultValue = ""
-
- return CertificateItem(
- title = subject.name,
- commonNameSubject = subject.getCommonName(defaultValue),
- organisationSubject = subject.getOrganisation(defaultValue),
- organisationalUnitSubject = subject.organisationalUnit(defaultValue),
- commonNameIssuer = issuer.getCommonName(defaultValue),
- organisationIssuer = issuer.getOrganisation(defaultValue),
- organisationalUnitIssuer = issuer.organisationalUnit(defaultValue),
- notBefore = this.certificate.javaX509Certificate.notBefore,
- notAfter = this.certificate.javaX509Certificate.notAfter,
- sha255Fingerprint = sha255Fingerprint,
- sha1Fingerprint = sha1Fingerprint,
- docTypes = docTypes,
- supportsDelete = HolderApp.certificateStorageEngineInstance.get(
- this.certificate.javaX509Certificate.getSubjectKeyIdentifier()
- ) != null,
- trustPoint = this
- )
-}
-
-private fun hexWithSpaces(byteArray: ByteArray): String {
- val stringBuilder = StringBuilder()
- byteArray.forEach {
- if (stringBuilder.isNotEmpty()) {
- stringBuilder.append(" ")
- }
- stringBuilder.append(String.format("%02X", it))
- }
- return stringBuilder.toString()
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsFragment.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsFragment.kt
deleted file mode 100644
index 1919fe1a4..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsFragment.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.android.identity.wallet.settings
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.collectAsState
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ComposeView
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.wallet.theme.HolderAppTheme
-
-class SettingsFragment : Fragment() {
-
- private val settingsViewModel: SettingsViewModel by viewModels()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return ComposeView(requireContext()).apply {
- setContent {
- HolderAppTheme {
- val state = settingsViewModel.settingsState.collectAsState().value
- SettingsScreen(
- modifier = Modifier.fillMaxSize(),
- screenState = state,
- onAutoCloseChanged = settingsViewModel::onConnectionAutoCloseChanged,
- onSessionEncryptionCurveChanged = settingsViewModel::onEphemeralKeyCurveChanged,
- onUseStaticHandoverChanged = settingsViewModel::onUseStaticHandoverChanged,
- onUseL2CAPChanged = settingsViewModel::onL2CAPChanged,
- onBLEDataRetrievalModeChanged = settingsViewModel::onBleDataRetrievalChanged,
- onBLEServiceCacheChanged = settingsViewModel::onBleServiceCacheChanged,
- onBLEPeripheralDataRetrievalModeChanged = settingsViewModel::onBlePeripheralModeChanged,
- onWiFiAwareChanged = settingsViewModel::onWiFiAwareChanged,
- onNfcChanged = settingsViewModel::onNFCChanged,
- onDebugChanged = settingsViewModel::onDebugLoggingChanged,
- onOpenCaCertificates = {openCaCertificates()},
- )
- }
- }
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- settingsViewModel.loadSettings()
- }
-
- private fun openCaCertificates(){
- val destination = SettingsFragmentDirections.toCaCertificates()
- findNavController().navigate(destination)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreen.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreen.kt
deleted file mode 100644
index 5de767056..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreen.kt
+++ /dev/null
@@ -1,296 +0,0 @@
-package com.android.identity.wallet.settings
-
-import android.content.res.Configuration
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowDropDown
-import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Switch
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.android.identity.wallet.composables.curveLabelFor
-import com.android.identity.wallet.theme.HolderAppTheme
-
-@Composable
-fun SettingsScreen(
- modifier: Modifier = Modifier,
- screenState: SettingsScreenState,
- onAutoCloseChanged: (Boolean) -> Unit,
- onSessionEncryptionCurveChanged: (newValue: SettingsScreenState.SessionEncryptionCurveOption) -> Unit,
- onUseStaticHandoverChanged: (Boolean) -> Unit,
- onUseL2CAPChanged: (Boolean) -> Unit,
- onBLEServiceCacheChanged: (Boolean) -> Unit,
- onBLEDataRetrievalModeChanged: (Boolean) -> Unit,
- onBLEPeripheralDataRetrievalModeChanged: (Boolean) -> Unit,
- onWiFiAwareChanged: (Boolean) -> Unit,
- onNfcChanged: (Boolean) -> Unit,
- onDebugChanged: (Boolean) -> Unit,
- onOpenCaCertificates: () -> Unit,
-) {
- Column(modifier = modifier) {
- val scrollState = rememberScrollState()
- Column(
- modifier = Modifier
- .padding(16.dp)
- .verticalScroll(scrollState),
- verticalArrangement = Arrangement.spacedBy(16.dp)
- ) {
- SettingSectionTitle(title = "General")
- SettingToggle(
- title = "Auto close connection",
- subtitleOn = "Close connection after first response",
- subtitleOff = "Don't close connection after first response",
- isChecked = screenState.autoCloseEnabled,
- onCheckedChange = onAutoCloseChanged
- )
- SettingsDropDown(
- title = "Session Encryption Curve",
- description = curveLabelFor(screenState.sessionEncryptionCurveOption.toEcCurve()),
- onCurveChanged = onSessionEncryptionCurveChanged
- )
- SettingSectionTitle(title = "NFC Engagement")
- SettingToggle(
- title = "Use static handover",
- subtitleOn = "Use static handover",
- subtitleOff = "Use negotiated handover",
- isChecked = screenState.useStaticHandover,
- onCheckedChange = onUseStaticHandoverChanged
- )
- SettingSectionTitle(title = "Data retrieval options")
- SettingToggle(
- title = "Use L2CAP if available",
- subtitleOn = "Use L2CAP",
- subtitleOff = "Don't use L2CAP",
- isChecked = screenState.isL2CAPEnabled,
- enabled = screenState.isBleEnabled(),
- onCheckedChange = onUseL2CAPChanged
- )
- SettingToggle(
- title = "Clear BLE Service Cache",
- subtitleOn = "Clean the cache",
- subtitleOff = "Don't clean the cache",
- isChecked = screenState.isBleClearCacheEnabled,
- enabled = screenState.isBleEnabled(),
- onCheckedChange = onBLEServiceCacheChanged
- )
- SettingSectionTitle(title = "Data retrieval methods")
- SettingToggle(
- title = "BLE central client mode",
- subtitleOn = "BLE central client mode activated",
- subtitleOff = "BLE central client mode deactivated",
- isChecked = screenState.isBleDataRetrievalEnabled,
- onCheckedChange = onBLEDataRetrievalModeChanged
- )
- SettingToggle(
- title = "BLE peripheral server mode",
- subtitleOn = "BLE peripheral server mode activated",
- subtitleOff = "BLE peripheral server mode deactivated",
- isChecked = screenState.isBlePeripheralModeEnabled,
- onCheckedChange = onBLEPeripheralDataRetrievalModeChanged
- )
- SettingToggle(
- title = "Wifi Aware",
- subtitleOn = "Wifi Aware transfer activated",
- subtitleOff = "Wifi Aware transfer deactivated",
- isChecked = screenState.wifiAwareEnabled,
- onCheckedChange = onWiFiAwareChanged
- )
- SettingToggle(
- title = "NFC",
- subtitleOn = "NFC transfer activated",
- subtitleOff = "NFC transfer deactivated",
- isChecked = screenState.nfcEnabled,
- onCheckedChange = onNfcChanged
- )
- SettingSectionTitle(title = "Debug logging options")
- SettingToggle(
- title = "Debug",
- subtitleOn = "Debug logging activated",
- subtitleOff = "Debug logging deactivated",
- isChecked = screenState.debugEnabled,
- onCheckedChange = onDebugChanged
- )
- SettingSectionTitle(
- title = "CA Certificates"
- )
- SettingItem(
- modifier = Modifier
- .clickable { onOpenCaCertificates() },
- title = "Show CA Certificates",
- subtitle = "Click here to show the CA Certificates"
- )
- }
- }
-}
-
-@Composable
-private fun SettingSectionTitle(
- modifier: Modifier = Modifier,
- title: String
-) {
- Column(modifier = modifier) {
- Text(
- modifier = Modifier.fillMaxWidth(),
- text = title,
- style = MaterialTheme.typography.titleLarge,
- color = MaterialTheme.colorScheme.onSurface
- )
- }
-}
-
-@Composable
-private fun SettingItem(
- modifier: Modifier = Modifier,
- title: String,
- subtitle: String
-) {
- Row(
- modifier = modifier,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column(modifier = Modifier.fillMaxWidth()) {
- Text(
- text = title,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface
- )
- Text(
- text = subtitle,
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurface
- )
- }
- }
-}
-
-@Composable
-private fun SettingToggle(
- modifier: Modifier = Modifier,
- title: String,
- subtitleOn: String,
- subtitleOff: String,
- isChecked: Boolean,
- onCheckedChange: (Boolean) -> Unit,
- enabled: Boolean = true
-) {
- Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
- Column(modifier = Modifier.weight(1f)) {
- Text(
- text = title,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface
- )
- val subtitle = if (isChecked) subtitleOn else subtitleOff
- Text(
- text = subtitle,
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurface
- )
- }
- Switch(
- checked = isChecked,
- enabled = enabled,
- onCheckedChange = onCheckedChange
- )
- }
-}
-
-@Composable
-private fun SettingsDropDown(
- modifier: Modifier = Modifier,
- title: String,
- description: String,
- onCurveChanged: (selection: SettingsScreenState.SessionEncryptionCurveOption) -> Unit
-) {
- var dropDownExpanded by remember { mutableStateOf(false) }
- val expandDropDown = { dropDownExpanded = true }
- Row(
- modifier = modifier.clickable { expandDropDown() },
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column(modifier = Modifier.weight(1f)) {
- Text(
- text = title,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface
- )
- Text(
- text = description,
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurface
- )
- }
- IconButton(onClick = expandDropDown) {
- Icon(
- imageVector = Icons.Default.ArrowDropDown,
- contentDescription = null,
- tint = MaterialTheme.colorScheme.onSurface
- )
- }
- val entries = SettingsScreenState.SessionEncryptionCurveOption.values().toList()
- DropdownMenu(
- expanded = dropDownExpanded,
- onDismissRequest = { dropDownExpanded = false }
- ) {
- for (entry in entries) {
- DropdownMenuItem(
- modifier = modifier,
- text = {
- Text(
- modifier = Modifier.fillMaxWidth(),
- text = curveLabelFor(curveOption = entry.toEcCurve())
- )
- },
- onClick = {
- onCurveChanged(entry)
- dropDownExpanded = false
- }
- )
- }
- }
- }
-}
-
-
-@Preview(showBackground = true)
-@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-private fun SettingsScreenPreview() {
- HolderAppTheme {
- SettingsScreen(
- modifier = Modifier.fillMaxSize(),
- screenState = SettingsScreenState(),
- onAutoCloseChanged = {},
- onSessionEncryptionCurveChanged = {},
- onUseStaticHandoverChanged = {},
- onUseL2CAPChanged = {},
- onBLEServiceCacheChanged = {},
- onBLEDataRetrievalModeChanged = {},
- onBLEPeripheralDataRetrievalModeChanged = {},
- onWiFiAwareChanged = {},
- onNfcChanged = {},
- onDebugChanged = {},
- onOpenCaCertificates = {}
- )
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreenState.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreenState.kt
deleted file mode 100644
index dacbd9a9e..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreenState.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.android.identity.wallet.settings
-
-import android.os.Parcelable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.Stable
-import com.android.identity.crypto.EcCurve
-import kotlinx.parcelize.Parcelize
-
-@Stable
-@Immutable
-data class SettingsScreenState(
- val autoCloseEnabled: Boolean = true,
- val sessionEncryptionCurveOption: SessionEncryptionCurveOption = SessionEncryptionCurveOption.P256,
- val useStaticHandover: Boolean = true,
- val isL2CAPEnabled: Boolean = false,
- val isBleClearCacheEnabled: Boolean = false,
- val isBleDataRetrievalEnabled: Boolean = true,
- val isBlePeripheralModeEnabled: Boolean = false,
- val wifiAwareEnabled: Boolean = false,
- val nfcEnabled: Boolean = false,
- val debugEnabled: Boolean = true
-) {
-
- fun isBleEnabled(): Boolean = isBleDataRetrievalEnabled || isBlePeripheralModeEnabled
-
- fun canToggleBleDataRetrievalMode(newBleCentralMode: Boolean): Boolean {
- val updatedState = copy(isBleDataRetrievalEnabled = newBleCentralMode)
- return updatedState.hasDataRetrieval()
- }
-
- fun canToggleBlePeripheralMode(newBlePeripheralMode: Boolean): Boolean {
- val updatedState = copy(isBlePeripheralModeEnabled = newBlePeripheralMode)
- return updatedState.hasDataRetrieval()
- }
-
- fun canToggleWifiAware(newWifiAwareValue: Boolean): Boolean {
- val updatedState = copy(wifiAwareEnabled = newWifiAwareValue)
- return updatedState.hasDataRetrieval()
- }
-
- fun canToggleNfc(newNfcValue: Boolean): Boolean {
- val updatedState = copy(nfcEnabled = newNfcValue)
- return updatedState.hasDataRetrieval()
- }
-
- private fun hasDataRetrieval(): Boolean =
- isBleDataRetrievalEnabled
- || isBlePeripheralModeEnabled
- || wifiAwareEnabled
- || nfcEnabled
-
- @Parcelize
- enum class SessionEncryptionCurveOption : Parcelable {
- P256,
- P384,
- P521,
- BrainPoolP256R1,
- BrainPoolP320R1,
- BrainPoolP384R1,
- BrainPoolP512R1,
- X25519,
- X448;
-
- fun toEcCurve(): EcCurve =
- when (this) {
- P256 -> EcCurve.P256
- P384 -> EcCurve.P384
- P521 -> EcCurve.P521
- BrainPoolP256R1 -> EcCurve.BRAINPOOLP256R1
- BrainPoolP320R1 -> EcCurve.BRAINPOOLP320R1
- BrainPoolP384R1 -> EcCurve.BRAINPOOLP384R1
- BrainPoolP512R1 -> EcCurve.BRAINPOOLP512R1
- X25519 -> EcCurve.X25519
- X448 -> EcCurve.X448
- }
-
- companion object {
- fun fromEcCurve(curve: EcCurve): SessionEncryptionCurveOption =
- when (curve) {
- EcCurve.P256 -> P256
- EcCurve.P384 -> P384
- EcCurve.P521 -> P521
- EcCurve.BRAINPOOLP256R1 -> BrainPoolP256R1
- EcCurve.BRAINPOOLP320R1 -> BrainPoolP320R1
- EcCurve.BRAINPOOLP384R1 -> BrainPoolP384R1
- EcCurve.BRAINPOOLP512R1 -> BrainPoolP512R1
- EcCurve.X25519 -> X25519
- EcCurve.X448 -> X448
- else -> throw IllegalStateException("Unknown EcCurve")
- }
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsViewModel.kt
deleted file mode 100644
index 0526dd243..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsViewModel.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.android.identity.wallet.settings
-
-import androidx.lifecycle.ViewModel
-import com.android.identity.wallet.util.PreferencesHelper
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.update
-
-class SettingsViewModel : ViewModel() {
-
- private val mutableSettingsState = MutableStateFlow(SettingsScreenState())
- val settingsState: StateFlow = mutableSettingsState
-
- fun loadSettings() {
- val settingsState = SettingsScreenState(
- autoCloseEnabled = PreferencesHelper.isConnectionAutoCloseEnabled(),
- sessionEncryptionCurveOption = SettingsScreenState.SessionEncryptionCurveOption.fromEcCurve(
- PreferencesHelper.getEphemeralKeyCurveOption()
- ),
- useStaticHandover = PreferencesHelper.shouldUseStaticHandover(),
- isL2CAPEnabled = PreferencesHelper.isBleL2capEnabled(),
- isBleClearCacheEnabled = PreferencesHelper.isBleClearCacheEnabled(),
- isBleDataRetrievalEnabled = PreferencesHelper.isBleDataRetrievalEnabled(),
- isBlePeripheralModeEnabled = PreferencesHelper.isBleDataRetrievalPeripheralModeEnabled(),
- wifiAwareEnabled = PreferencesHelper.isWifiDataRetrievalEnabled(),
- nfcEnabled = PreferencesHelper.isNfcDataRetrievalEnabled(),
- debugEnabled = PreferencesHelper.isDebugLoggingEnabled()
- )
- mutableSettingsState.value = settingsState
- }
-
- fun onConnectionAutoCloseChanged(newValue: Boolean) {
- PreferencesHelper.setConnectionAutoCloseEnabled(newValue)
- mutableSettingsState.update { it.copy(autoCloseEnabled = newValue) }
- }
-
- fun onEphemeralKeyCurveChanged(
- sessionEncryptionCurveOption: SettingsScreenState.SessionEncryptionCurveOption
- ) {
- PreferencesHelper.setEphemeralKeyCurveOption(sessionEncryptionCurveOption.toEcCurve())
- mutableSettingsState.update { it.copy(sessionEncryptionCurveOption = sessionEncryptionCurveOption) }
- }
-
- fun onUseStaticHandoverChanged(newValue: Boolean) {
- PreferencesHelper.setUseStaticHandover(newValue)
- mutableSettingsState.update { it.copy(useStaticHandover = newValue) }
- }
-
- fun onL2CAPChanged(newValue: Boolean) {
- PreferencesHelper.setBleL2CAPEnabled(newValue)
- mutableSettingsState.update { it.copy(isL2CAPEnabled = newValue) }
- }
-
- fun onBleServiceCacheChanged(newValue: Boolean) {
- PreferencesHelper.setBleClearCacheEnabled(newValue)
- mutableSettingsState.update { it.copy(isBleClearCacheEnabled = newValue) }
- }
-
- fun onBleDataRetrievalChanged(newValue: Boolean) {
- val state = mutableSettingsState.value
- if (state.canToggleBleDataRetrievalMode(newValue)) {
- PreferencesHelper.setBleDataRetrievalEnabled(newValue)
- mutableSettingsState.update { it.copy(isBleDataRetrievalEnabled = newValue) }
- }
- }
-
- fun onBlePeripheralModeChanged(newValue: Boolean) {
- val state = mutableSettingsState.value
- if (state.canToggleBlePeripheralMode(newValue)) {
- PreferencesHelper.setBlePeripheralDataRetrievalMode(newValue)
- mutableSettingsState.update { it.copy(isBlePeripheralModeEnabled = newValue) }
- }
- }
-
- fun onWiFiAwareChanged(newValue: Boolean) {
- val state = mutableSettingsState.value
- if (state.canToggleWifiAware(newValue)) {
- PreferencesHelper.setWifiDataRetrievalEnabled(newValue)
- mutableSettingsState.update { it.copy(wifiAwareEnabled = newValue) }
- }
- }
-
- fun onNFCChanged(newValue: Boolean) {
- val state = mutableSettingsState.value
- if (state.canToggleNfc(newValue)) {
- PreferencesHelper.setNfcDataRetrievalEnabled(newValue)
- mutableSettingsState.update { it.copy(nfcEnabled = newValue) }
- }
- }
-
- fun onDebugLoggingChanged(newValue: Boolean) {
- PreferencesHelper.setDebugLoggingEnabled(newValue)
- mutableSettingsState.update { it.copy(debugEnabled = newValue) }
- }
-}
-
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/AndroidKeystoreSecureAreaSupport.kt b/appholder/src/main/java/com/android/identity/wallet/support/AndroidKeystoreSecureAreaSupport.kt
deleted file mode 100644
index f9d518c7b..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/AndroidKeystoreSecureAreaSupport.kt
+++ /dev/null
@@ -1,227 +0,0 @@
-package com.android.identity.wallet.support
-
-import android.os.Handler
-import android.os.Looper
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.fragment.app.Fragment
-import com.android.identity.android.securearea.AndroidKeystoreCreateKeySettings
-import com.android.identity.android.securearea.AndroidKeystoreKeyInfo
-import com.android.identity.android.securearea.AndroidKeystoreKeyUnlockData
-import com.android.identity.android.securearea.AndroidKeystoreSecureArea
-import com.android.identity.android.securearea.UserAuthenticationType
-import com.android.identity.android.securearea.userAuthenticationTypeSet
-import com.android.identity.cbor.Cbor
-import com.android.identity.cbor.CborMap
-import com.android.identity.crypto.Algorithm
-import com.android.identity.securearea.CreateKeySettings
-import com.android.identity.crypto.EcCurve
-import com.android.identity.mdoc.credential.MdocCredential
-import com.android.identity.securearea.KeyPurpose
-import com.android.identity.securearea.KeyUnlockData
-import com.android.identity.wallet.R
-import com.android.identity.wallet.authprompt.UserAuthPromptBuilder
-import com.android.identity.wallet.composables.AndroidSetupContainer
-import com.android.identity.wallet.composables.AuthenticationKeyCurveAndroid
-import com.android.identity.wallet.composables.MdocAuthentication
-import com.android.identity.wallet.support.androidkeystore.AndroidAuthKeyCurveOption
-import com.android.identity.wallet.support.androidkeystore.AndroidAuthKeyCurveState
-import com.android.identity.wallet.composables.state.AuthTypeState
-import com.android.identity.wallet.composables.state.MdocAuthOption
-import kotlinx.datetime.Instant
-
-class AndroidKeystoreSecureAreaSupport(
- private val capabilities: AndroidKeystoreSecureArea.Capabilities
-) : SecureAreaSupport {
-
- private val screenState = AndroidKeystoreSecureAreaSupportState(
- allowLSKFUnlocking = AuthTypeState(true, capabilities.multipleAuthenticationTypesSupported),
- allowBiometricUnlocking = AuthTypeState(true, capabilities.multipleAuthenticationTypesSupported),
- useStrongBox = AuthTypeState(false, capabilities.strongBoxSupported),
- mDocAuthOption = MdocAuthOption(isEnabled = capabilities.keyAgreementSupported),
- authKeyCurveState = AndroidAuthKeyCurveState(isEnabled = capabilities.curve25519Supported)
- )
-
- override fun Fragment.unlockKey(
- credential: MdocCredential,
- onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
- onUnlockFailure: (wasCancelled: Boolean) -> Unit
- ) {
- val keyInfo = credential.secureArea.getKeyInfo(credential.alias) as AndroidKeystoreKeyInfo
- val unlockData = AndroidKeystoreKeyUnlockData(credential.alias)
-
- val allowLskf = keyInfo.userAuthenticationTypes.contains(UserAuthenticationType.LSKF)
- val allowBiometric = keyInfo.userAuthenticationTypes.contains(UserAuthenticationType.BIOMETRIC)
- val allowBoth = keyInfo.userAuthenticationTypes.contains(UserAuthenticationType.LSKF) &&
- keyInfo.userAuthenticationTypes.contains(UserAuthenticationType.BIOMETRIC)
- val allowLskfUnlock = allowLskf || allowBoth
- val allowBiometricUnlock = allowBiometric || allowBoth
- val forceLskf: Boolean = !allowBiometricUnlock
-
- val userAuthRequest = UserAuthPromptBuilder.requestUserAuth(this)
- .withTitle(getString(R.string.bio_auth_title))
- .withSuccessCallback {
- onKeyUnlocked(unlockData)
- }
- .withCancelledCallback {
- if (allowLskfUnlock) {
- val runnable = {
- unlockKey(credential, onKeyUnlocked, onUnlockFailure)
- }
- // Without this delay, the prompt won't reshow
- Handler(Looper.getMainLooper()).postDelayed(runnable, 100)
- } else {
- onUnlockFailure(true)
- }
- }
- .withFailureCallback { onUnlockFailure(false) }
- .setForceLskf(forceLskf)
- if (allowLskfUnlock) {
- userAuthRequest.withNegativeButton(getString(R.string.bio_auth_use_pin))
- } else {
- userAuthRequest.withNegativeButton("Cancel")
- }
- val cryptoObject = unlockData.getCryptoObjectForSigning(Algorithm.ES256)
- userAuthRequest.build().authenticate(cryptoObject)
- }
-
- @Composable
- override fun SecureAreaAuthUi(
- onUiStateUpdated: (newState: SecureAreaSupportState) -> Unit
- ) {
- var compositionState by remember { mutableStateOf(screenState) }
- LaunchedEffect(key1 = compositionState) {
- onUiStateUpdated(compositionState)
- }
- AndroidSetupContainer(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- isOn = compositionState.userAuthentication,
- timeoutSeconds = compositionState.userAuthenticationTimeoutSeconds,
- lskfAuthTypeState = compositionState.allowLSKFUnlocking,
- biometricAuthTypeState = compositionState.allowBiometricUnlocking,
- useStrongBox = compositionState.useStrongBox,
- onUserAuthenticationChanged = {
- compositionState = compositionState.copy(userAuthentication = it)
- },
- onAuthTimeoutChanged = { seconds ->
- if (seconds < 0) return@AndroidSetupContainer
- compositionState = compositionState.copy(userAuthenticationTimeoutSeconds = seconds)
- },
- onLskfAuthChanged = {
- val allowLskfUnlock =
- if (compositionState.allowBiometricUnlocking.isEnabled) it else true
- val newValue = compositionState.allowLSKFUnlocking.copy(isEnabled = allowLskfUnlock)
- compositionState = compositionState.copy(allowLSKFUnlocking = newValue)
- },
- onBiometricAuthChanged = {
- val allowBiometricUnlock =
- if (compositionState.allowLSKFUnlocking.isEnabled) it else true
- val newValue =
- compositionState.allowBiometricUnlocking.copy(isEnabled = allowBiometricUnlock)
- compositionState = compositionState.copy(allowBiometricUnlocking = newValue)
- },
- onStrongBoxChanged = { newValue ->
- val update = compositionState.copy(
- useStrongBox = compositionState.useStrongBox.copy(isEnabled = newValue),
- mDocAuthOption = MdocAuthOption(
- isEnabled = if (newValue) capabilities.strongBoxKeyAgreementSupported else capabilities.keyAgreementSupported
- ),
- authKeyCurveState = AndroidAuthKeyCurveState(
- isEnabled = if (newValue) capabilities.strongBoxCurve25519Supported else capabilities.curve25519Supported
- )
- )
- compositionState = update
- }
- )
- MdocAuthentication(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- state = compositionState.mDocAuthOption,
- onMdocAuthOptionChange = { newValue ->
- val authState = compositionState.mDocAuthOption.copy(mDocAuthentication = newValue)
- compositionState = compositionState.copy(
- mDocAuthOption = authState,
- authKeyCurveState = compositionState.authKeyCurveState.copy(
- authCurve = AndroidAuthKeyCurveOption.P_256
- )
- )
- }
- )
- AuthenticationKeyCurveAndroid(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- state = compositionState.authKeyCurveState,
- mDocAuthState = compositionState.mDocAuthOption,
- onAndroidAuthKeyCurveChanged = {
- val newValue = compositionState.authKeyCurveState.copy(authCurve = it)
- compositionState = compositionState.copy(authKeyCurveState = newValue)
- }
- )
- }
-
- override fun getSecureAreaSupportState(): SecureAreaSupportState {
- return screenState
- }
-
- override fun createAuthKeySettingsConfiguration(secureAreaSupportState: SecureAreaSupportState): ByteArray {
- val state = secureAreaSupportState as AndroidKeystoreSecureAreaSupportState
-
- val userAuthSettings = mutableSetOf()
- if (state.allowLSKFUnlocking.isEnabled) {
- userAuthSettings.add(UserAuthenticationType.LSKF)
- }
- if (state.allowBiometricUnlocking.isEnabled) {
- userAuthSettings.add(UserAuthenticationType.BIOMETRIC)
- }
-
- return Cbor.encode(
- CborMap.builder()
- .put("curve", state.authKeyCurveState.authCurve.toEcCurve().coseCurveIdentifier.toLong())
- .put("purposes", KeyPurpose.encodeSet(setOf(state.mDocAuthOption.mDocAuthentication.toKeyPurpose())))
- .put("userAuthEnabled", state.userAuthentication)
- .put("userAuthTimeoutMillis", state.userAuthenticationTimeoutSeconds.toLong() * 1000L)
- .put("userAuthSettings", UserAuthenticationType.encodeSet(userAuthSettings))
- .put("useStrongBox", state.useStrongBox.isEnabled)
- .end()
- .build()
- )
- }
-
- override fun createAuthKeySettingsFromConfiguration(
- encodedConfiguration: ByteArray,
- challenge: ByteArray,
- validFrom: Instant,
- validUntil: Instant
- ): CreateKeySettings {
- val map = Cbor.decode(encodedConfiguration)
- val curve = EcCurve.fromInt(map["curve"].asNumber.toInt())
- val purposes = KeyPurpose.decodeSet(map["purposes"].asNumber)
- val userAuthEnabled = map["userAuthEnabled"].asBoolean
- val userAuthTimeoutMillis = map["userAuthTimeoutMillis"].asNumber
- val userAuthSettings = map["userAuthSettings"].asNumber
- val useStrongBox = map["useStrongBox"].asBoolean
- return AndroidKeystoreCreateKeySettings.Builder(challenge)
- .setEcCurve(curve)
- .setKeyPurposes(purposes)
- .setValidityPeriod(validFrom, validUntil)
- .setUseStrongBox(useStrongBox)
- .setUserAuthenticationRequired(
- userAuthEnabled,
- userAuthTimeoutMillis,
- userAuthSettings.userAuthenticationTypeSet
- )
- .build()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/AndroidKeystoreSecureAreaSupportState.kt b/appholder/src/main/java/com/android/identity/wallet/support/AndroidKeystoreSecureAreaSupportState.kt
deleted file mode 100644
index 8e75167b2..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/AndroidKeystoreSecureAreaSupportState.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.android.identity.wallet.support
-
-import com.android.identity.wallet.support.androidkeystore.AndroidAuthKeyCurveState
-import com.android.identity.wallet.composables.state.AuthTypeState
-import com.android.identity.wallet.composables.state.MdocAuthOption
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class AndroidKeystoreSecureAreaSupportState(
- override val mDocAuthOption: MdocAuthOption = MdocAuthOption(),
- val userAuthentication: Boolean = true,
- val userAuthenticationTimeoutSeconds: Int = 0,
- val allowLSKFUnlocking: AuthTypeState = AuthTypeState(
- isEnabled = true,
- canBeModified = false
- ),
- val allowBiometricUnlocking: AuthTypeState = AuthTypeState(
- isEnabled = true,
- canBeModified = false
- ),
- val useStrongBox: AuthTypeState = AuthTypeState(
- isEnabled = false,
- canBeModified = false
- ),
- val authKeyCurveState: AndroidAuthKeyCurveState = AndroidAuthKeyCurveState(),
-) : SecureAreaSupportState {
-
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/CurrentSecureArea.kt b/appholder/src/main/java/com/android/identity/wallet/support/CurrentSecureArea.kt
deleted file mode 100644
index 7508e8571..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/CurrentSecureArea.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.android.identity.wallet.support
-
-import android.os.Parcelable
-import com.android.identity.securearea.SecureArea
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class CurrentSecureArea(
- @IgnoredOnParcel val secureArea: SecureArea,
- val identifier: String = secureArea.identifier,
- val displayName: String = secureArea.displayName
-) : Parcelable
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/MdocAuthStateExtensions.kt b/appholder/src/main/java/com/android/identity/wallet/support/MdocAuthStateExtensions.kt
deleted file mode 100644
index 14088a783..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/MdocAuthStateExtensions.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.identity.wallet.support
-
-import com.android.identity.securearea.KeyPurpose
-import com.android.identity.wallet.composables.state.MdocAuthStateOption
-
-fun MdocAuthStateOption.toKeyPurpose(): KeyPurpose =
- if (this == MdocAuthStateOption.ECDSA) {
- KeyPurpose.SIGN
- } else {
- KeyPurpose.AGREE_KEY
- }
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupport.kt b/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupport.kt
deleted file mode 100644
index 35c697d76..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupport.kt
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.android.identity.wallet.support
-
-import android.content.Context
-import androidx.compose.runtime.Composable
-import androidx.fragment.app.Fragment
-import com.android.identity.android.securearea.AndroidKeystoreSecureArea
-import com.android.identity.document.Document
-import com.android.identity.mdoc.credential.MdocCredential
-import com.android.identity.securearea.CreateKeySettings
-import com.android.identity.securearea.KeyUnlockData
-import com.android.identity.securearea.SecureArea
-import com.android.identity.securearea.software.SoftwareSecureArea
-import kotlinx.datetime.Instant
-
-interface SecureAreaSupport {
-
- /**
- * This function should create a composable that will render the portion of the UI
- * for the specific [SecureArea] setup inside the [AddSelfSignedDocumentScreen].
- *
- * The composable should hold and manage its state internally, and expose it through
- * the [onUiStateUpdated] lambda. The state must be an implementation of [SecureAreaSupportState]
- */
- @Composable
- fun SecureAreaAuthUi(onUiStateUpdated: (newState: SecureAreaSupportState) -> Unit)
-
- /**
- * This function should create [SecureArea.KeyUnlockData] based on the incoming
- * [Document.AuthenticationKey]. Its implementation should decide on the mechanism
- * that will do the unlocking (i.e. present a biometric prompts or other sort of UI),
- * and the way the [SecureArea.KeyUnlockData] is created.
- *
- * The function is an extension on a [Fragment] due to the nature of Android and the navigation,
- * so in case of rendering a UI specific for unlocking (like a Biometric Prompt, or a Dialog),
- * there is a provided way to navigate using the [findNavController] function.
- */
- fun Fragment.unlockKey(
- credential: MdocCredential,
- onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
- onUnlockFailure: (wasCancelled: Boolean) -> Unit
- )
-
- /**
- * Should return the current [SecureAreaSupportState] which is used by the composition
- * when rendering the composable UI.
- */
- fun getSecureAreaSupportState(): SecureAreaSupportState
-
- /**
- * Returns a configuration for creating authentication keys which can be persisted to disk
- * and passed to [createAuthKeySettingsFromConfiguration] every time new authentication keys
- * need to be created.
- */
- fun createAuthKeySettingsConfiguration(secureAreaSupportState: SecureAreaSupportState): ByteArray
-
- /**
- * Creates a [SecureArea.CreateKeySettings] instead based on the settings previously created
- * with [createAuthKeySettingsConfiguration] and the given challenge and validity period.
- */
- fun createAuthKeySettingsFromConfiguration(
- encodedConfiguration: ByteArray,
- challenge: ByteArray,
- validFrom: Instant,
- validUntil: Instant
- ): CreateKeySettings
-
- companion object {
- fun getInstance(
- context: Context,
- currentSecureArea: CurrentSecureArea
- ): SecureAreaSupport {
- return when (currentSecureArea.secureArea) {
- is AndroidKeystoreSecureArea -> {
- val capabilities = AndroidKeystoreSecureArea.Capabilities(context)
- AndroidKeystoreSecureAreaSupport(capabilities)
- }
-
- is SoftwareSecureArea -> SoftwareKeystoreSecureAreaSupport()
-
- else -> SecureAreaSupportNull()
- }
- }
-
- fun getInstance(
- context: Context,
- secureArea: SecureArea,
- ): SecureAreaSupport {
- return when (secureArea) {
- is AndroidKeystoreSecureArea -> {
- val capabilities = AndroidKeystoreSecureArea.Capabilities(context)
- AndroidKeystoreSecureAreaSupport(capabilities)
- }
-
- is SoftwareSecureArea -> SoftwareKeystoreSecureAreaSupport()
-
- else -> SecureAreaSupportNull()
- }
- }
- }
-}
-
-/**
- * Utility function to convert a [SecureArea] implementation into a corresponding state
- * used by the Jetpack Compose composition when rendering the UI.
- */
-fun SecureArea.toSecureAreaState(): CurrentSecureArea {
- return CurrentSecureArea(this)
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportNull.kt b/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportNull.kt
deleted file mode 100644
index 9467ac0f6..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportNull.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.android.identity.wallet.support
-
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.fragment.app.Fragment
-import com.android.identity.mdoc.credential.MdocCredential
-import com.android.identity.securearea.CreateKeySettings
-import com.android.identity.securearea.KeyUnlockData
-import com.android.identity.wallet.selfsigned.OutlinedContainerVertical
-import kotlinx.datetime.Instant
-
-class SecureAreaSupportNull : SecureAreaSupport {
-
- private val state = SecureAreaSupportStateNull()
-
- @Composable
- override fun SecureAreaAuthUi(onUiStateUpdated: (newState: SecureAreaSupportState) -> Unit) {
- val compositionState by remember { mutableStateOf(state) }
- LaunchedEffect(key1 = compositionState) {
- onUiStateUpdated(compositionState)
- }
- OutlinedContainerVertical(
- modifier = Modifier.padding(horizontal = 16.dp)
- ) {
- Text(
- modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp),
- text = "The selected Secure Area lacks dedicated support so no additional options are available. " +
- "Please add a SecureAreaSupport-derived class.",
- style = MaterialTheme.typography.bodySmall
- )
- }
- }
-
- override fun Fragment.unlockKey(
- credential: MdocCredential,
- onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
- onUnlockFailure: (wasCancelled: Boolean) -> Unit
- ) {
- throw IllegalStateException("No implementation")
- }
-
- override fun getSecureAreaSupportState(): SecureAreaSupportState = state
-
- override fun createAuthKeySettingsConfiguration(secureAreaSupportState: SecureAreaSupportState): ByteArray =
- ByteArray(0)
-
- override fun createAuthKeySettingsFromConfiguration(
- encodedConfiguration: ByteArray,
- challenge: ByteArray,
- validFrom: Instant,
- validUntil: Instant
- ): CreateKeySettings = CreateKeySettings()
-}
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportState.kt b/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportState.kt
deleted file mode 100644
index 864f32447..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportState.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.android.identity.wallet.support
-
-import android.os.Parcelable
-import com.android.identity.wallet.composables.state.MdocAuthOption
-
-interface SecureAreaSupportState : Parcelable {
- val mDocAuthOption: MdocAuthOption
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportStateNull.kt b/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportStateNull.kt
deleted file mode 100644
index 74a419f7e..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/SecureAreaSupportStateNull.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.identity.wallet.support
-
-import com.android.identity.wallet.composables.state.MdocAuthOption
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-class SecureAreaSupportStateNull : SecureAreaSupportState {
-
- override val mDocAuthOption: MdocAuthOption
- get() = MdocAuthOption()
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/SoftwareKeystoreSecureAreaSupport.kt b/appholder/src/main/java/com/android/identity/wallet/support/SoftwareKeystoreSecureAreaSupport.kt
deleted file mode 100644
index 040ca0d28..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/SoftwareKeystoreSecureAreaSupport.kt
+++ /dev/null
@@ -1,154 +0,0 @@
-package com.android.identity.wallet.support
-
-import android.os.Handler
-import android.os.Looper
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import androidx.navigation.fragment.findNavController
-import co.nstant.`in`.cbor.CborBuilder
-import com.android.identity.cbor.Cbor
-import com.android.identity.securearea.CreateKeySettings
-import com.android.identity.crypto.EcCurve
-import com.android.identity.mdoc.credential.MdocCredential
-import com.android.identity.securearea.KeyPurpose
-import com.android.identity.securearea.KeyUnlockData
-import com.android.identity.securearea.software.SoftwareCreateKeySettings
-import com.android.identity.securearea.software.SoftwareKeyUnlockData
-import com.android.identity.wallet.authconfirmation.AuthConfirmationFragmentDirections
-import com.android.identity.wallet.authconfirmation.PassphraseAuthResult
-import com.android.identity.wallet.authconfirmation.PassphrasePromptViewModel
-import com.android.identity.wallet.composables.AuthenticationKeyCurveSoftware
-import com.android.identity.wallet.composables.MdocAuthentication
-import com.android.identity.wallet.composables.SoftwareSetupContainer
-import com.android.identity.wallet.support.softwarekeystore.SoftwareAuthKeyCurveOption
-import com.android.identity.wallet.util.FormatUtil
-import kotlinx.coroutines.launch
-import kotlinx.datetime.Instant
-
-class SoftwareKeystoreSecureAreaSupport : SecureAreaSupport {
-
- private val screenState = SoftwareKeystoreSecureAreaSupportState()
-
- override fun Fragment.unlockKey(
- credential: MdocCredential,
- onKeyUnlocked: (unlockData: KeyUnlockData?) -> Unit,
- onUnlockFailure: (wasCancelled: Boolean) -> Unit
- ) {
- val viewModel: PassphrasePromptViewModel by activityViewModels()
- var didAttemptToUnlock = false
-
- viewLifecycleOwner.lifecycleScope.launch {
- viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
- viewModel.authorizationState.collect { value ->
- if (value is PassphraseAuthResult.Success) {
- val keyUnlockData = SoftwareKeyUnlockData(value.userPassphrase)
- didAttemptToUnlock = true
- onKeyUnlocked(keyUnlockData)
- viewModel.reset()
- }
- }
- }
- }
- val destination = AuthConfirmationFragmentDirections.openPassphrasePrompt(
- showIncorrectPassword = didAttemptToUnlock
- )
- val runnable = { findNavController().navigate(destination) }
- // The system needs a little time to get back to this screen
- Handler(Looper.getMainLooper()).postDelayed(runnable, 500)
- }
-
- @Composable
- override fun SecureAreaAuthUi(
- onUiStateUpdated: (newState: SecureAreaSupportState) -> Unit
- ) {
- var compositionState by remember { mutableStateOf(screenState) }
- LaunchedEffect(key1 = compositionState) {
- onUiStateUpdated(compositionState)
- }
- SoftwareSetupContainer(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- passphrase = compositionState.passphrase,
- onPassphraseChanged = {
- compositionState = compositionState.copy(passphrase = it)
- }
- )
- MdocAuthentication(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- state = compositionState.mDocAuthOption,
- onMdocAuthOptionChange = {
- val newValue = compositionState.mDocAuthOption.copy(mDocAuthentication = it)
- compositionState = compositionState.copy(
- mDocAuthOption = newValue,
- softwareAuthKeyCurveState = compositionState.softwareAuthKeyCurveState.copy(
- authCurve = SoftwareAuthKeyCurveOption.P256
- )
- )
- }
- )
- AuthenticationKeyCurveSoftware(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- state = compositionState.softwareAuthKeyCurveState,
- mDocAuthState = compositionState.mDocAuthOption,
- onSoftwareAuthKeyCurveChanged = {
- val newValue = compositionState.authKeyCurve.copy(authCurve = it)
- compositionState = compositionState.copy(softwareAuthKeyCurveState = newValue)
- }
- )
- }
-
- override fun getSecureAreaSupportState(): SecureAreaSupportState {
- return screenState
- }
-
- override fun createAuthKeySettingsConfiguration(secureAreaSupportState: SecureAreaSupportState): ByteArray {
- val state = secureAreaSupportState as SoftwareKeystoreSecureAreaSupportState
- return FormatUtil.cborEncode(
- CborBuilder()
- .addMap()
- .put("curve", state.softwareAuthKeyCurveState.authCurve.toEcCurve().coseCurveIdentifier.toLong())
- .put("purposes", KeyPurpose.encodeSet(
- setOf(state.mDocAuthOption.mDocAuthentication.toKeyPurpose())).toLong())
- .put("passphraseRequired", state.passphrase.isNotEmpty())
- .put("passphrase", state.passphrase)
- .end()
- .build().get(0))
- }
-
- override fun createAuthKeySettingsFromConfiguration(
- encodedConfiguration: ByteArray,
- challenge: ByteArray,
- validFrom: Instant,
- validUntil: Instant
- ): CreateKeySettings {
- val map = Cbor.decode(encodedConfiguration)
- val curve = EcCurve.fromInt(map["curve"].asNumber.toInt())
- val purposes = KeyPurpose.decodeSet(map["purposes"].asNumber)
- val passphraseRequired = map["passphraseRequired"].asBoolean
- val passphrase = map["passphrase"].asTstr
- return SoftwareCreateKeySettings.Builder()
- .setEcCurve(curve)
- .setKeyPurposes(purposes)
- .setValidityPeriod(validFrom, validUntil)
- .setPassphraseRequired(passphraseRequired, passphrase, null)
- .build()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/SoftwareKeystoreSecureAreaSupportState.kt b/appholder/src/main/java/com/android/identity/wallet/support/SoftwareKeystoreSecureAreaSupportState.kt
deleted file mode 100644
index 43b8487a4..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/SoftwareKeystoreSecureAreaSupportState.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.identity.wallet.support
-
-import com.android.identity.wallet.support.softwarekeystore.SoftwareAuthKeyCurveState
-import com.android.identity.wallet.composables.state.MdocAuthOption
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class SoftwareKeystoreSecureAreaSupportState(
- override val mDocAuthOption: MdocAuthOption = MdocAuthOption(),
- val softwareAuthKeyCurveState: SoftwareAuthKeyCurveState = SoftwareAuthKeyCurveState(),
- val passphrase: String = "",
- val authKeyCurve: SoftwareAuthKeyCurveState = SoftwareAuthKeyCurveState(),
-) : SecureAreaSupportState {
-
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/androidkeystore/AndroidAuthKeyCurveOption.kt b/appholder/src/main/java/com/android/identity/wallet/support/androidkeystore/AndroidAuthKeyCurveOption.kt
deleted file mode 100644
index 7f56aefa5..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/androidkeystore/AndroidAuthKeyCurveOption.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.android.identity.wallet.support.androidkeystore
-
-import android.os.Parcelable
-import com.android.identity.crypto.EcCurve
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-enum class AndroidAuthKeyCurveOption : Parcelable {
- P_256, Ed25519, X25519;
-
- fun toEcCurve(): EcCurve =
- when (this) {
- P_256 -> EcCurve.P256
- Ed25519 -> EcCurve.ED25519
- X25519 -> EcCurve.X25519
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/androidkeystore/AndroidAuthKeyCurveState.kt b/appholder/src/main/java/com/android/identity/wallet/support/androidkeystore/AndroidAuthKeyCurveState.kt
deleted file mode 100644
index 80d8cbde8..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/androidkeystore/AndroidAuthKeyCurveState.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.android.identity.wallet.support.androidkeystore
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class AndroidAuthKeyCurveState(
- val isEnabled: Boolean = true,
- val authCurve: AndroidAuthKeyCurveOption = AndroidAuthKeyCurveOption.P_256
-) : Parcelable
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/softwarekeystore/SoftwareAuthKeyCurveOption.kt b/appholder/src/main/java/com/android/identity/wallet/support/softwarekeystore/SoftwareAuthKeyCurveOption.kt
deleted file mode 100644
index aea270074..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/softwarekeystore/SoftwareAuthKeyCurveOption.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.android.identity.wallet.support.softwarekeystore
-
-import android.os.Parcelable
-import com.android.identity.crypto.EcCurve
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-enum class SoftwareAuthKeyCurveOption : Parcelable {
- P256,
- P384,
- P521,
- BrainPoolP256R1,
- BrainPoolP320R1,
- BrainPoolP384R1,
- BrainPoolP512R1,
- Ed25519,
- Ed448,
- X25519,
- X448;
-
- fun toEcCurve(): EcCurve =
- when (this) {
- P256 -> EcCurve.P256
- P384 -> EcCurve.P384
- P521 -> EcCurve.P521
- BrainPoolP256R1 -> EcCurve.BRAINPOOLP256R1
- BrainPoolP320R1 -> EcCurve.BRAINPOOLP320R1
- BrainPoolP384R1 -> EcCurve.BRAINPOOLP384R1
- BrainPoolP512R1 -> EcCurve.BRAINPOOLP512R1
- Ed25519 -> EcCurve.ED25519
- Ed448 -> EcCurve.ED448
- X25519 -> EcCurve.X25519
- X448 -> EcCurve.X448
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/support/softwarekeystore/SoftwareAuthKeyCurveState.kt b/appholder/src/main/java/com/android/identity/wallet/support/softwarekeystore/SoftwareAuthKeyCurveState.kt
deleted file mode 100644
index f7004ee44..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/support/softwarekeystore/SoftwareAuthKeyCurveState.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.android.identity.wallet.support.softwarekeystore
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class SoftwareAuthKeyCurveState(
- val isEnabled: Boolean = true,
- val authCurve: SoftwareAuthKeyCurveOption = SoftwareAuthKeyCurveOption.P256
-) : Parcelable
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/theme/Color.kt b/appholder/src/main/java/com/android/identity/wallet/theme/Color.kt
deleted file mode 100644
index 3cd8bccb9..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/theme/Color.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.android.identity.wallet.theme
-
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-
-//Light Theme
-val ThemeLightPrimary = Color(0xFF476810)
-val ThemeLightOnPrimary = Color(0xFFFFFFFF)
-val ThemeLightPrimaryContainer = Color(0xFFC7F089)
-
-//Dark Theme
-val ThemeDarkPrimary = Color(0xFFACD370)
-val ThemeDarkOnPrimary = Color(0xFF213600)
-val ThemeDarkPrimaryContainer = Color(0xFF324F00)
-
-val GreenLight = Color(0xFF00E676)
-val GreenDark = Color(0xFF34A853)
-val GreenGradient = Brush.linearGradient(colors = listOf(GreenDark, GreenLight))
-
-val BlueLight = Color(0xFF00B0FF)
-val BlueDark = Color(0xFF4285F4)
-val BlueGradient = Brush.linearGradient(colors = listOf(BlueDark, BlueLight))
-
-val YellowLight = Color(0xFFFFEA00)
-val YellowDark = Color(0xFFFBBC05)
-val YellowGradient = Brush.linearGradient(colors = listOf(YellowDark, YellowLight))
-
-val RedLight = Color(0xFFFF5722)
-val RedDark = Color(0xFFF44336)
-val RedGradient = Brush.linearGradient(colors = listOf(RedDark, RedLight))
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/theme/Shape.kt b/appholder/src/main/java/com/android/identity/wallet/theme/Shape.kt
deleted file mode 100644
index 6edaebc19..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/theme/Shape.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.identity.wallet.theme
-
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.Shapes
-import androidx.compose.ui.unit.dp
-
-val Shapes = Shapes(
- small = RoundedCornerShape(4.dp),
- medium = RoundedCornerShape(4.dp),
- large = RoundedCornerShape(0.dp)
-)
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/theme/Theme.kt b/appholder/src/main/java/com/android/identity/wallet/theme/Theme.kt
deleted file mode 100644
index d5c9b31d8..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/theme/Theme.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.android.identity.wallet.theme
-
-import android.os.Build
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
-import androidx.compose.material3.lightColorScheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
-
-private val lightColorPalette = lightColorScheme(
- primary = ThemeLightPrimary,
- onPrimary = ThemeLightOnPrimary,
- primaryContainer = ThemeLightPrimaryContainer,
- onPrimaryContainer = ThemeLightPrimaryContainer,
-)
-
-private val darkColorPalette = darkColorScheme(
- primary = ThemeDarkPrimary,
- onPrimary = ThemeDarkOnPrimary,
- primaryContainer = ThemeDarkPrimaryContainer,
- onPrimaryContainer = ThemeDarkPrimaryContainer,
-)
-
-@Composable
-fun HolderAppTheme(
- darkTheme: Boolean = isSystemInDarkTheme(),
- content: @Composable () -> Unit
-) {
-
- val darkColorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- dynamicDarkColorScheme(LocalContext.current)
- } else {
- darkColorPalette
- }
-
- val lightColorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- dynamicLightColorScheme(LocalContext.current)
- } else {
- lightColorPalette
- }
-
- val colors = when {
- darkTheme -> darkColorScheme
- else -> lightColorScheme
- }
-
- MaterialTheme(
- colorScheme = colors,
- typography = Typography,
- shapes = Shapes,
- content = content
- )
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/theme/Type.kt b/appholder/src/main/java/com/android/identity/wallet/theme/Type.kt
deleted file mode 100644
index d6341d7b3..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/theme/Type.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.android.identity.wallet.theme
-
-import androidx.compose.material3.Typography
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.sp
-
-val Typography = Typography(
- bodyLarge = TextStyle(
- fontWeight = FontWeight.Bold,
- fontSize = 36.sp
- ),
- bodyMedium = TextStyle(
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- ),
- bodySmall = TextStyle(
- fontWeight = FontWeight.Normal,
- fontSize = 14.sp,
- )
-)
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/AddDocumentToResponseResult.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/AddDocumentToResponseResult.kt
deleted file mode 100644
index f73ff598f..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/transfer/AddDocumentToResponseResult.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.android.identity.wallet.transfer
-
-import com.android.identity.mdoc.credential.MdocCredential
-
-sealed class AddDocumentToResponseResult {
-
- data class DocumentAdded(
- val signingKeyUsageLimitPassed: Boolean
- ) : AddDocumentToResponseResult()
-
- data class DocumentLocked(
- val credential: MdocCredential
- ) : AddDocumentToResponseResult()
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/Communication.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/Communication.kt
deleted file mode 100644
index 908e273e2..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/transfer/Communication.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.android.identity.wallet.transfer
-
-import android.annotation.SuppressLint
-import android.content.Context
-import com.android.identity.util.Constants
-import com.android.identity.mdoc.request.DeviceRequestParser
-import com.android.identity.android.mdoc.deviceretrieval.DeviceRetrievalHelper
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.util.mainExecutor
-import java.util.OptionalLong
-
-class Communication private constructor(
- private val context: Context,
-) {
-
- private var request: DeviceRequest? = null
- var deviceRetrievalHelper: DeviceRetrievalHelper? = null
-
- fun setDeviceRequest(deviceRequest: ByteArray) {
- this.request = DeviceRequest(deviceRequest)
- }
-
- fun getDeviceRequest(): DeviceRequestParser.DeviceRequest =
- request?.let { requestBytes ->
- deviceRetrievalHelper?.let { presentation ->
- DeviceRequestParser(
- requestBytes.value,
- presentation.sessionTranscript
- ).run { parse() }
- } ?: throw IllegalStateException("Presentation not set")
- } ?: throw IllegalStateException("Request not received")
-
-
- fun getSessionTranscript(): ByteArray? = deviceRetrievalHelper?.sessionTranscript
-
- fun sendResponse(deviceResponse: ByteArray, closeAfterSending: Boolean) {
- if (closeAfterSending) {
- deviceRetrievalHelper?.sendDeviceResponse(
- deviceResponse,
- Constants.SESSION_DATA_STATUS_SESSION_TERMINATION)
- deviceRetrievalHelper?.disconnect()
- } else {
- deviceRetrievalHelper?.sendDeviceResponse(deviceResponse, null)
- }
- }
-
- fun stopPresentation(
- sendSessionTerminationMessage: Boolean,
- useTransportSpecificSessionTermination: Boolean
- ) {
- if (sendSessionTerminationMessage) {
- if (useTransportSpecificSessionTermination) {
- deviceRetrievalHelper?.sendTransportSpecificTermination()
- } else {
- deviceRetrievalHelper?.sendDeviceResponse(
- null,
- Constants.SESSION_DATA_STATUS_SESSION_TERMINATION
- )
- }
- }
- disconnect()
- }
-
- fun disconnect() =
- try {
- request = null
- deviceRetrievalHelper?.disconnect()
- } catch (e: RuntimeException) {
- log("Error ignored closing presentation", e)
- }
-
- companion object {
-
- @SuppressLint("StaticFieldLeak")
- @Volatile
- private var instance: Communication? = null
-
- fun getInstance(context: Context): Communication {
- return instance ?: synchronized(this) {
- instance ?: Communication(context).also { instance = it }
- }
- }
- }
-
- @JvmInline
- value class DeviceRequest(val value: ByteArray)
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/ConnectionSetup.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/ConnectionSetup.kt
deleted file mode 100644
index d15b5da13..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/transfer/ConnectionSetup.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.android.identity.wallet.transfer
-
-import android.content.Context
-import com.android.identity.mdoc.connectionmethod.ConnectionMethod
-import com.android.identity.mdoc.connectionmethod.ConnectionMethodBle
-import com.android.identity.mdoc.connectionmethod.ConnectionMethodNfc
-import com.android.identity.mdoc.connectionmethod.ConnectionMethodWifiAware
-import com.android.identity.android.mdoc.transport.DataTransportOptions
-import com.android.identity.util.UUID
-import com.android.identity.wallet.util.PreferencesHelper
-import java.util.ArrayList
-import java.util.OptionalLong
-
-class ConnectionSetup(
- private val context: Context
-) {
-
- fun getConnectionOptions(): DataTransportOptions {
- val builder = DataTransportOptions.Builder()
- .setBleUseL2CAP(PreferencesHelper.isBleL2capEnabled())
- .setBleClearCache(PreferencesHelper.isBleClearCacheEnabled())
- return builder.build()
- }
-
- fun getConnectionMethods(): List {
- val connectionMethods = ArrayList()
- if (PreferencesHelper.isBleDataRetrievalEnabled()) {
- connectionMethods.add(
- ConnectionMethodBle(
- false,
- true,
- null,
- UUID.randomUUID()
- )
- )
- }
- if (PreferencesHelper.isBleDataRetrievalPeripheralModeEnabled()) {
- connectionMethods.add(
- ConnectionMethodBle(
- true,
- false,
- UUID.randomUUID(),
- null
- )
- )
- }
- if (PreferencesHelper.isWifiDataRetrievalEnabled()) {
- connectionMethods.add(
- ConnectionMethodWifiAware(
- null,
- null,
- null,
- null
- )
- )
- }
- if (PreferencesHelper.isNfcDataRetrievalEnabled()) {
- // TODO: Add API to ConnectionMethodNfc to get sizes appropriate for the device
- connectionMethods.add(
- ConnectionMethodNfc(
- 0xffff,
- 0x10000
- )
- )
- }
- return connectionMethods
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/QrCommunicationSetup.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/QrCommunicationSetup.kt
deleted file mode 100644
index 82e1a6402..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/transfer/QrCommunicationSetup.kt
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.android.identity.wallet.transfer
-
-import android.content.Context
-import com.android.identity.android.mdoc.deviceretrieval.DeviceRetrievalHelper
-import com.android.identity.android.mdoc.engagement.QrEngagementHelper
-import com.android.identity.android.mdoc.transport.DataTransport
-import com.android.identity.crypto.Crypto
-import com.android.identity.crypto.EcPublicKey
-import com.android.identity.wallet.util.PreferencesHelper
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.util.mainExecutor
-
-class QrCommunicationSetup(
- private val context: Context,
- private val onConnecting: () -> Unit,
- private val onDeviceRetrievalHelperReady: (deviceRetrievalHelper: DeviceRetrievalHelper) -> Unit,
- private val onNewDeviceRequest: (request: ByteArray) -> Unit,
- private val onDisconnected: (transportSpecificTermination: Boolean) -> Unit,
- private val onCommunicationError: (error: Throwable) -> Unit,
-) {
-
- private val settings = PreferencesHelper.apply { initialize(context) }
- private val connectionSetup = ConnectionSetup(context)
- private val eDeviceKey = Crypto.createEcPrivateKey(settings.getEphemeralKeyCurveOption())
-
- private var deviceRetrievalHelper: DeviceRetrievalHelper? = null
- private lateinit var qrEngagement: QrEngagementHelper
-
- val deviceEngagementUriEncoded: String
- get() = qrEngagement.deviceEngagementUriEncoded
-
- private val qrEngagementListener = object : QrEngagementHelper.Listener {
-
- override fun onDeviceConnecting() {
- log("QR Engagement: Device Connecting")
- onConnecting()
- }
-
- override fun onDeviceConnected(transport: DataTransport) {
- if (deviceRetrievalHelper != null) {
- log("OnDeviceConnected for QR engagement -> ignoring due to active presentation")
- return
- }
- log("OnDeviceConnected via QR: qrEngagement=$qrEngagement")
- val builder = DeviceRetrievalHelper.Builder(
- context,
- deviceRetrievalHelperListener,
- context.mainExecutor(),
- eDeviceKey
- )
- builder.useForwardEngagement(
- transport,
- qrEngagement.deviceEngagement,
- qrEngagement.handover
- )
- deviceRetrievalHelper = builder.build()
- qrEngagement.close()
- onDeviceRetrievalHelperReady(requireNotNull(deviceRetrievalHelper))
- }
-
- override fun onError(error: Throwable) {
- log("QR onError: ${error.message}")
- onCommunicationError(error)
- }
- }
-
- private val deviceRetrievalHelperListener = object : DeviceRetrievalHelper.Listener {
- override fun onEReaderKeyReceived(eReaderKey: EcPublicKey) {
- log("DeviceRetrievalHelper Listener (QR): OnEReaderKeyReceived")
- }
-
- override fun onDeviceRequest(deviceRequestBytes: ByteArray) {
- log("DeviceRetrievalHelper Listener (QR): OnDeviceRequest")
- onNewDeviceRequest(deviceRequestBytes)
- }
-
- override fun onDeviceDisconnected(transportSpecificTermination: Boolean) {
- log("DeviceRetrievalHelper Listener (QR): onDeviceDisconnected")
- onDisconnected(transportSpecificTermination)
- }
-
- override fun onError(error: Throwable) {
- log("DeviceRetrievalHelper Listener (QR): onError -> ${error.message}")
- onCommunicationError(error)
- }
- }
-
- fun configure() {
- qrEngagement = QrEngagementHelper.Builder(
- context,
- eDeviceKey.publicKey,
- connectionSetup.getConnectionOptions(),
- qrEngagementListener,
- context.mainExecutor()
- ).setConnectionMethods(connectionSetup.getConnectionMethods())
- .build()
- }
-
- fun close() {
- try {
- qrEngagement.close()
- } catch (exception: RuntimeException) {
- log("Error closing QR engagement", exception)
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/ReverseQrCommunicationSetup.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/ReverseQrCommunicationSetup.kt
deleted file mode 100644
index df51050be..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/transfer/ReverseQrCommunicationSetup.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.android.identity.wallet.transfer
-
-import android.content.Context
-import android.net.Uri
-import android.util.Base64
-import com.android.identity.android.mdoc.deviceretrieval.DeviceRetrievalHelper
-import com.android.identity.android.mdoc.transport.DataTransport
-import com.android.identity.crypto.Crypto
-import com.android.identity.crypto.EcPublicKey
-import com.android.identity.mdoc.engagement.EngagementParser
-import com.android.identity.mdoc.origininfo.OriginInfo
-import com.android.identity.wallet.util.PreferencesHelper
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.util.mainExecutor
-
-class ReverseQrCommunicationSetup(
- private val context: Context,
- private val onPresentationReady: (presentation: DeviceRetrievalHelper) -> Unit,
- private val onNewRequest: (request: ByteArray) -> Unit,
- private val onDisconnected: () -> Unit,
- private val onCommunicationError: (error: Throwable) -> Unit,
-) {
-
- private val settings = PreferencesHelper.apply { initialize(context) }
- private val connectionSetup = ConnectionSetup(context)
- private val eDeviceKey = Crypto.createEcPrivateKey(settings.getEphemeralKeyCurveOption())
-
- private var presentation: DeviceRetrievalHelper? = null
-
- private val presentationListener = object : DeviceRetrievalHelper.Listener {
- override fun onEReaderKeyReceived(eReaderKey: EcPublicKey) {
- log("DeviceRetrievalHelper Listener (QR): OnEReaderKeyReceived")
- }
-
- override fun onDeviceRequest(deviceRequestBytes: ByteArray) {
- onNewRequest(deviceRequestBytes)
- }
-
- override fun onDeviceDisconnected(transportSpecificTermination: Boolean) {
- onDisconnected()
- }
-
- override fun onError(error: Throwable) {
- onCommunicationError(error)
- }
- }
-
- fun configure(
- reverseEngagementUri: String,
- origins: List
- ) {
- val uri = Uri.parse(reverseEngagementUri)
- if (!uri.scheme.equals("mdoc")) {
- throw IllegalStateException("Only supports mdoc URIs")
- }
- val encodedReaderEngagement = Base64.decode(
- uri.encodedSchemeSpecificPart,
- Base64.URL_SAFE or Base64.NO_PADDING
- )
- val engagement = EngagementParser(
- encodedReaderEngagement
- ).parse()
- if (engagement.connectionMethods.size == 0) {
- throw IllegalStateException("No connection methods in engagement")
- }
-
- // For now, just pick the first transport
- val connectionMethod = engagement.connectionMethods[0]
- log("Using connection method $connectionMethod")
-
- val transport = DataTransport.fromConnectionMethod(
- context,
- connectionMethod,
- DataTransport.Role.MDOC,
- connectionSetup.getConnectionOptions()
- )
-
- val builder = DeviceRetrievalHelper.Builder(
- context,
- presentationListener,
- context.mainExecutor(),
- eDeviceKey
- )
- builder.useReverseEngagement(transport, encodedReaderEngagement, origins)
- presentation = builder.build()
- onPresentationReady(requireNotNull(presentation))
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt
deleted file mode 100644
index 01172abd7..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt
+++ /dev/null
@@ -1,288 +0,0 @@
-package com.android.identity.wallet.transfer
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Color.BLACK
-import android.graphics.Color.WHITE
-import android.view.View
-import android.widget.ImageView
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import com.android.identity.document.DocumentRequest
-import com.android.identity.document.NameSpacedData
-import com.android.identity.mdoc.mso.StaticAuthDataParser
-import com.android.identity.mdoc.origininfo.OriginInfo
-import com.android.identity.mdoc.request.DeviceRequestParser
-import com.android.identity.mdoc.response.DeviceResponseGenerator
-import com.android.identity.mdoc.response.DocumentGenerator
-import com.android.identity.mdoc.util.MdocUtil
-import com.android.identity.crypto.Algorithm
-import com.android.identity.mdoc.credential.MdocCredential
-import com.android.identity.securearea.KeyLockedException
-import com.android.identity.securearea.KeyPurpose
-import com.android.identity.securearea.KeyUnlockData
-import com.android.identity.wallet.document.DocumentManager
-import com.android.identity.wallet.documentdata.DocumentDataReader
-import com.android.identity.wallet.documentdata.DocumentElements
-import com.android.identity.wallet.util.ProvisioningUtil
-import com.android.identity.wallet.util.TransferStatus
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.util.logWarning
-import com.android.identity.wallet.util.requireValidProperty
-import com.google.zxing.BarcodeFormat
-import com.google.zxing.MultiFormatWriter
-import com.google.zxing.WriterException
-import com.google.zxing.common.BitMatrix
-import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlinx.datetime.Clock
-import kotlin.coroutines.resume
-
-class TransferManager private constructor(private val context: Context) {
-
- companion object {
- @SuppressLint("StaticFieldLeak")
- @Volatile
- private var instance: TransferManager? = null
-
- fun getInstance(context: Context) =
- instance ?: synchronized(this) {
- instance ?: TransferManager(context).also { instance = it }
- }
- }
-
- private var reversedQrCommunicationSetup: ReverseQrCommunicationSetup? = null
- private var qrCommunicationSetup: QrCommunicationSetup? = null
- private var hasStarted = false
-
- private lateinit var communication: Communication
-
- private var transferStatusLd = MutableLiveData()
-
- fun setCommunication(communication: Communication) {
- this.communication = communication
- }
-
- fun getTransferStatus(): LiveData = transferStatusLd
-
- fun updateStatus(status: TransferStatus) {
- transferStatusLd.value = status
- }
-
- fun documentRequests(): Collection {
- return communication.getDeviceRequest().docRequests
- }
-
- fun startPresentationReverseEngagement(
- reverseEngagementUri: String,
- origins: List
- ) {
- if (hasStarted) {
- throw IllegalStateException("Transfer has already started.")
- }
- communication = Communication.getInstance(context)
- reversedQrCommunicationSetup = ReverseQrCommunicationSetup(
- context = context,
- onPresentationReady = { deviceRetrievalHelper ->
- communication.deviceRetrievalHelper = deviceRetrievalHelper
- },
- onNewRequest = { request ->
- communication.setDeviceRequest(request)
- transferStatusLd.value = TransferStatus.REQUEST
- },
- onDisconnected = { transferStatusLd.value = TransferStatus.DISCONNECTED },
- onCommunicationError = { error ->
- log("onError: ${error.message}")
- transferStatusLd.value = TransferStatus.ERROR
- }
- ).apply {
- configure(reverseEngagementUri, origins)
- }
- hasStarted = true
- }
-
- fun startQrEngagement() {
- if (hasStarted) {
- throw IllegalStateException("Transfer has already started.")
- }
- communication = Communication.getInstance(context)
- qrCommunicationSetup = QrCommunicationSetup(
- context = context,
- onConnecting = { transferStatusLd.value = TransferStatus.CONNECTING },
- onDeviceRetrievalHelperReady = { deviceRetrievalHelper ->
- communication.deviceRetrievalHelper = deviceRetrievalHelper
- transferStatusLd.value = TransferStatus.CONNECTED
- },
- onNewDeviceRequest = { deviceRequest ->
- communication.setDeviceRequest(deviceRequest)
- transferStatusLd.value = TransferStatus.REQUEST
- },
- onDisconnected = { transferStatusLd.value = TransferStatus.DISCONNECTED }
- ) { error ->
- log("onError: ${error.message}")
- transferStatusLd.value = TransferStatus.ERROR
- }.apply {
- configure()
- }
- hasStarted = true
- }
-
- fun getDeviceEngagementQrCode(): View {
- val deviceEngagementForQrCode = qrCommunicationSetup!!.deviceEngagementUriEncoded
- val qrCodeBitmap = encodeQRCodeAsBitmap(deviceEngagementForQrCode)
- val qrCodeView = ImageView(context)
- qrCodeView.setImageBitmap(qrCodeBitmap)
-
- return qrCodeView
- }
-
- private fun encodeQRCodeAsBitmap(str: String): Bitmap {
- val width = 800
- val result: BitMatrix = try {
- MultiFormatWriter().encode(
- str,
- BarcodeFormat.QR_CODE, width, width, null
- )
- } catch (e: WriterException) {
- throw java.lang.IllegalArgumentException(e)
- }
- val w = result.width
- val h = result.height
- val pixels = IntArray(w * h)
- for (y in 0 until h) {
- val offset = y * w
- for (x in 0 until w) {
- pixels[offset + x] = if (result[x, y]) BLACK else WHITE
- }
- }
- val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
- bitmap.setPixels(pixels, 0, width, 0, 0, w, h)
- return bitmap
- }
-
- @Throws(IllegalStateException::class)
- suspend fun addDocumentToResponse(
- documentName: String,
- docType: String,
- issuerSignedEntriesToRequest: MutableMap>,
- deviceResponseGenerator: DeviceResponseGenerator,
- credential: MdocCredential?,
- authKeyUnlockData: KeyUnlockData?,
- ) = suspendCancellableCoroutine { continuation ->
- var result: AddDocumentToResponseResult
- var signingKeyUsageLimitPassed = false
- val documentManager = DocumentManager.getInstance(context)
- val documentInformation = documentManager.getDocumentInformation(documentName)
- requireValidProperty(documentInformation) { "Document not found!" }
-
- val document = requireNotNull(documentManager.getDocumentByName(documentName))
- val dataElements = issuerSignedEntriesToRequest.keys.flatMap { key ->
- issuerSignedEntriesToRequest.getOrDefault(key, emptyList()).map { value ->
- DocumentRequest.DataElement(key, value, false)
- }
- }
-
- val request = DocumentRequest(dataElements)
-
- val credentialToUse: MdocCredential
- if (credential != null) {
- credentialToUse = credential
- } else {
- credentialToUse = document.findCredential(
- ProvisioningUtil.CREDENTIAL_DOMAIN,
- Clock.System.now()
- ) as MdocCredential?
- ?: throw IllegalStateException("No credential available")
- }
-
- if (credentialToUse.usageCount >= documentInformation.maxUsagesPerKey) {
- logWarning("Using Credential previously used ${credentialToUse.usageCount} times, and maxUsagesPerKey is ${documentInformation.maxUsagesPerKey}")
- signingKeyUsageLimitPassed = true
- }
-
- val staticAuthData = StaticAuthDataParser(credentialToUse.issuerProvidedData).parse()
- val mergedIssuerNamespaces = MdocUtil.mergeIssuerNamesSpaces(
- request,
- document.applicationData.getNameSpacedData("documentData"),
- staticAuthData
- )
-
- val transcript = communication.getSessionTranscript() ?: byteArrayOf()
-
- try {
- val generator = DocumentGenerator(docType, staticAuthData.issuerAuth, transcript)
- .setIssuerNamespaces(mergedIssuerNamespaces)
- val keyInfo = credentialToUse.secureArea.getKeyInfo(credentialToUse.alias)
- if (keyInfo.keyPurposes.contains(KeyPurpose.SIGN)) {
- generator.setDeviceNamespacesSignature(
- NameSpacedData.Builder().build(),
- credentialToUse.secureArea,
- credentialToUse.alias,
- authKeyUnlockData,
- Algorithm.ES256
- )
- } else {
- generator.setDeviceNamespacesMac(
- NameSpacedData.Builder().build(),
- credentialToUse.secureArea,
- credentialToUse.alias,
- authKeyUnlockData,
- communication.deviceRetrievalHelper!!.eReaderKey
- )
- }
- val data = generator.generate()
- deviceResponseGenerator.addDocument(data)
- log("Increasing usage count on ${credentialToUse.alias}")
- credentialToUse.increaseUsageCount()
- ProvisioningUtil.getInstance(context).trackUsageTimestamp(document)
- result = AddDocumentToResponseResult.DocumentAdded(signingKeyUsageLimitPassed)
- } catch (lockedException: KeyLockedException) {
- result = AddDocumentToResponseResult.DocumentLocked(credentialToUse)
- }
- continuation.resume(result)
- }
-
- fun stopPresentation(
- sendSessionTerminationMessage: Boolean,
- useTransportSpecificSessionTermination: Boolean
- ) {
- communication.stopPresentation(
- sendSessionTerminationMessage,
- useTransportSpecificSessionTermination
- )
- disconnect()
- }
-
- fun disconnect() {
- communication.disconnect()
- qrCommunicationSetup?.close()
- transferStatusLd = MutableLiveData()
- destroy()
- }
-
- fun destroy() {
- qrCommunicationSetup = null
- reversedQrCommunicationSetup = null
- hasStarted = false
- }
-
- fun sendResponse(deviceResponse: ByteArray, closeAfterSending: Boolean) {
- communication.sendResponse(deviceResponse, closeAfterSending)
- if (closeAfterSending) {
- disconnect()
- }
- }
-
- fun readDocumentEntries(documentName: String): DocumentElements {
- val documentManager = DocumentManager.getInstance(context)
- val documentInformation = documentManager.getDocumentInformation(documentName)
-
- val document = requireNotNull(documentManager.getDocumentByName(documentName))
- val nameSpacedData = document.applicationData.getNameSpacedData("documentData")
- return DocumentDataReader(documentInformation?.docType ?: "").read(nameSpacedData)
- }
-
- fun setResponseServed() {
- transferStatusLd.value = TransferStatus.REQUEST_SERVED
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/CountryValidator.kt b/appholder/src/main/java/com/android/identity/wallet/trustmanagement/CountryValidator.kt
deleted file mode 100644
index a1a1f829a..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/CountryValidator.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.android.identity.wallet.trustmanagement
-
-import java.security.cert.Certificate
-import java.security.cert.CertificateException
-import java.security.cert.PKIXCertPathChecker
-import java.security.cert.X509Certificate
-
-/**
- * Class used to validate that the country code in the whole certificate chain is the same
- */
-class CountryValidator : PKIXCertPathChecker() {
- private var previousCountryCode: String = ""
-
- /**
- * There is no custom initialisation of this class
- */
- override fun init(p0: Boolean) {
- // intentionally left empty
- }
-
-
- /**
- * Forward checking supported: the order of the certificate chain is not relevant for the check
- * on country code.
- */
- override fun isForwardCheckingSupported(): Boolean {
- return true
- }
-
- /**
- * Check the country code
- */
- override fun check(certificate: Certificate?, state: MutableCollection?) {
- if (certificate is X509Certificate) {
- val countryCode = certificate.subjectX500Principal.countryCode("")
- if (countryCode.isBlank()) {
- throw CertificateException("Country code is not present in certificate " + certificate.subjectX500Principal.name)
- }
- if (previousCountryCode.isNotBlank() && previousCountryCode.uppercase() != countryCode.uppercase()) {
- throw CertificateException("There are different country codes in the certificate chain: $previousCountryCode and $countryCode")
- } else {
- previousCountryCode = countryCode
- }
- }
- }
-
- /**
- * Extensions are not validated on country code
- */
- override fun getSupportedExtensions(): MutableSet {
- return mutableSetOf()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/CustomValidators.kt b/appholder/src/main/java/com/android/identity/wallet/trustmanagement/CustomValidators.kt
deleted file mode 100644
index aa5275971..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/CustomValidators.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.android.identity.wallet.trustmanagement
-
-import java.security.cert.PKIXCertPathChecker
-
-/**
- * Object used to obtain custom validators based on docType
- */
-object CustomValidators {
- fun getByDocType(docType: String): List
- {
- when (docType){
- "org.iso.18013.5.1.mDL" -> return listOf(CountryValidator())
- else -> return emptyList()
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/X500PrincipalExtensions.kt b/appholder/src/main/java/com/android/identity/wallet/trustmanagement/X500PrincipalExtensions.kt
deleted file mode 100644
index 5b2f73741..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/X500PrincipalExtensions.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.identity.wallet.trustmanagement
-
-import org.bouncycastle.asn1.ASN1ObjectIdentifier
-import org.bouncycastle.asn1.x500.X500Name
-import org.bouncycastle.asn1.x500.style.BCStyle
-import javax.security.auth.x500.X500Principal
-
-/**
- * Extract the common name of a X500Principal (subject or issuer)
- */
-fun X500Principal.getCommonName(defaultValue: String): String {
- return readRdn(this.name, BCStyle.CN, defaultValue)
-}
-
-/**
- * Extract the organisation of a X500Principal (subject or issuer)
- */
-fun X500Principal.getOrganisation(defaultValue: String): String {
- return readRdn(this.name, BCStyle.O, defaultValue)
-}
-
-/**
- * Extract the organisational unit of a X500Principal (subject or issuer)
- */
-fun X500Principal.organisationalUnit(defaultValue: String): String {
- return readRdn(this.name, BCStyle.OU, defaultValue)
-}
-
-/**
- * Extract the country code of a X500Principal (subject or issuer)
- */
-fun X500Principal.countryCode(defaultValue: String): String {
- return readRdn(this.name, BCStyle.C, defaultValue)
-}
-
-/**
- * Read a relative distinguished name from a distinguished name
- */
-private fun readRdn(name: String, field: ASN1ObjectIdentifier, defaultValue: String): String {
- val x500name = X500Name(name)
- for (rdn in x500name.getRDNs(field)) {
- val attributes = rdn.typesAndValues
- for (attribute in attributes) {
- return attribute.value.toString()
- }
- }
- return defaultValue
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/X509CertificateExtensions.kt b/appholder/src/main/java/com/android/identity/wallet/trustmanagement/X509CertificateExtensions.kt
deleted file mode 100644
index fe32decec..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/trustmanagement/X509CertificateExtensions.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.android.identity.wallet.trustmanagement
-
-import com.android.identity.util.toHex
-import org.bouncycastle.asn1.DEROctetString
-import org.bouncycastle.asn1.x509.Extension
-import org.bouncycastle.asn1.x509.SubjectKeyIdentifier
-import java.security.cert.X509Certificate
-
-/**
- * Get the Subject Key Identifier Extension from the
- * X509 certificate in hexadecimal format.
- */
-fun X509Certificate.getSubjectKeyIdentifier(): String {
- val extensionValue = this.getExtensionValue(Extension.subjectKeyIdentifier.id)
- val octets = DEROctetString.getInstance(extensionValue).octets
- val subjectKeyIdentifier = SubjectKeyIdentifier.getInstance(octets)
- return subjectKeyIdentifier.keyIdentifier.toHex()
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/BindingAdapters.kt b/appholder/src/main/java/com/android/identity/wallet/util/BindingAdapters.kt
deleted file mode 100644
index c61aba37b..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/BindingAdapters.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.view.View
-import android.view.ViewGroup
-import android.widget.LinearLayout
-import androidx.databinding.BindingAdapter
-
-
-object BindingAdapters {
- /**
- * A Binding Adapter that is called whenever the value of the attribute `app:engagementView`
- * changes. Receives a view with the QR Code for the device engagement.
- */
- @BindingAdapter("app:engagementView")
- @JvmStatic
- fun engagementView(view: LinearLayout, viewEngagement: View?) {
- viewEngagement?.let {
- (viewEngagement.parent as? ViewGroup)?.removeView(viewEngagement)
- view.addView(it)
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/ContextExtensions.kt b/appholder/src/main/java/com/android/identity/wallet/util/ContextExtensions.kt
deleted file mode 100644
index 76ee921ec..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/ContextExtensions.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.content.Context
-import android.os.Build
-import androidx.core.content.ContextCompat
-import java.util.concurrent.Executor
-
-fun Context.mainExecutor(): Executor {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- mainExecutor
- } else {
- ContextCompat.getMainExecutor(applicationContext)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/DocumentData.kt b/appholder/src/main/java/com/android/identity/wallet/util/DocumentData.kt
deleted file mode 100644
index 4bb0972c4..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/DocumentData.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.android.identity.wallet.util
-
-object DocumentData {
- const val MDL_DOCTYPE = "org.iso.18013.5.1.mDL"
- const val MDL_NAMESPACE = "org.iso.18013.5.1"
- const val MVR_DOCTYPE = "nl.rdw.mekb.1"
- const val MVR_NAMESPACE = "nl.rdw.mekb.1"
- const val MICOV_DOCTYPE = "org.micov.1"
- const val MICOV_VTR_NAMESPACE = "org.micov.vtr.1"
- const val MICOV_ATT_NAMESPACE = "org.micov.attestation.1"
- const val AAMVA_NAMESPACE = "org.iso.18013.5.1.aamva"
- const val EU_PID_DOCTYPE = "eu.europa.ec.eudi.pid.1"
- const val EU_PID_NAMESPACE = "eu.europa.ec.eudi.pid.1"
-
- enum class ErikaStaticData(val identifier: String, val value: String) {
- VISIBLE_NAME("visible_name", "Driving License"),
- }
-
- enum class MekbStaticData(val identifier: String, val value: String) {
- VISIBLE_NAME("visible_name", "Vehicle Registration"),
- }
-
- enum class MicovStaticData(val identifier: String, val value: String) {
- VISIBLE_NAME("visible_name", "Vaccination Document"),
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/FormatUtil.kt b/appholder/src/main/java/com/android/identity/wallet/util/FormatUtil.kt
deleted file mode 100644
index 179d0a179..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/FormatUtil.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.icu.text.SimpleDateFormat
-import android.icu.util.TimeZone
-import co.nstant.`in`.cbor.CborBuilder
-import co.nstant.`in`.cbor.CborEncoder
-import co.nstant.`in`.cbor.CborException
-import co.nstant.`in`.cbor.model.DataItem
-import java.io.ByteArrayOutputStream
-import java.security.PublicKey
-import java.security.interfaces.ECPublicKey
-import java.security.spec.ECPoint
-import kotlin.math.min
-
-
-object FormatUtil {
- // Helper function to convert a byteArray to HEX string
- fun encodeToString(bytes: ByteArray): String {
- val sb = StringBuilder(bytes.size * 2)
- for (b in bytes) {
- sb.append(String.format("%02x", b))
- }
-
- return sb.toString()
- }
-
- private const val CHUNK_SIZE = 2048
-
- private fun debugPrint(message: String) {
- var index = 0
- while (index < message.length) {
- log(message.substring(index, min(message.length, index + CHUNK_SIZE)))
- index += CHUNK_SIZE
- }
- }
-
- fun debugPrintEncodeToString(bytes: ByteArray) {
- debugPrint(encodeToString(bytes))
- }
-
- private const val COSE_KEY_KTY = 1
- private const val COSE_KEY_TYPE_EC2 = 2
- private const val COSE_KEY_EC2_CRV = -1
- private const val COSE_KEY_EC2_X = -2
- private const val COSE_KEY_EC2_Y = -3
- private const val COSE_KEY_EC2_CRV_P256 = 1
-
- fun cborBuildCoseKey(key: PublicKey): DataItem {
- val ecKey: ECPublicKey = key as ECPublicKey
- val w: ECPoint = ecKey.w
- // X and Y are always positive so for interop we remove any leading zeroes
- // inserted by the BigInteger encoder.
- val x = stripLeadingZeroes(w.affineX.toByteArray())
- val y = stripLeadingZeroes(w.affineY.toByteArray())
- return CborBuilder()
- .addMap()
- .put(COSE_KEY_KTY.toLong(), COSE_KEY_TYPE_EC2.toLong())
- .put(COSE_KEY_EC2_CRV.toLong(), COSE_KEY_EC2_CRV_P256.toLong())
- .put(COSE_KEY_EC2_X.toLong(), x)
- .put(COSE_KEY_EC2_Y.toLong(), y)
- .end()
- .build()[0]
- }
-
- fun cborEncode(dataItem: DataItem): ByteArray {
- val baos = ByteArrayOutputStream()
- try {
- CborEncoder(baos).encode(dataItem)
- } catch (e: CborException) {
- // This should never happen and we don't want cborEncode() to throw since that
- // would complicate all callers. Log it instead.
- throw IllegalStateException("Unexpected failure encoding data", e)
- }
- return baos.toByteArray()
- }
-
- private fun stripLeadingZeroes(value: ByteArray): ByteArray {
- var n = 0
- while (n < value.size && value[n] == 0x00.toByte()) {
- n++
- }
- return value.copyOfRange(n, value.size)
- }
-
- fun fullDateStringToMilliseconds(date: String): Long {
- val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
- simpleDateFormat.timeZone = TimeZone.getTimeZone("UTC")
- return simpleDateFormat.parse(date).toInstant().toEpochMilli()
- }
-
- fun millisecondsToFullDateString(milliseconds: Long): String {
- val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
- return simpleDateFormat.format(milliseconds)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/LogginExtensions.kt b/appholder/src/main/java/com/android/identity/wallet/util/LogginExtensions.kt
deleted file mode 100644
index 67e19216b..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/LogginExtensions.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.util.Log
-
-fun Any.log(message: String, exception: Throwable? = null) {
- if (!PreferencesHelper.isDebugLoggingEnabled()) return
- val tag: String = tagValue()
- if (exception == null) {
- Log.d(tag, message)
- } else {
- Log.e(tag, message, exception)
- }
-}
-
-fun Any.logInfo(message: String) {
- if (!PreferencesHelper.isDebugLoggingEnabled()) return
- val tag: String = tagValue()
- Log.i(tag, message)
-}
-
-fun Any.logWarning(message: String) {
- if (!PreferencesHelper.isDebugLoggingEnabled()) return
- val tag: String = tagValue()
- Log.w(tag, message)
-}
-
-fun Any.logError(message: String) {
- if (!PreferencesHelper.isDebugLoggingEnabled()) return
- val tag: String = tagValue()
- Log.e(tag, message)
-}
-
-private fun Any.tagValue(): String {
- if (this is String) return this
- val fullClassName: String = this::class.qualifiedName ?: this::class.java.typeName
- val outerClassName = fullClassName.substringBefore('$')
- val simplerOuterClassName = outerClassName.substringAfterLast('.')
- return if (simplerOuterClassName.isEmpty()) {
- fullClassName
- } else {
- simplerOuterClassName.removeSuffix("Kt")
- }
-}
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/NfcDataTransferHandler.kt b/appholder/src/main/java/com/android/identity/wallet/util/NfcDataTransferHandler.kt
deleted file mode 100644
index ec7821753..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/NfcDataTransferHandler.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2019 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-package com.android.identity.wallet.util
-
-import android.nfc.cardemulation.HostApduService
-import android.os.Bundle
-import com.android.identity.android.mdoc.transport.DataTransportNfc
-import com.android.identity.wallet.transfer.TransferManager
-
-class NfcDataTransferHandler : HostApduService() {
-
- private lateinit var transferManager: TransferManager
-
- override fun onCreate() {
- super.onCreate()
- log("onCreate")
- transferManager = TransferManager.getInstance(applicationContext)
- }
-
- override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle?): ByteArray? {
- log("processCommandApdu: Command-> ${FormatUtil.encodeToString(commandApdu)}")
- return DataTransportNfc.processCommandApdu(this, commandApdu)
- }
-
- override fun onDeactivated(reason: Int) {
- log("onDeactivated: reason-> $reason")
- DataTransportNfc.onDeactivated(reason)
- }
-}
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/NfcEngagementHandler.kt b/appholder/src/main/java/com/android/identity/wallet/util/NfcEngagementHandler.kt
deleted file mode 100644
index d52813e74..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/NfcEngagementHandler.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2019 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-package com.android.identity.wallet.util
-
-import android.content.Intent
-import android.nfc.cardemulation.HostApduService
-import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import androidx.navigation.NavDeepLinkBuilder
-import com.android.identity.android.mdoc.deviceretrieval.DeviceRetrievalHelper
-import com.android.identity.android.mdoc.engagement.NfcEngagementHelper
-import com.android.identity.android.mdoc.transport.DataTransport
-import com.android.identity.crypto.Crypto
-import com.android.identity.crypto.EcPublicKey
-import com.android.identity.wallet.R
-import com.android.identity.wallet.transfer.Communication
-import com.android.identity.wallet.transfer.ConnectionSetup
-import com.android.identity.wallet.transfer.TransferManager
-
-class NfcEngagementHandler : HostApduService() {
-
- private lateinit var engagementHelper: NfcEngagementHelper
- private lateinit var communication: Communication
- private lateinit var transferManager: TransferManager
-
- private var deviceRetrievalHelper: DeviceRetrievalHelper? = null
-
- private val settings by lazy {
- PreferencesHelper.apply { initialize(applicationContext) }
- }
- private val eDeviceKey by lazy {
- Crypto.createEcPrivateKey(settings.getEphemeralKeyCurveOption())
- }
- private val nfcEngagementListener = object : NfcEngagementHelper.Listener {
-
- override fun onTwoWayEngagementDetected() {
- log("Engagement Listener: Two Way Engagement Detected.")
- }
-
- override fun onHandoverSelectMessageSent() {
- log("Engagement Listener: Handover Select Message Sent.")
- }
-
- override fun onDeviceConnecting() {
- log("Engagement Listener: Device Connecting. Launching Transfer Screen")
- val pendingIntent = NavDeepLinkBuilder(applicationContext)
- .setGraph(R.navigation.navigation_graph)
- .setDestination(R.id.transferDocumentFragment)
- .setComponentName(com.android.identity.wallet.MainActivity::class.java)
- .createPendingIntent()
- pendingIntent.send(applicationContext, 0, null)
- transferManager.updateStatus(TransferStatus.CONNECTING)
- }
-
- override fun onDeviceConnected(transport: DataTransport) {
- if (deviceRetrievalHelper != null) {
- log("Engagement Listener: Device Connected -> ignored due to active presentation")
- return
- }
-
- log("Engagement Listener: Device Connected via NFC")
-
- val builder = DeviceRetrievalHelper.Builder(
- applicationContext,
- presentationListener,
- applicationContext.mainExecutor(),
- eDeviceKey
- )
- builder.useForwardEngagement(
- transport,
- engagementHelper.deviceEngagement,
- engagementHelper.handover
- )
- deviceRetrievalHelper = builder.build()
- communication.deviceRetrievalHelper = deviceRetrievalHelper
- engagementHelper.close()
- transferManager.updateStatus(TransferStatus.CONNECTED)
- }
-
- override fun onError(error: Throwable) {
- log("Engagement Listener: onError -> ${error.message}")
- transferManager.updateStatus(TransferStatus.ERROR)
- engagementHelper.close()
- }
- }
-
- private val presentationListener = object : DeviceRetrievalHelper.Listener {
-
- override fun onEReaderKeyReceived(eReaderKey: EcPublicKey) {
- log("DeviceRetrievalHelper Listener (NFC): OnEReaderKeyReceived")
- }
-
- override fun onDeviceRequest(deviceRequestBytes: ByteArray) {
- log("Presentation Listener: OnDeviceRequest")
- communication.setDeviceRequest(deviceRequestBytes)
- transferManager.updateStatus(TransferStatus.REQUEST)
- }
-
- override fun onDeviceDisconnected(transportSpecificTermination: Boolean) {
- log("Presentation Listener: onDeviceDisconnected")
- transferManager.updateStatus(TransferStatus.DISCONNECTED)
- }
-
- override fun onError(error: Throwable) {
- log("Presentation Listener: onError -> ${error.message}")
- transferManager.updateStatus(TransferStatus.ERROR)
- }
- }
-
- override fun onCreate() {
- super.onCreate()
- log("onCreate")
- communication = Communication.getInstance(applicationContext)
- transferManager = TransferManager.getInstance(applicationContext)
- transferManager.setCommunication(communication)
- val connectionSetup = ConnectionSetup(applicationContext)
- val builder = NfcEngagementHelper.Builder(
- applicationContext,
- eDeviceKey.publicKey,
- connectionSetup.getConnectionOptions(),
- nfcEngagementListener,
- applicationContext.mainExecutor()
- )
- if (PreferencesHelper.shouldUseStaticHandover()) {
- builder.useStaticHandover(connectionSetup.getConnectionMethods())
- } else {
- builder.useNegotiatedHandover()
- }
- engagementHelper = builder.build()
-
- val launchAppIntent = Intent(applicationContext, com.android.identity.wallet.MainActivity::class.java)
- launchAppIntent.action = Intent.ACTION_VIEW
- launchAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
- launchAppIntent.addCategory(Intent.CATEGORY_DEFAULT)
- launchAppIntent.addCategory(Intent.CATEGORY_BROWSABLE)
- applicationContext.startActivity(launchAppIntent)
- }
-
- override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle?): ByteArray? {
- log("processCommandApdu: Command-> ${FormatUtil.encodeToString(commandApdu)}")
- return engagementHelper.nfcProcessCommandApdu(commandApdu)
- }
-
- override fun onDeactivated(reason: Int) {
- log("onDeactivated: reason-> $reason")
- engagementHelper.nfcOnDeactivated(reason)
-
- // We need to close the NfcEngagementHelper but if we're doing it as the reader moves
- // out of the field, it's too soon as it may take a couple of seconds to establish
- // the connection, triggering onDeviceConnected() callback above.
- //
- // In fact, the reader _could_ actually take a while to establish the connection...
- // for example the UI in the mdoc doc reader might have the operator pick the
- // transport if more than one is offered. In fact this is exactly what we do in
- // our mdoc reader.
- //
- // So we give the reader 15 seconds to do this...
- //
- val timeoutSeconds = 15
- Handler(Looper.getMainLooper()).postDelayed({
- if (deviceRetrievalHelper == null) {
- logWarning("reader didn't connect inside $timeoutSeconds seconds, closing")
- engagementHelper.close()
- }
- }, timeoutSeconds * 1000L)
- }
-}
-
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/PeriodicKeysRefreshWorkRequest.kt b/appholder/src/main/java/com/android/identity/wallet/util/PeriodicKeysRefreshWorkRequest.kt
deleted file mode 100644
index 69e2c04b8..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/PeriodicKeysRefreshWorkRequest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.content.Context
-import androidx.work.PeriodicWorkRequestBuilder
-import androidx.work.WorkManager
-import java.util.concurrent.TimeUnit
-
-class PeriodicKeysRefreshWorkRequest(context: Context) {
-
- private val workManager = WorkManager.getInstance(context)
-
- fun schedulePeriodicKeysRefreshing() {
- val workRequest = PeriodicWorkRequestBuilder(1, TimeUnit.DAYS)
- .build()
- workManager.enqueue(workRequest)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/Preconditions.kt b/appholder/src/main/java/com/android/identity/wallet/util/Preconditions.kt
deleted file mode 100644
index 096d4f7d5..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/Preconditions.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-@file:OptIn(ExperimentalContracts::class)
-
-package com.android.identity.wallet.util
-
-import kotlin.contracts.ExperimentalContracts
-import kotlin.contracts.contract
-
-inline fun requireValidProperty(value: T?, lazyMessage: () -> Any): T {
- contract {
- returns() implies (value != null)
- }
-
- if (value == null) {
- val message = lazyMessage()
- throw IllegalStateException(message.toString())
- } else {
- return value
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/PreferencesHelper.kt b/appholder/src/main/java/com/android/identity/wallet/util/PreferencesHelper.kt
deleted file mode 100644
index 90a36b64f..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/PreferencesHelper.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.content.Context
-import android.content.SharedPreferences
-import androidx.core.content.edit
-import androidx.preference.PreferenceManager
-import com.android.identity.crypto.EcCurve
-import com.android.identity.util.Logger
-import java.io.File
-
-object PreferencesHelper {
- private const val BLE_DATA_RETRIEVAL = "ble_transport"
- private const val BLE_DATA_RETRIEVAL_PERIPHERAL_MODE = "ble_transport_peripheral_mode"
- private const val BLE_DATA_L2CAP = "ble_l2cap"
- private const val BLE_CLEAR_CACHE = "ble_clear_cache"
- private const val WIFI_DATA_RETRIEVAL = "wifi_transport"
- private const val NFC_DATA_RETRIEVAL = "nfc_transport"
- private const val DEBUG_LOG = "debug_log"
- private const val CONNECTION_AUTO_CLOSE = "connection_auto_close"
- private const val STATIC_HANDOVER = "static_handover"
- private const val EPHEMERAL_KEY_CURVE_OPTION = "ephemeral_key_curve"
-
- private lateinit var sharedPreferences: SharedPreferences
-
- fun initialize(context: Context) {
- sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
- }
-
- fun getKeystoreBackedStorageLocation(context: Context): File {
- // As per the docs, the document data contains reference to Keystore aliases so ensure
- // this is stored in a location where it's not automatically backed up and restored by
- // Android Backup as per https://developer.android.com/guide/topics/data/autobackup
- return File(context.noBackupFilesDir, "identity.bin")
- }
-
- fun isBleDataRetrievalEnabled(): Boolean =
- sharedPreferences.getBoolean(BLE_DATA_RETRIEVAL, true)
-
- fun setBleDataRetrievalEnabled(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(BLE_DATA_RETRIEVAL, enabled) }
-
- fun isBleDataRetrievalPeripheralModeEnabled(): Boolean =
- sharedPreferences.getBoolean(BLE_DATA_RETRIEVAL_PERIPHERAL_MODE, false)
-
- fun setBlePeripheralDataRetrievalMode(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(BLE_DATA_RETRIEVAL_PERIPHERAL_MODE, enabled) }
-
- fun isBleL2capEnabled(): Boolean =
- sharedPreferences.getBoolean(BLE_DATA_L2CAP, false)
-
- fun setBleL2CAPEnabled(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(BLE_DATA_L2CAP, enabled) }
-
- fun isBleClearCacheEnabled(): Boolean =
- sharedPreferences.getBoolean(BLE_CLEAR_CACHE, false)
-
- fun setBleClearCacheEnabled(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(BLE_CLEAR_CACHE, enabled) }
-
- fun isWifiDataRetrievalEnabled(): Boolean =
- sharedPreferences.getBoolean(WIFI_DATA_RETRIEVAL, false)
-
- fun setWifiDataRetrievalEnabled(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(WIFI_DATA_RETRIEVAL, enabled) }
-
- fun isNfcDataRetrievalEnabled(): Boolean =
- sharedPreferences.getBoolean(NFC_DATA_RETRIEVAL, false)
-
- fun setNfcDataRetrievalEnabled(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(NFC_DATA_RETRIEVAL, enabled) }
-
- fun isConnectionAutoCloseEnabled(): Boolean =
- sharedPreferences.getBoolean(CONNECTION_AUTO_CLOSE, true)
-
- fun setConnectionAutoCloseEnabled(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(CONNECTION_AUTO_CLOSE, enabled) }
-
- fun shouldUseStaticHandover(): Boolean =
- sharedPreferences.getBoolean(STATIC_HANDOVER, false)
-
- fun setUseStaticHandover(enabled: Boolean) =
- sharedPreferences.edit { putBoolean(STATIC_HANDOVER, enabled) }
-
- fun isDebugLoggingEnabled(): Boolean =
- sharedPreferences.getBoolean(DEBUG_LOG, true)
-
- fun setDebugLoggingEnabled(enabled: Boolean) =
- sharedPreferences
- .edit { putBoolean(DEBUG_LOG, enabled) }
- .also { Logger.isDebugEnabled = enabled }
-
- fun getEphemeralKeyCurveOption(): EcCurve =
- EcCurve.fromInt(
- sharedPreferences.getInt(
- EPHEMERAL_KEY_CURVE_OPTION,
- EcCurve.P256.coseCurveIdentifier
- )
- )
-
- fun setEphemeralKeyCurveOption(newValue: EcCurve) =
- sharedPreferences.edit { putInt(EPHEMERAL_KEY_CURVE_OPTION, newValue.coseCurveIdentifier) }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/ProvisioningUtil.kt b/appholder/src/main/java/com/android/identity/wallet/util/ProvisioningUtil.kt
deleted file mode 100644
index 04f3b55b4..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/ProvisioningUtil.kt
+++ /dev/null
@@ -1,344 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.Rect
-import com.android.identity.cbor.Bstr
-import com.android.identity.cbor.Cbor
-import com.android.identity.cbor.Tagged
-import com.android.identity.cbor.toDataItem
-import com.android.identity.cose.Cose
-import com.android.identity.cose.CoseNumberLabel
-import com.android.identity.document.Document
-import com.android.identity.document.DocumentUtil
-import com.android.identity.document.NameSpacedData
-import com.android.identity.crypto.Algorithm
-import com.android.identity.crypto.X509Cert
-import com.android.identity.crypto.X509CertChain
-import com.android.identity.crypto.EcCurve
-import com.android.identity.crypto.toEcPrivateKey
-import com.android.identity.mdoc.credential.MdocCredential
-import com.android.identity.mdoc.mso.MobileSecurityObjectGenerator
-import com.android.identity.mdoc.mso.StaticAuthDataGenerator
-import com.android.identity.mdoc.util.MdocUtil
-import com.android.identity.securearea.SecureArea
-import com.android.identity.securearea.SecureAreaRepository
-import com.android.identity.wallet.HolderApp
-import com.android.identity.wallet.document.DocumentInformation
-import com.android.identity.wallet.document.KeysAndCertificates
-import com.android.identity.wallet.selfsigned.ProvisionInfo
-import com.android.identity.wallet.support.SecureAreaSupport
-import com.android.identity.wallet.util.DocumentData.MICOV_DOCTYPE
-import com.android.identity.wallet.util.DocumentData.MVR_DOCTYPE
-import java.io.ByteArrayOutputStream
-import java.time.Instant as JavaInstant
-import java.time.ZoneId
-import java.time.ZonedDateTime
-import java.time.format.DateTimeFormatter
-import kotlin.random.Random
-import kotlinx.datetime.Clock
-import kotlinx.datetime.Instant
-
-class ProvisioningUtil private constructor(
- private val context: Context
-) {
-
- val secureAreaRepository = SecureAreaRepository()
- val documentStore by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- HolderApp.createDocumentStore(context, secureAreaRepository)
- }
-
- fun provisionSelfSigned(
- nameSpacedData: NameSpacedData,
- provisionInfo: ProvisionInfo,
- ) {
- val document = documentStore.createDocument(provisionInfo.documentName())
- documentStore.addDocument(document)
- document.applicationData.setNameSpacedData("documentData", nameSpacedData)
-
- val authKeySecureArea: SecureArea = provisionInfo.currentSecureArea.secureArea
-
- // Store all settings for the document that are not SecureArea specific
- document.applicationData.setString(USER_VISIBLE_NAME, provisionInfo.docName)
- document.applicationData.setString(DOCUMENT_TYPE, provisionInfo.docType)
- document.applicationData.setString(DATE_PROVISIONED, dateTimeFormatter.format(ZonedDateTime.now()))
- document.applicationData.setNumber(CARD_ART, provisionInfo.docColor.toLong())
- document.applicationData.setBoolean(IS_SELF_SIGNED, true)
- document.applicationData.setNumber(MAX_USAGES_PER_KEY, provisionInfo.maxUseMso.toLong())
- document.applicationData.setNumber(VALIDITY_IN_DAYS, provisionInfo.validityInDays.toLong())
- document.applicationData.setNumber(MIN_VALIDITY_IN_DAYS, provisionInfo.minValidityInDays.toLong())
- document.applicationData.setNumber(LAST_TIME_USED, -1)
- document.applicationData.setString(AUTH_KEY_SECURE_AREA_IDENTIFIER, authKeySecureArea.identifier)
- document.applicationData.setNumber(NUM_CREDENTIALS, provisionInfo.numberMso.toLong())
-
- // Store settings for auth-key creation, these are all SecureArea-specific and we store
- // them in a single blob at AUTH_KEY_SETTINGS
- val support = SecureAreaSupport.getInstance(context, authKeySecureArea)
- document.applicationData.setData(
- AUTH_KEY_SETTINGS,
- support.createAuthKeySettingsConfiguration(provisionInfo.secureAreaSupportState))
-
- // Create initial batch of credentials
- refreshCredentials(document, provisionInfo.docType)
- }
-
- private fun ProvisionInfo.documentName(): String {
- val regex = Regex("[^A-Za-z0-9 ]")
- return regex.replace(docName, "").replace(" ", "_").lowercase()
- }
-
- fun trackUsageTimestamp(document: Document) {
- val now = Clock.System.now()
- document.applicationData.setNumber(LAST_TIME_USED, now.toEpochMilliseconds())
- }
-
- fun refreshCredentials(document: Document, docType: String) {
- val secureAreaIdentifier = document.applicationData.getString(AUTH_KEY_SECURE_AREA_IDENTIFIER)
- val minValidTimeDays = document.applicationData.getNumber(MIN_VALIDITY_IN_DAYS)
- val maxUsagesPerCred = document.applicationData.getNumber(MAX_USAGES_PER_KEY)
- val numCreds = document.applicationData.getNumber(NUM_CREDENTIALS)
- val validityInDays = document.applicationData.getNumber(VALIDITY_IN_DAYS).toInt()
-
- val now = Clock.System.now()
- val validFrom = now
- val validUntil = Instant.fromEpochMilliseconds(
- validFrom.toEpochMilliseconds() + validityInDays*86400*1000L)
-
- val secureArea = secureAreaRepository.getImplementation(secureAreaIdentifier)
- ?: throw IllegalStateException("No Secure Area with id ${secureAreaIdentifier} for document ${document.name}")
-
- val support = SecureAreaSupport.getInstance(context, secureArea)
- val settings = support.createAuthKeySettingsFromConfiguration(
- document.applicationData.getData(AUTH_KEY_SETTINGS),
- "challenge".toByteArray(),
- validFrom,
- validUntil
- )
-
- val pendingCredsCount = DocumentUtil.managedCredentialHelper(
- document,
- CREDENTIAL_DOMAIN,
- {toBeReplaced -> MdocCredential(
- document,
- toBeReplaced,
- CREDENTIAL_DOMAIN,
- secureArea,
- settings,
- docType
- )},
- now,
- numCreds.toInt(),
- maxUsagesPerCred.toInt(),
- minValidTimeDays*24*60*60*1000L,
- false
- )
- if (pendingCredsCount <= 0) {
- return
- }
-
- for (pendingCred in document.pendingCredentials.filter { it.domain == CREDENTIAL_DOMAIN }) {
- pendingCred as MdocCredential
- val msoGenerator = MobileSecurityObjectGenerator(
- "SHA-256",
- docType,
- pendingCred.attestation.publicKey
- )
- msoGenerator.setValidityInfo(now, validFrom, validUntil, null)
-
- // For mDLs, override the portrait with AuthenticationKeyCounter on top
- //
- var dataElementExceptions: Map>? = null
- var dataElementOverrides: Map>? = null
- if (docType.equals("org.iso.18013.5.1.mDL")) {
- val portrait = document.applicationData.getNameSpacedData("documentData")
- .getDataElementByteString("org.iso.18013.5.1", "portrait")
- val portrait_override = overridePortrait(portrait,
- pendingCred.credentialCounter)
-
- dataElementExceptions =
- mapOf("org.iso.18013.5.1" to listOf("given_name", "portrait"))
- dataElementOverrides =
- mapOf("org.iso.18013.5.1" to mapOf(
- "portrait" to Cbor.encode(Bstr(portrait_override))))
- }
-
- val issuerNameSpaces = MdocUtil.generateIssuerNameSpaces(
- document.applicationData.getNameSpacedData("documentData"),
- Random.Default,
- 16,
- dataElementOverrides
- )
-
- for (nameSpaceName in issuerNameSpaces.keys) {
- val digests = MdocUtil.calculateDigestsForNameSpace(
- nameSpaceName,
- issuerNameSpaces,
- Algorithm.SHA256
- )
- msoGenerator.addDigestIdsForNamespace(nameSpaceName, digests)
- }
-
- val mso = msoGenerator.generate()
- val taggedEncodedMso = Cbor.encode(Tagged(Tagged.ENCODED_CBOR, Bstr(mso)))
-
- val issuerKeyPair = when (docType) {
- MVR_DOCTYPE -> KeysAndCertificates.getMekbDsKeyPair(context)
- MICOV_DOCTYPE -> KeysAndCertificates.getMicovDsKeyPair(context)
- else -> KeysAndCertificates.getMdlDsKeyPair(context)
- }
-
- val issuerCert = when (docType) {
- MVR_DOCTYPE -> KeysAndCertificates.getMekbDsCertificate(context)
- MICOV_DOCTYPE -> KeysAndCertificates.getMicovDsCertificate(context)
- else -> KeysAndCertificates.getMdlDsCertificate(context)
- }
-
- val encodedIssuerAuth = Cbor.encode(
- Cose.coseSign1Sign(
- issuerKeyPair.private.toEcPrivateKey(issuerKeyPair.public, EcCurve.P256),
- taggedEncodedMso,
- true,
- Algorithm.ES256,
- protectedHeaders = mapOf(
- Pair(
- CoseNumberLabel(Cose.COSE_LABEL_ALG),
- Algorithm.ES256.coseAlgorithmIdentifier.toDataItem()
- )
- ),
- unprotectedHeaders = mapOf(
- Pair(
- CoseNumberLabel(Cose.COSE_LABEL_X5CHAIN),
- X509CertChain(
- listOf(X509Cert(issuerCert.encoded))
- ).toDataItem()
- )
- ),
- ).toDataItem()
- )
-
- val issuerProvidedAuthenticationData = StaticAuthDataGenerator(
- MdocUtil.stripIssuerNameSpaces(issuerNameSpaces, dataElementExceptions),
- encodedIssuerAuth
- ).generate()
-
- pendingCred.certify(
- issuerProvidedAuthenticationData,
- validFrom,
- validUntil
- )
- }
- }
-
- // Puts the string "MSO ${counter}" on top of the portrait image.
- private fun overridePortrait(encodedPortrait: ByteArray, counter: Number): ByteArray {
- val options = BitmapFactory.Options()
- options.inMutable = true
- val bitmap = BitmapFactory.decodeByteArray(
- encodedPortrait,
- 0,
- encodedPortrait.size,
- options)
-
- val text = "MSO ${counter}"
- val canvas = Canvas(bitmap)
- val paint = Paint(Paint.ANTI_ALIAS_FLAG)
- paint.setColor(Color.WHITE)
- paint.textSize = bitmap.width / 5.0f
- paint.setShadowLayer(2.0f, 1.0f, 1.0f, Color.BLACK)
- val bounds = Rect()
- paint.getTextBounds(text, 0, text.length, bounds)
- val x: Float = (bitmap.width - bounds.width()) / 2.0f
- val y: Float = (bitmap.height - bounds.height()) / 4.0f
- canvas.drawText(text, x, y, paint)
-
- val baos = ByteArrayOutputStream()
- bitmap.compress(Bitmap.CompressFormat.JPEG, 50, baos)
- val encodedModifiedPortrait: ByteArray = baos.toByteArray()
-
- return encodedModifiedPortrait
- }
-
- companion object {
-
- const val CREDENTIAL_DOMAIN = "mdoc/MSO"
- private const val USER_VISIBLE_NAME = "userVisibleName"
- const val DOCUMENT_TYPE = "documentType"
- private const val DATE_PROVISIONED = "dateProvisioned"
- private const val CARD_ART = "cardArt"
- private const val IS_SELF_SIGNED = "isSelfSigned"
- private const val MAX_USAGES_PER_KEY = "maxUsagesPerCredential"
- private const val VALIDITY_IN_DAYS = "validityInDays"
- private const val MIN_VALIDITY_IN_DAYS = "minValidityInDays"
- private const val LAST_TIME_USED = "lastTimeUsed"
- private const val NUM_CREDENTIALS = "numCredentials"
- private const val AUTH_KEY_SETTINGS = "authKeySettings"
- private const val AUTH_KEY_SECURE_AREA_IDENTIFIER = "authKeySecureAreaIdentifier"
-
- private val dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
-
- @SuppressLint("StaticFieldLeak")
- @Volatile
- private var instance: ProvisioningUtil? = null
-
- fun getInstance(context: Context) = instance ?: synchronized(this) {
- instance ?: ProvisioningUtil(context).also { instance = it }
- }
-
- val defaultSecureArea: SecureArea
- get() = requireNotNull(instance?.secureAreaRepository?.implementations?.first())
-
- fun Document?.toDocumentInformation(): DocumentInformation? {
- return this?.let {
-
- val authKeySecureAreaIdentifier = it.applicationData.getString(AUTH_KEY_SECURE_AREA_IDENTIFIER)
- val authKeySecureArea = instance!!.secureAreaRepository.getImplementation(authKeySecureAreaIdentifier)
- ?: throw IllegalStateException("No Secure Area with id ${authKeySecureAreaIdentifier} for document ${it.name}")
-
- val credentials = certifiedCredentials.map { key ->
- key as MdocCredential
- val info = authKeySecureArea.getKeyInfo(key.alias)
- DocumentInformation.KeyData(
- counter = key.credentialCounter.toInt(),
- validFrom = key.validFrom.formatted(),
- validUntil = key.validUntil.formatted(),
- domain = key.domain,
- issuerDataBytesCount = key.issuerProvidedData.size,
- usagesCount = key.usageCount,
- keyPurposes = info.keyPurposes.first(),
- ecCurve = info.publicKey.curve,
- isHardwareBacked = false, // TODO: remove
- secureAreaDisplayName = authKeySecureArea.displayName
- )
- }
- val lastTimeUsedMillis = it.applicationData.getNumber(LAST_TIME_USED)
- val lastTimeUsed = if (lastTimeUsedMillis == -1L) {
- ""
- } else {
- Instant.fromEpochMilliseconds(lastTimeUsedMillis).formatted()
- }
- DocumentInformation(
- userVisibleName = it.applicationData.getString(USER_VISIBLE_NAME),
- docName = it.name,
- docType = it.applicationData.getString(DOCUMENT_TYPE),
- dateProvisioned = it.applicationData.getString(DATE_PROVISIONED),
- documentColor = it.applicationData.getNumber(CARD_ART).toInt(),
- selfSigned = it.applicationData.getBoolean(IS_SELF_SIGNED),
- maxUsagesPerKey = it.applicationData.getNumber(MAX_USAGES_PER_KEY).toInt(),
- lastTimeUsed = lastTimeUsed,
- authKeys = credentials
- )
- }
- }
-
- private fun Instant.formatted(): String {
- val javaInstant = JavaInstant.ofEpochMilli(this.toEpochMilliseconds())
- val dateTime = ZonedDateTime.ofInstant(javaInstant, ZoneId.systemDefault())
- return dateTimeFormatter.format(dateTime)
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/RefreshKeysWorker.kt b/appholder/src/main/java/com/android/identity/wallet/util/RefreshKeysWorker.kt
deleted file mode 100644
index c73520336..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/RefreshKeysWorker.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.content.Context
-import androidx.work.Worker
-import androidx.work.WorkerParameters
-import com.android.identity.wallet.document.DocumentManager
-
-class RefreshKeysWorker(
- context: Context,
- params: WorkerParameters
-) : Worker(context, params) {
-
- private val documentManager = DocumentManager.getInstance(context)
- private val provisioningUtil = ProvisioningUtil.getInstance(context)
-
- override fun doWork(): Result {
- documentManager.getDocuments().forEach { documentInformation ->
- val document = documentManager.getDocumentByName(documentInformation.docName)
- document?.let { provisioningUtil.refreshCredentials(it, documentInformation.docType) }
- }
- return Result.success()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/SampleDataProvider.kt b/appholder/src/main/java/com/android/identity/wallet/util/SampleDataProvider.kt
deleted file mode 100644
index 81f287011..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/SampleDataProvider.kt
+++ /dev/null
@@ -1,342 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import com.android.identity.documenttype.DocumentAttributeType
-import com.android.identity.wallet.R
-
-object SampleDataProvider {
- const val MDL_NAMESPACE = "org.iso.18013.5.1"
- const val AAMVA_NAMESPACE = "org.iso.18013.5.1.aamva"
- const val MVR_NAMESPACE = "nl.rdw.mekb.1"
- const val MICOV_ATT_NAMESPACE = "org.micov.attestation.1"
- const val MICOV_VTR_NAMESPACE = "org.micov.vtr.1"
- const val EUPID_NAMESPACE = "eu.europa.ec.eudi.pid.1"
-
- fun getSampleValue(
- context: Context,
- namespace: String,
- identifier: String,
- type: DocumentAttributeType,
- identifierParent: String? = null
- ): Any? {
- return when (namespace) {
- MDL_NAMESPACE -> when (identifier) {
- "family_name" -> "Mustermann"
- "given_name" -> "Erika"
- "birth_date" -> "1971-09-01"
- "issue_date" -> "2021-04-18"
- "expiry_date" -> "2026-04-18"
- "issuing_country" -> "US"
- "issuing_authority" -> "Google"
- "document_number" -> "987654321"
- "portrait" -> BitmapFactory.decodeResource(
- context.resources,
- R.drawable.img_erika_portrait
- )
-
- "un_distinguishing_sign" -> "USA"
- "administrative_number" -> "123456789"
- "sex" -> 2
- "height" -> 175
- "weight" -> 68
- "eye_colour" -> "blue"
- "hair_colour" -> "blond"
- "birth_place" -> "Sample City"
- "resident_address" -> "Sample address"
- "portrait_capture_date" -> "2021-04-18"
- "age_in_years" -> 52
- "age_birth_year" -> 1971
- "age_over_18" -> true
- "age_over_21" -> true
- "age_over_25" -> true
- "age_over_62" -> false
- "age_over_65" -> false
- "issuing_jurisdiction" -> "Sample issuing jurisdiction"
- "nationality" -> "US"
- "resident_city" -> "Sample City"
- "resident_state" -> "Sample State"
- "resident_postal_code" -> "18013"
- "resident_country" -> "US"
- "family_name_national_character" -> "Бабіак"
- "given_name_national_character" -> "Ерика"
- "signature_usual_mark" -> BitmapFactory.decodeResource(
- context.resources,
- R.drawable.img_erika_signature
- )
-
- "biometric_template_face",
- "biometric_template_finger",
- "biometric_template_signature_sign",
- "biometric_template_iris" -> Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888)
-
- else -> defaultValue(type)
- }
-
- AAMVA_NAMESPACE -> when (identifier) {
- "name_suffix" -> "SR"
- "organ_donor" -> 1
- "veteran" -> null
- "family_name_truncation" -> "N"
- "given_name_truncation" -> "N"
- "aka_family_name" -> "Muster"
- "aka_given_name" -> "Erik"
- "aka_suffix" -> "JR"
- "weight_range" -> 3
- "race_ethnicity" -> "W"
- "DHS_compliance" -> "F"
- "DHS_temporary_lawful_status" -> null
- "EDL_credential" -> null
- "resident_county" -> "123"
- "hazmat_endorsement_expiration_date" -> "2026-04-18"
- "sex" -> 2
- "audit_information" -> "Sample auditor"
- "aamva_version" -> 2
- "domestic_vehicle_class_code" -> "B"
- "domestic_vehicle_class_description" -> "Light vehicles"
- "issue_date" -> "2021-04-18"
- "expiry_date" -> "2026-04-18"
- else -> defaultValue(type)
- }
-
- MVR_NAMESPACE -> when (identifier) {
- "issue_date" -> "2021-04-18"
- "vin" -> "1M8GDM9AXKP042788"
- "issuingCountry" -> "NL"
- "competentAuthority" -> "RDW"
- "registrationNumber" -> "E-01-23"
- "validFrom" -> "2021-04-19"
- "validUntil" -> "2023-04-20"
- "ownershipStatus" -> 2
- "name" -> "Erika"
- "streetName" -> "Teststraat"
- "houseNumber" -> "86"
- "houseNumberSuffix" -> "A"
- "postalCode" -> "1234 AA"
- "placeOfResidence" -> "Samplecity"
- "make" -> "Dummymobile"
- else -> defaultValue(type)
- }
-
- MICOV_ATT_NAMESPACE -> when (identifier) {
- "1D47_vaccinated" -> true
- "RA01_vaccinated" -> true
- "fac" -> BitmapFactory.decodeResource(
- context.resources,
- R.drawable.img_erika_portrait
- )
-
- "fni" -> "M"
- "gni" -> "E"
- "by" -> 1964
- "bm" -> 8
- "bd" -> 12
- "Result" -> "260415000"
- "TypeOfTest" -> "LP6464-4"
- "TimeOfTest" -> "2021-10-12"
- "SeCondFulfilled" -> true
- "SeCondType" -> "leisure"
- "SeCondExpiry" -> "2021-10-13"
- else -> defaultValue(type)
- }
-
- MICOV_VTR_NAMESPACE -> when (identifier) {
- "fn" -> "Mustermann"
- "gn" -> "Erika"
- "dob" -> "1964-08-12"
- "sex" -> 2
- "tg" -> "840539006"
- "vp" -> "1119349007"
- "mp" -> "EU/1/20/1528"
- "br" -> "Sample brand"
- "ma" -> "ORG-100030215"
- "bn" -> when (identifierParent != null && identifierParent == "v_RA01_1") {
- true -> "B12345/67"
- else -> "B67890/12"
- }
-
- "dn" -> when (identifierParent != null && identifierParent == "v_RA01_1") {
- true -> 1
- else -> 2
- }
-
- "sd" -> 2
- "dt" -> when (identifierParent != null && identifierParent == "v_RA01_1") {
- true -> "2021-04-08"
- else -> "2021-05-18"
- }
-
- "co" -> "US"
- "ao" -> "RHI"
- "ap" -> ""
- "nx" -> "2021-05-20"
- "is" -> "SC17"
- "ci" -> when (identifierParent != null && identifierParent == "v_RA01_1") {
- true -> "URN:UVCI:01:UT:187/37512422923"
- else -> "URN:UVCI:01:UT:187/37512533044"
- }
-
- "pd" -> ""
- "vf" -> "2021-05-27"
- "vu" -> "2022-05-27"
- "pty" -> when (identifierParent != null && identifierParent == "pid_PPN") {
- true -> "PPN"
- else -> "DL"
- }
-
- "pnr" -> when (identifierParent != null && identifierParent == "pid_PPN") {
- true -> "476284728"
- else -> "987654321"
- }
-
- "pic" -> "US"
- "pia" -> ""
- else -> defaultValue(type)
- }
-
- EUPID_NAMESPACE -> when (identifier) {
- "family_name" -> "Mustermann"
- "family_name_national_characters" -> "Бабіак"
- "given_name" -> "Erika"
- "given_name_national_characters" -> "Ерика"
- "birth_date" -> "1986-03-14"
- "persistent_id" -> "0128196532"
- "family_name_birth" -> "Mustermann"
- "family_name_birth_national_characters" -> "Бабіак"
- "given_name_birth" -> "Erika"
- "given_name_birth_national_characters" -> "Ерика"
- "birth_place" -> "Place of birth"
- "resident_address" -> "Resident address"
- "resident_city" -> "Resident City"
- "resident_postal_code" -> "Resident postal code"
- "resident_state" -> "Resident state"
- "resident_country" -> "Resident country"
- "gender" -> "female"
- "nationality" -> "NL"
- "portrait" -> BitmapFactory.decodeResource(
- context.resources,
- R.drawable.img_erika_portrait
- )
- "portrait_capture_date" -> "2022-11-14"
- "biometric_template_finger" -> Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888)
- "age_over_13" -> true
- "age_over_16" -> true
- "age_over_18" -> true
- "age_over_21" -> true
- "age_over_60" -> false
- "age_over_65" -> false
- "age_over_68" -> false
- "age_in_years" -> 37
- "age_birth_year" -> 1986
- else -> defaultValue(type)
- }
-
- else -> defaultValue(type)
- }
- }
-
- fun getSampleValue(namespace: String, identifier: String, type: DocumentAttributeType, index: Int): Any? {
- return when (namespace) {
- MDL_NAMESPACE -> when (identifier) {
- "vehicle_category_code" -> when (index) {
- 0 -> "A"
- else -> "B"
- }
-
- "issue_date" -> when (index) {
- 0 -> "2018-08-09"
- else -> "2017-02-23"
- }
-
- "expiry_date" -> when (index) {
- 0 -> "2024-10-20"
- else -> "2024-10-20"
- }
-
- "code" -> when (index) {
- 0 -> "S01"
- else -> "S02"
- }
-
- "sign" -> when (index) {
- 0 -> "<="
- else -> "="
- }
-
- "value" -> when (index) {
- 0 -> "2500"
- else -> "8"
- }
-
- else -> defaultValue(type)
- }
-
- AAMVA_NAMESPACE -> when (identifier) {
- "domestic_vehicle_restriction_code" -> when (index) {
- 0 -> "B"
- else -> "C"
- }
-
- "domestic_vehicle_restriction_description" -> when (index) {
- 0 -> "Corrective lenses must be worn"
- else -> "Mechanical Aid (special brakes, hand controls, or other adaptive devices)"
- }
-
- "domestic_vehicle_endorsement_code" -> when (index) {
- 0 -> "P"
- else -> "S"
- }
-
- "domestic_vehicle_endorsement_description" -> when (index) {
- 0 -> "Passenger"
- else -> "School Bus"
- }
-
- else -> defaultValue(type)
- }
-
- else -> defaultValue(type)
- }
- }
-
- fun getArrayLength(namespace: String, identifier: String): Int {
- return when (namespace) {
- MDL_NAMESPACE -> when (identifier) {
- "driving_privileges" -> 2
- "codes" -> 2
- else -> 2
- }
-
- AAMVA_NAMESPACE -> when (identifier) {
- "domestic_driving_privileges" -> 1
- "domestic_vehicle_restrictions" -> 2
- "domestic_vehicle_endorsements" -> 2
- else -> 2
- }
-
- else -> 2
- }
- }
-
- private fun defaultValue(type: DocumentAttributeType): Any? {
- return when (type) {
- is DocumentAttributeType.Blob -> byteArrayOf()
- is DocumentAttributeType.String -> "-"
- is DocumentAttributeType.Number -> 0
- is DocumentAttributeType.Date,
- is DocumentAttributeType.DateTime -> "2100-01-01"
-
- is DocumentAttributeType.Picture -> Bitmap.createBitmap(
- 200,
- 200,
- Bitmap.Config.ARGB_8888
- )
-
- is DocumentAttributeType.Boolean -> false
- is DocumentAttributeType.StringOptions,
- is DocumentAttributeType.IntegerOptions,
- is DocumentAttributeType.ComplexType -> null
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/SavedStateHandleExtensions.kt b/appholder/src/main/java/com/android/identity/wallet/util/SavedStateHandleExtensions.kt
deleted file mode 100644
index 2fc445ce0..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/SavedStateHandleExtensions.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.identity.wallet.util
-
-import androidx.lifecycle.SavedStateHandle
-
-fun SavedStateHandle.updateState(block: (T) -> T) {
- val prevValue = get("state")!!
- val nextValue = block(prevValue)
- set("state", nextValue)
-}
-
-fun SavedStateHandle.getState(initialState: T) = getStateFlow("state", initialState)
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/TransferStatus.kt b/appholder/src/main/java/com/android/identity/wallet/util/TransferStatus.kt
deleted file mode 100644
index c41943ef5..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/TransferStatus.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.identity.wallet.util
-
-enum class TransferStatus {
- ENGAGEMENT_DETECTED,
- CONNECTING,
- CONNECTED,
- REQUEST,
- REQUEST_SERVED,
- DISCONNECTED,
- ERROR
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/util/ViewHelper.kt b/appholder/src/main/java/com/android/identity/wallet/util/ViewHelper.kt
deleted file mode 100644
index a4f72f9ff..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/util/ViewHelper.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.android.identity.wallet.util
-
-import android.graphics.Bitmap
-import com.android.identity.documenttype.DocumentAttributeType
-import com.android.identity.documenttype.IntegerOption
-import com.android.identity.documenttype.StringOption
-
-data class Field(
- val id: Int,
- val label: String,
- val name: String,
- val fieldType: DocumentAttributeType,
- val value: Any?,
- val namespace: String? = null,
- val isArray: Boolean = false,
- val parentId: Int? = null,
- var stringOptions: List? = null,
- var integerOptions: List? = null
-) {
- fun hasValue(): Boolean {
- return value != ""
- }
-
- fun getValueLong(): Long {
- return value?.toString()?.toLong() ?: 0
- }
-
- fun getValueString(): String {
- return value as String
- }
-
- fun getValueBoolean(): Boolean {
- return value as Boolean
- }
-
- fun getValueBitmap(): Bitmap {
- return value as Bitmap
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/viewmodel/SelfSignedViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/viewmodel/SelfSignedViewModel.kt
deleted file mode 100644
index afb48c285..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/viewmodel/SelfSignedViewModel.kt
+++ /dev/null
@@ -1,286 +0,0 @@
-package com.android.identity.wallet.viewmodel
-
-import android.app.Application
-import android.view.View
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.viewModelScope
-import com.android.identity.documenttype.DocumentAttributeType
-import com.android.identity.documenttype.MdocDocumentType
-import com.android.identity.documenttype.MdocDataElement
-import com.android.identity.wallet.HolderApp
-import com.android.identity.wallet.document.DocumentManager
-import com.android.identity.wallet.documentdata.MdocComplexTypeDefinition
-import com.android.identity.wallet.documentdata.MdocComplexTypeRepository
-import com.android.identity.wallet.selfsigned.SelfSignedDocumentData
-import com.android.identity.wallet.util.Field
-import com.android.identity.wallet.util.SampleDataProvider
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class SelfSignedViewModel(val app: Application) :
- AndroidViewModel(app) {
-
- companion object {
- private const val LOG_TAG = "SelfSignedViewModel"
- }
-
- private val documentManager = DocumentManager.getInstance(app.applicationContext)
- val loading = MutableLiveData()
- val created = MutableLiveData()
- private val fieldsByDocType: MutableMap> = mutableMapOf()
- private var id = 1
-
- init {
- loading.value = View.GONE
- for (documentType in HolderApp.documentTypeRepositoryInstance.documentTypes
- .filter { it.mdocDocumentType != null }) {
- id = 1 // reset the id to 1
- fieldsByDocType[documentType.mdocDocumentType?.docType!!] =
- createFields(documentType.mdocDocumentType!!)
- }
- }
-
- fun getFields(docType: String): MutableList {
- return fieldsByDocType[docType]
- ?: throw IllegalArgumentException("No field list valid for $docType")
- }
-
- fun createSelfSigned(documentData: SelfSignedDocumentData) {
- loading.value = View.VISIBLE
- viewModelScope.launch {
- withContext(Dispatchers.IO) {
- documentManager.createSelfSignedDocument(documentData)
- }
- created.value = true
- loading.value = View.GONE
- }
- }
-
- private fun createFields(mdocDocumentType: MdocDocumentType): MutableList {
- val fields: MutableList = mutableListOf()
- val complexTypes = MdocComplexTypeRepository.getComplexTypes(mdocDocumentType.docType)
- for (namespace in mdocDocumentType.namespaces.values) {
- val namespaceComplexTypes =
- complexTypes?.namespaces?.find { it.namespace == namespace.namespace }
- for (dataElement in namespace.dataElements.values) {
- when (dataElement.attribute.type) {
- is DocumentAttributeType.ComplexType -> {
- val complexTypeDefinitions = namespaceComplexTypes?.dataElements?.filter {
- it.parentIdentifiers.contains(dataElement.attribute.identifier)
- }
-
- if (complexTypeDefinitions?.first()?.partOfArray == true) {
- val arrayLength =
- SampleDataProvider.getArrayLength(
- namespace.namespace,
- dataElement.attribute.identifier
- )
- val parentField = Field(
- id++,
- "${dataElement.attribute.displayName} ($arrayLength items)",
- dataElement.attribute.identifier,
- dataElement.attribute.type,
- null,
- namespace = namespace.namespace,
- isArray = true,
- )
- fields.add(parentField)
- addArrayFields(
- parentField,
- fields,
- namespaceComplexTypes.dataElements)
- } else {
- val parentField = Field(
- id++,
- dataElement.attribute.displayName,
- dataElement.attribute.identifier,
- dataElement.attribute.type,
- null,
- namespace = namespace.namespace
- )
- fields.add(parentField)
- addMapFields(
- parentField,
- fields,
- namespaceComplexTypes?.dataElements!!)
- }
- }
-
- else -> {
-
- val sampleValue = SampleDataProvider.getSampleValue(
- app,
- namespace.namespace,
- dataElement.attribute.identifier,
- dataElement.attribute.type
- )
- val field = Field(
- id++,
- dataElement.attribute.displayName,
- dataElement.attribute.identifier,
- dataElement.attribute.type,
- sampleValue,
- namespace = namespace.namespace
- )
- addOptions(field, dataElement)
- fields.add(field)
- }
- }
- }
- }
- return fields
- }
-
-
- private fun addArrayFields(
- parentField: Field,
- fields: MutableList,
- dataElements: List,
- prefix: String = ""
- ) {
- val arrayLength =
- SampleDataProvider.getArrayLength(parentField.namespace!!, parentField.name)
- val childElements = dataElements.filter { it.parentIdentifiers.contains(parentField.name) }
- for (i in 0..arrayLength - 1) {
- for (childElement in childElements) {
- if (childElement.type is DocumentAttributeType.ComplexType) {
-
- if (dataElements.any { it.parentIdentifiers.contains(childElement.identifier) && it.partOfArray }) {
- val childField = Field(
- id++,
- "$prefix${i + 1} | ${childElement.displayName} (${
- SampleDataProvider.getArrayLength(
- parentField.namespace,
- childElement.identifier
- )
- } items)",
- childElement.identifier,
- childElement.type,
- null,
- namespace = parentField.namespace,
- isArray = true,
- parentId = parentField.id
- )
- fields.add(childField)
- addArrayFields(
- childField,
- fields,
- dataElements,
- "$prefix${i + 1} | "
- )
- } else {
- val childField = Field(
- id++,
- "$prefix${i + 1} | ${childElement.displayName}",
- childElement.identifier,
- childElement.type,
- null,
- namespace = parentField.namespace,
- parentId = parentField.id
- )
- fields.add(childField)
- addMapFields(
- childField,
- fields,
- dataElements,
- "$prefix${i + 1} | "
- )
- }
- } else {
- val sampleValue =
- SampleDataProvider.getSampleValue(parentField.namespace, childElement.identifier, childElement.type, i)
- val childField = Field(
- id++,
- "$prefix${i + 1} | ${childElement.displayName}",
- childElement.identifier,
- childElement.type,
- sampleValue,
- namespace = parentField.namespace,
- parentId = parentField.id
- )
- addOptions(childField, childElement)
- fields.add(childField)
- }
- }
- }
- }
-
- private fun addMapFields(
- parentField: Field,
- fields: MutableList,
- dataElements: List,
- prefix: String = ""
- ) {
-
- val childElements = dataElements.filter { it.parentIdentifiers.contains(parentField.name) }
- for (childElement in childElements) {
- if (childElement.type is DocumentAttributeType.ComplexType) {
- val isArray = dataElements.any { it.parentIdentifiers.contains(childElement.identifier) && it.partOfArray }
- val childField = Field(
- id++,
- "$prefix${childElement.displayName}",
- childElement.identifier,
- childElement.type,
- null,
- namespace = parentField.namespace,
- isArray = isArray,
- parentId = parentField.id
- )
- fields.add(childField)
- if (isArray){
- addArrayFields(childField, fields, dataElements, prefix)
- } else {
- addMapFields(childField, fields, dataElements, prefix)
- }
- } else {
- val sampleValue =
- SampleDataProvider.getSampleValue(
- app,
- parentField.namespace!!,
- childElement.identifier,
- childElement.type
- )
- val childField = Field(
- id++,
- "$prefix${childElement.displayName}",
- childElement.identifier,
- childElement.type,
- sampleValue,
- namespace = parentField.namespace,
- parentId = parentField.id
- )
- addOptions(childField, childElement)
- fields.add(childField)
- }
-
- }
- }
-
- fun addOptions(field: Field, dataElement: MdocDataElement) {
- when (dataElement.attribute.type) {
- is DocumentAttributeType.StringOptions -> field.stringOptions =
- (dataElement.attribute.type as DocumentAttributeType.StringOptions).options
-
- is DocumentAttributeType.IntegerOptions -> field.integerOptions =
- (dataElement.attribute.type as DocumentAttributeType.IntegerOptions).options
-
- else -> {}
- }
- }
-
- fun addOptions(field: Field, dataElement: MdocComplexTypeDefinition) {
- when (dataElement.type) {
- is DocumentAttributeType.StringOptions -> field.stringOptions =
- dataElement.type.options
-
- is DocumentAttributeType.IntegerOptions -> field.integerOptions =
- dataElement.type.options
-
- else -> {}
- }
- }
-
-}
-
diff --git a/appholder/src/main/java/com/android/identity/wallet/viewmodel/ShareDocumentViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/viewmodel/ShareDocumentViewModel.kt
deleted file mode 100644
index 8e98d874c..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/viewmodel/ShareDocumentViewModel.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.android.identity.wallet.viewmodel
-
-import android.app.Application
-import android.view.View
-import androidx.databinding.ObservableField
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.LiveData
-import com.android.identity.mdoc.origininfo.OriginInfo
-import com.android.identity.wallet.transfer.TransferManager
-import com.android.identity.wallet.util.TransferStatus
-
-class ShareDocumentViewModel(val app: Application) : AndroidViewModel(app) {
-
- private val transferManager = TransferManager.getInstance(app.applicationContext)
- var deviceEngagementQr = ObservableField()
- var message = ObservableField()
- private var hasStarted = false
-
- fun getTransferStatus(): LiveData = transferManager.getTransferStatus()
-
- fun startPresentationReverseEngagement(
- reverseEngagementUri: String,
- originInfos: List
- ) {
- if (!hasStarted) {
- transferManager.startPresentationReverseEngagement(reverseEngagementUri, originInfos)
- hasStarted = true
- }
- }
-
- fun cancelPresentation() {
- transferManager.stopPresentation(
- sendSessionTerminationMessage = true,
- useTransportSpecificSessionTermination = false
- )
- hasStarted = false
- message.set("Presentation canceled")
- }
-
- fun showQrCode() {
- deviceEngagementQr.set(transferManager.getDeviceEngagementQrCode())
- }
-
- fun triggerQrEngagement() {
- transferManager.startQrEngagement()
- }
-}
diff --git a/appholder/src/main/java/com/android/identity/wallet/viewmodel/TransferDocumentViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/viewmodel/TransferDocumentViewModel.kt
deleted file mode 100644
index cbe4f224b..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/viewmodel/TransferDocumentViewModel.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-package com.android.identity.wallet.viewmodel
-
-import android.app.Application
-import android.view.View
-import androidx.databinding.ObservableField
-import androidx.databinding.ObservableInt
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.viewModelScope
-import com.android.identity.mdoc.credential.MdocCredential
-import com.android.identity.mdoc.request.DeviceRequestParser
-import com.android.identity.mdoc.response.DeviceResponseGenerator
-import com.android.identity.securearea.KeyUnlockData
-import com.android.identity.util.Constants.DEVICE_RESPONSE_STATUS_OK
-import com.android.identity.wallet.R
-import com.android.identity.wallet.authconfirmation.RequestedDocumentData
-import com.android.identity.wallet.authconfirmation.RequestedElement
-import com.android.identity.wallet.authconfirmation.SignedElementsCollection
-import com.android.identity.wallet.document.DocumentInformation
-import com.android.identity.wallet.document.DocumentManager
-import com.android.identity.wallet.transfer.AddDocumentToResponseResult
-import com.android.identity.wallet.transfer.TransferManager
-import com.android.identity.wallet.util.PreferencesHelper
-import com.android.identity.wallet.util.TransferStatus
-import com.android.identity.wallet.util.logWarning
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class TransferDocumentViewModel(val app: Application) : AndroidViewModel(app) {
-
- private val transferManager = TransferManager.getInstance(app.applicationContext)
- private val documentManager = DocumentManager.getInstance(app.applicationContext)
- private val signedElements = SignedElementsCollection()
- private val requestedElements = mutableListOf()
- private val closeConnectionMutableLiveData = MutableLiveData()
- private val selectedDocuments = mutableListOf()
-
- var inProgress = ObservableInt(View.GONE)
- var documentsSent = ObservableField()
- val connectionClosedLiveData: LiveData = closeConnectionMutableLiveData
-
- private val mutableConfirmationState = MutableLiveData()
- val authConfirmationState: LiveData = mutableConfirmationState
-
- fun onAuthenticationCancelled() {
- mutableConfirmationState.value = true
- }
-
- fun onAuthenticationCancellationConsumed() {
- mutableConfirmationState.value = null
- }
-
- fun getTransferStatus(): LiveData =
- transferManager.getTransferStatus()
-
- fun getRequestedDocuments(): Collection =
- transferManager.documentRequests()
-
- fun getDocuments() = documentManager.getDocuments()
-
- fun getSelectedDocuments() = selectedDocuments
-
- fun requestedElements() = requestedElements
-
- fun closeConnection() {
- cleanUp()
- closeConnectionMutableLiveData.value = true
- }
-
- fun addDocumentForSigning(document: RequestedDocumentData) {
- signedElements.addNamespace(document)
- }
-
- fun toggleSignedElement(element: RequestedElement) {
- signedElements.toggleProperty(element)
- }
-
- fun createSelectedItemsList() {
- val ownDocuments = getSelectedDocuments()
- val requestedDocuments = getRequestedDocuments()
- val result = mutableListOf()
- requestedDocuments.forEach { requestedDocument ->
- try {
- val ownDocument = ownDocuments.first { it.docType == requestedDocument.docType }
- val issuerSignedEntriesToRequest = requestedElementsFrom(requestedDocument)
- result.add(
- RequestedDocumentData(
- userReadableName = ownDocument.userVisibleName,
- identityCredentialName = ownDocument.docName,
- requestedElements = issuerSignedEntriesToRequest,
- requestedDocument = requestedDocument
- )
- )
- } catch (e: NoSuchElementException) {
- logWarning("No document for docType " + requestedDocument.docType)
- }
- }
- requestedElements.addAll(result)
- }
-
- fun sendResponseForSelection(
- onResultReady: (result: AddDocumentToResponseResult) -> Unit,
- credential: MdocCredential? = null,
- authKeyUnlockData: KeyUnlockData? = null
- ) {
- val elementsToSend = signedElements.collect()
- val responseGenerator = DeviceResponseGenerator(DEVICE_RESPONSE_STATUS_OK)
- viewModelScope.launch {
- elementsToSend.forEach { signedDocument ->
- try {
- val issuerSignedEntries = signedDocument.issuerSignedEntries()
- val result = withContext(Dispatchers.IO) { //<- Offload from UI thread
- transferManager.addDocumentToResponse(
- signedDocument.identityCredentialName,
- signedDocument.documentType,
- issuerSignedEntries,
- responseGenerator,
- credential,
- authKeyUnlockData,
- )
- }
- if (result !is AddDocumentToResponseResult.DocumentAdded) {
- onResultReady(result)
- return@forEach
- }
- transferManager.sendResponse(
- responseGenerator.generate(),
- PreferencesHelper.isConnectionAutoCloseEnabled()
- )
- transferManager.setResponseServed()
- val documentsCount = elementsToSend.count()
- documentsSent.set(app.getString(R.string.txt_documents_sent, documentsCount as Int))
- cleanUp()
- onResultReady(result)
- /*
- } catch (e: CredentialInvalidatedException) {
- logWarning("Credential '${signedDocument.identityCredentialName}' is invalid. Deleting.")
- documentManager.deleteCredentialByName(signedDocument.identityCredentialName)
- Toast.makeText(
- app.applicationContext, "Deleting invalid document "
- + signedDocument.identityCredentialName,
- Toast.LENGTH_SHORT
- ).show()
- */
- } catch (e: NoSuchElementException) {
- logWarning("No requestedDocument for " + signedDocument.documentType)
- }
- }
- }
- }
-
- fun cancelPresentation(
- sendSessionTerminationMessage: Boolean,
- useTransportSpecificSessionTermination: Boolean
- ) {
- transferManager.stopPresentation(
- sendSessionTerminationMessage,
- useTransportSpecificSessionTermination
- )
- }
-
- private fun requestedElementsFrom(
- requestedDocument: DeviceRequestParser.DocRequest
- ): ArrayList {
- val result = arrayListOf()
- requestedDocument.namespaces.forEach { namespace ->
- val elements = requestedDocument.getEntryNames(namespace).map { element ->
- RequestedElement(namespace, element)
- }
- result.addAll(elements)
- }
- return result
- }
-
- private fun cleanUp() {
- requestedElements.clear()
- signedElements.clear()
- selectedDocuments.clear()
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/wallet/DocumentPageTransformer.kt b/appholder/src/main/java/com/android/identity/wallet/wallet/DocumentPageTransformer.kt
deleted file mode 100644
index ab7f3ac23..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/wallet/DocumentPageTransformer.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.android.identity.wallet.wallet
-
-import android.content.Context
-import android.view.View
-import androidx.viewpager2.widget.ViewPager2
-import com.android.identity.wallet.R
-import kotlin.math.abs
-
-class DocumentPageTransformer(context: Context) : ViewPager2.PageTransformer {
- private val resources = context.resources
- private val nextItemVisiblePx = resources.getDimension(R.dimen.viewpager_next_item_visible)
- private val currentItemHorizontalMarginPx = resources.getDimension(R.dimen.viewpager_current_item_horizontal_margin)
- private val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
-
- override fun transformPage(page: View, position: Float) {
- page.translationX = -pageTranslationX * position
- page.scaleY = 1 - (0.25f * abs(position))
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/wallet/DocumentPagerItemDecoration.kt b/appholder/src/main/java/com/android/identity/wallet/wallet/DocumentPagerItemDecoration.kt
deleted file mode 100644
index c9903b79d..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/wallet/DocumentPagerItemDecoration.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.android.identity.wallet.wallet
-
-import android.content.Context
-import android.graphics.Rect
-import android.view.View
-import androidx.annotation.DimenRes
-import androidx.recyclerview.widget.RecyclerView
-
-class DocumentPagerItemDecoration(
- context: Context,
- @DimenRes horizontalMarginInDp: Int
-) : RecyclerView.ItemDecoration() {
-
- private val horizontalMarginInPx: Int =
- context.resources.getDimension(horizontalMarginInDp).toInt()
-
- override fun getItemOffsets(
- outRect: Rect,
- view: View,
- parent: RecyclerView,
- state: RecyclerView.State
- ) {
- outRect.right = horizontalMarginInPx
- outRect.left = horizontalMarginInPx
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/java/com/android/identity/wallet/wallet/SelectDocumentFragment.kt b/appholder/src/main/java/com/android/identity/wallet/wallet/SelectDocumentFragment.kt
deleted file mode 100644
index 14ad18614..000000000
--- a/appholder/src/main/java/com/android/identity/wallet/wallet/SelectDocumentFragment.kt
+++ /dev/null
@@ -1,188 +0,0 @@
-package com.android.identity.wallet.wallet
-
-import android.Manifest
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Toast
-import androidx.activity.OnBackPressedCallback
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.navigation.fragment.findNavController
-import com.android.identity.wallet.R
-import com.android.identity.wallet.adapter.DocumentAdapter
-import com.android.identity.wallet.databinding.FragmentSelectDocumentBinding
-import com.android.identity.wallet.document.DocumentInformation
-import com.android.identity.wallet.document.DocumentManager
-import com.android.identity.wallet.util.TransferStatus
-import com.android.identity.wallet.util.log
-import com.android.identity.wallet.viewmodel.ShareDocumentViewModel
-import com.google.android.material.tabs.TabLayoutMediator
-
-class SelectDocumentFragment : Fragment() {
-
- private var _binding: FragmentSelectDocumentBinding? = null
- private val binding get() = _binding!!
-
- private val viewModel: ShareDocumentViewModel by activityViewModels()
- private val timeInterval = 2000 // # milliseconds passed between two back presses
- private var mBackPressed: Long = 0
-
- private val appPermissions: Array =
- if (android.os.Build.VERSION.SDK_INT >= 31) {
- arrayOf(
- Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.BLUETOOTH_ADVERTISE,
- Manifest.permission.BLUETOOTH_SCAN,
- Manifest.permission.BLUETOOTH_CONNECT,
- )
- } else {
- arrayOf(
- Manifest.permission.ACCESS_FINE_LOCATION,
- )
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // Ask to press twice before leave the app
- requireActivity().onBackPressedDispatcher.addCallback(
- this,
- object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- if (mBackPressed + timeInterval > System.currentTimeMillis()) {
- requireActivity().finish()
- return
- } else {
- Toast.makeText(
- requireContext(),
- R.string.toast_press_back_twice,
- Toast.LENGTH_SHORT
- ).show()
- }
- mBackPressed = System.currentTimeMillis()
- }
- })
- }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentSelectDocumentBinding.inflate(inflater)
- val adapter = DocumentAdapter()
- binding.vpDocuments.adapter = adapter
- binding.fragment = this
- setupDocumentsPager(binding)
-
- val documentManager = DocumentManager.getInstance(requireContext())
- setupScreen(binding, adapter, documentManager.getDocuments().toMutableList())
-
- val permissionsNeeded = appPermissions.filter { permission ->
- ContextCompat.checkSelfPermission(
- requireContext(),
- permission
- ) != PackageManager.PERMISSION_GRANTED
- }
-
- if (permissionsNeeded.isNotEmpty()) {
- permissionsLauncher.launch(
- permissionsNeeded.toTypedArray()
- )
- }
-
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- viewModel.getTransferStatus().observe(viewLifecycleOwner) {
- when (it) {
- TransferStatus.CONNECTED -> {
- openTransferScreen()
- }
-
- TransferStatus.ERROR -> {
- binding.tvNfcLabel.text = "Error on presentation!"
- }
- //Shall we update the top label of the screen for each state?
- else -> {}
- }
- }
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- private fun setupDocumentsPager(binding: FragmentSelectDocumentBinding) {
- TabLayoutMediator(binding.tlPageIndicator, binding.vpDocuments) { _, _ -> }.attach()
- binding.vpDocuments.offscreenPageLimit = 1
- binding.vpDocuments.setPageTransformer(DocumentPageTransformer(requireContext()))
- val itemDecoration = DocumentPagerItemDecoration(
- requireContext(),
- R.dimen.viewpager_current_item_horizontal_margin
- )
- binding.vpDocuments.addItemDecoration(itemDecoration)
- }
-
- private fun setupScreen(
- binding: FragmentSelectDocumentBinding,
- adapter: DocumentAdapter,
- documentsList: MutableList
- ) {
- if (documentsList.isEmpty()) {
- showEmptyView(binding)
- } else {
- adapter.submitList(documentsList)
- showDocumentsPager(binding)
- }
- }
-
- private fun openTransferScreen() {
- val destination = SelectDocumentFragmentDirections.toTransferDocument()
- findNavController().navigate(destination)
- }
-
- private fun showEmptyView(binding: FragmentSelectDocumentBinding) {
- binding.vpDocuments.visibility = View.GONE
- binding.cvEmptyView.visibility = View.VISIBLE
- binding.btShowQr.visibility = View.GONE
- binding.btAddDocument.setOnClickListener { openAddDocument() }
- }
-
- private fun showDocumentsPager(binding: FragmentSelectDocumentBinding) {
- binding.vpDocuments.visibility = View.VISIBLE
- binding.cvEmptyView.visibility = View.GONE
- binding.btShowQr.visibility = View.VISIBLE
- binding.btShowQr.setOnClickListener { displayQRCode() }
- }
-
- private fun displayQRCode() {
- val destination = SelectDocumentFragmentDirections.toShowQR()
- findNavController().navigate(destination)
- }
-
- private fun openAddDocument() {
- val destination = SelectDocumentFragmentDirections.toAddSelfSigned()
- findNavController().navigate(destination)
- }
-
- private val permissionsLauncher =
- registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
- permissions.entries.forEach {
- log("permissionsLauncher ${it.key} = ${it.value}")
- if (!it.value) {
- Toast.makeText(
- activity,
- "The ${it.key} permission is required for BLE",
- Toast.LENGTH_LONG
- ).show()
- return@registerForActivityResult
- }
- }
- }
-}
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/blue_gradient.xml b/appholder/src/main/res/drawable/blue_gradient.xml
deleted file mode 100644
index 25fb5fc4c..000000000
--- a/appholder/src/main/res/drawable/blue_gradient.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/bottom_sheet_handle.xml b/appholder/src/main/res/drawable/bottom_sheet_handle.xml
deleted file mode 100644
index 3e1969557..000000000
--- a/appholder/src/main/res/drawable/bottom_sheet_handle.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/default_page_dot.xml b/appholder/src/main/res/drawable/default_page_dot.xml
deleted file mode 100644
index 03c1edb14..000000000
--- a/appholder/src/main/res/drawable/default_page_dot.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/driving_license_bg.png b/appholder/src/main/res/drawable/driving_license_bg.png
deleted file mode 100644
index 180488d12..000000000
Binary files a/appholder/src/main/res/drawable/driving_license_bg.png and /dev/null differ
diff --git a/appholder/src/main/res/drawable/gradient_red.xml b/appholder/src/main/res/drawable/gradient_red.xml
deleted file mode 100644
index 725371076..000000000
--- a/appholder/src/main/res/drawable/gradient_red.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/green_gradient.xml b/appholder/src/main/res/drawable/green_gradient.xml
deleted file mode 100644
index 44e42bf07..000000000
--- a/appholder/src/main/res/drawable/green_gradient.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/ic_add_document.xml b/appholder/src/main/res/drawable/ic_add_document.xml
deleted file mode 100644
index 7bf1f4a44..000000000
--- a/appholder/src/main/res/drawable/ic_add_document.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
diff --git a/appholder/src/main/res/drawable/ic_add_http.xml b/appholder/src/main/res/drawable/ic_add_http.xml
deleted file mode 100644
index 283b25e12..000000000
--- a/appholder/src/main/res/drawable/ic_add_http.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
diff --git a/appholder/src/main/res/drawable/ic_launcher_foreground.xml b/appholder/src/main/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index 04aff291b..000000000
--- a/appholder/src/main/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
diff --git a/appholder/src/main/res/drawable/ic_nfc.xml b/appholder/src/main/res/drawable/ic_nfc.xml
deleted file mode 100644
index 075742a4e..000000000
--- a/appholder/src/main/res/drawable/ic_nfc.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
diff --git a/appholder/src/main/res/drawable/ic_outline_info_24.xml b/appholder/src/main/res/drawable/ic_outline_info_24.xml
deleted file mode 100644
index 35f7f5f61..000000000
--- a/appholder/src/main/res/drawable/ic_outline_info_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/appholder/src/main/res/drawable/ic_present_document.xml b/appholder/src/main/res/drawable/ic_present_document.xml
deleted file mode 100644
index 91f8971aa..000000000
--- a/appholder/src/main/res/drawable/ic_present_document.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/appholder/src/main/res/drawable/ic_settings.xml b/appholder/src/main/res/drawable/ic_settings.xml
deleted file mode 100644
index 1134c3184..000000000
--- a/appholder/src/main/res/drawable/ic_settings.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
diff --git a/appholder/src/main/res/drawable/ic_wallet.xml b/appholder/src/main/res/drawable/ic_wallet.xml
deleted file mode 100644
index 94ca58094..000000000
--- a/appholder/src/main/res/drawable/ic_wallet.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
diff --git a/appholder/src/main/res/drawable/img_erika_portrait.jpg b/appholder/src/main/res/drawable/img_erika_portrait.jpg
deleted file mode 100644
index 31e356ddc..000000000
Binary files a/appholder/src/main/res/drawable/img_erika_portrait.jpg and /dev/null differ
diff --git a/appholder/src/main/res/drawable/img_erika_signature.jpg b/appholder/src/main/res/drawable/img_erika_signature.jpg
deleted file mode 100644
index 01fd5e8fb..000000000
Binary files a/appholder/src/main/res/drawable/img_erika_signature.jpg and /dev/null differ
diff --git a/appholder/src/main/res/drawable/pager_indicator_selector.xml b/appholder/src/main/res/drawable/pager_indicator_selector.xml
deleted file mode 100644
index f970bd233..000000000
--- a/appholder/src/main/res/drawable/pager_indicator_selector.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/selected_pager_dot.xml b/appholder/src/main/res/drawable/selected_pager_dot.xml
deleted file mode 100644
index d2408fc64..000000000
--- a/appholder/src/main/res/drawable/selected_pager_dot.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/drawable/yellow_gradient.xml b/appholder/src/main/res/drawable/yellow_gradient.xml
deleted file mode 100644
index 74d41f501..000000000
--- a/appholder/src/main/res/drawable/yellow_gradient.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout-land/fragment_share_document.xml b/appholder/src/main/res/layout-land/fragment_share_document.xml
deleted file mode 100644
index eb330fdb5..000000000
--- a/appholder/src/main/res/layout-land/fragment_share_document.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/activity_main.xml b/appholder/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 57136304a..000000000
--- a/appholder/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/fragment_reverse_engagement.xml b/appholder/src/main/res/layout/fragment_reverse_engagement.xml
deleted file mode 100644
index e37d685ad..000000000
--- a/appholder/src/main/res/layout/fragment_reverse_engagement.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/fragment_select_document.xml b/appholder/src/main/res/layout/fragment_select_document.xml
deleted file mode 100644
index 4f0d08f13..000000000
--- a/appholder/src/main/res/layout/fragment_select_document.xml
+++ /dev/null
@@ -1,140 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/fragment_self_signed_details.xml b/appholder/src/main/res/layout/fragment_self_signed_details.xml
deleted file mode 100644
index a9834ca2f..000000000
--- a/appholder/src/main/res/layout/fragment_self_signed_details.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/fragment_share_document.xml b/appholder/src/main/res/layout/fragment_share_document.xml
deleted file mode 100644
index 87818ba4f..000000000
--- a/appholder/src/main/res/layout/fragment_share_document.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/fragment_show_document_data.xml b/appholder/src/main/res/layout/fragment_show_document_data.xml
deleted file mode 100644
index cb566336a..000000000
--- a/appholder/src/main/res/layout/fragment_show_document_data.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/fragment_transfer_document.xml b/appholder/src/main/res/layout/fragment_transfer_document.xml
deleted file mode 100644
index 9f3e030ac..000000000
--- a/appholder/src/main/res/layout/fragment_transfer_document.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/layout/list_item_document.xml b/appholder/src/main/res/layout/list_item_document.xml
deleted file mode 100644
index a31e7e5d4..000000000
--- a/appholder/src/main/res/layout/list_item_document.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/appholder/src/main/res/menu/side_navigation_menu.xml b/appholder/src/main/res/menu/side_navigation_menu.xml
deleted file mode 100644
index 46cf5e96a..000000000
--- a/appholder/src/main/res/menu/side_navigation_menu.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/appholder/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 97c5f2f20..000000000
--- a/appholder/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/appholder/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 97c5f2f20..000000000
--- a/appholder/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/mipmap-hdpi/ic_launcher.png b/appholder/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 1b6fe4543..000000000
Binary files a/appholder/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-hdpi/ic_launcher_round.png b/appholder/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index f0269e1a7..000000000
Binary files a/appholder/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-mdpi/ic_launcher.png b/appholder/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 9831f8029..000000000
Binary files a/appholder/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-mdpi/ic_launcher_round.png b/appholder/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index c7a7f06c5..000000000
Binary files a/appholder/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-xhdpi/ic_launcher.png b/appholder/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 1ab6b22f4..000000000
Binary files a/appholder/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/appholder/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 3c513832d..000000000
Binary files a/appholder/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-xxhdpi/ic_launcher.png b/appholder/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 16acdf836..000000000
Binary files a/appholder/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/appholder/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index c6ce91651..000000000
Binary files a/appholder/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/appholder/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index e70005d04..000000000
Binary files a/appholder/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/appholder/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/appholder/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 608c430f2..000000000
Binary files a/appholder/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/appholder/src/main/res/navigation/navigation_graph.xml b/appholder/src/main/res/navigation/navigation_graph.xml
deleted file mode 100644
index 8fd22400f..000000000
--- a/appholder/src/main/res/navigation/navigation_graph.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/raw/bdr_iaca_cert.pem b/appholder/src/main/res/raw/bdr_iaca_cert.pem
deleted file mode 100644
index c07c54aea..000000000
--- a/appholder/src/main/res/raw/bdr_iaca_cert.pem
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICCDCCAa+gAwIBAgIUf9sUe7RX0O61QqCJqxbfwR5552QwCgYIKoZIzj0EAwIw
-PjELMAkGA1UEBhMCREUxLzAtBgNVBAMMJkJEUiBJQUNBIElTTy9JRUMgMTgwMTMt
-NSB2MSBURVNULU9OTFkgMB4XDTIyMDUxMDA2MTcxOFoXDTIzMDUxMDA2MTcxOFow
-PjELMAkGA1UEBhMCREUxLzAtBgNVBAMMJkJEUiBJQUNBIElTTy9JRUMgMTgwMTMt
-NSB2MSBURVNULU9OTFkgMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXdefvZac
-KisGZdi//9MLGOLRtU0vf0yynMQr290MwANgY+hrspesGUNTtwyhicUCzsxP3vUH
-VKAm1ch4gCgAHKOBijCBhzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQE
-AwIBBjAdBgNVHRIEFjAUgRJtZGwtZXhhbXBsZUBiZHIuZGUwIwYDVR0fBBwwGjAY
-oBagFIISbWRsLmV4YW1wbGUuYmRyLmRlMB0GA1UdDgQWBBTak6D3WHRyxIUfTlRL
-vMorzjC3VDAKBggqhkjOPQQDAgNHADBEAiB4xGrCaWnUQX6ocRk3WmgqDcpxYOlb
-bxUNTsHIfhC2ygIgTSjddwl5Ex4DkAWGSWWcmVkDQuv80Ab3qZRLsx4/qGs=
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/bdr_reader_ca_cert.pem b/appholder/src/main/res/raw/bdr_reader_ca_cert.pem
deleted file mode 100644
index 72c5f0d04..000000000
--- a/appholder/src/main/res/raw/bdr_reader_ca_cert.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICKDCCAc6gAwIBAgIEY2HtcTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJERTEv
-MC0GA1UEAwwmQkRSIElBQ0EgSVNPL0lFQyAxODAxMy01IHYxIFRFU1QtT05MWSAw
-HhcNMjIxMTAyMDQwOTIxWhcNMjMxMTAyMDQwOTIxWjA9MQswCQYDVQQGEwJERTEu
-MCwGA1UEAwwlQkRSIFJBIElTTy9JRUMgMTgwMTMtNSB2MS4xIFRFU1QtT05MWTBZ
-MBMGByqGSM49AgEGCCqGSM49AwEHA0IABFZGT29q3W06DSF5kjrNGXFpT6lIk1+k
-vZJMbrIAB49e2fa9XJ5eB3BEqhCfRXeg8mEGyNxdiQObf+hIKm11T4mjgbowgbcw
-FQYDVR0lAQH/BAswCQYHKIGMXQUBBjAOBgNVHQ8BAf8EBAMCB4AwIwYDVR0SBBww
-GoEYbWRscmVhZGVyLWV4YW1wbGVAYmRyLmRlMCkGA1UdHwQiMCAwHqAcoBqCGG1k
-bHJlYWRlci5leGFtcGxlLmJkci5kZTAfBgNVHSMEGDAWgBTak6D3WHRyxIUfTlRL
-vMorzjC3VDAdBgNVHQ4EFgQUM0vODYGZMQOCi574zwpssFpXzQUwCgYIKoZIzj0E
-AwIDSAAwRQIhAIaJ6jnbcmGT/O0RS+ry+YHJL7iVgTalHBu+K44myu5QAiA6mOzt
-N66gxv7xnpOt2axSg+C1TpldSORC4vlJNvZtoQ==
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/credenceid_mdl_reader_cert.pem b/appholder/src/main/res/raw/credenceid_mdl_reader_cert.pem
deleted file mode 100644
index dc3f5ffc5..000000000
--- a/appholder/src/main/res/raw/credenceid_mdl_reader_cert.pem
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIC9zCCAn2gAwIBAgIULZOxBG2xPmbtfz3GaTutkuein2AwCgYIKoZIzj0EAwMw
-gYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRAwDgYDVQQHDAdP
-YWtsYW5kMRMwEQYDVQQKDApDcmVkZW5jZUlEMSgwJgYDVQQLDB9DcmVkZW5jZUlE
-IFNlbGYgU2lnbmVkIENlcnQgbURMMRcwFQYDVQQDDA5jb20uY3JlZGVuY2VpZDAe
-Fw0yMjExMDcxMTIxMTZaFw0yMzExMDcxMTIxMTZaMIGMMQswCQYDVQQGEwJVUzET
-MBEGA1UECAwKQ2FsaWZvcm5pYTEQMA4GA1UEBwwHT2FrbGFuZDETMBEGA1UECgwK
-Q3JlZGVuY2VJRDEoMCYGA1UECwwfQ3JlZGVuY2VJRCBTZWxmIFNpZ25lZCBDZXJ0
-IG1ETDEXMBUGA1UEAwwOY29tLmNyZWRlbmNlaWQwdjAQBgcqhkjOPQIBBgUrgQQA
-IgNiAATK07rJgmtTEM1MPrnO/ydDDDOZ8QLqGDYi7RcFJrFdiNTWs+dggibM0oq+
-mdN6MPc90iTkX8/y8WqD/ygVO5qdJ9B8DBqmEE1kqNZ4JfRCS/u0jV7X0zv2Bvz4
-7U6P/dWjgZ0wgZowHwYDVR0jBBgwFoAUGJ3Hw2UxlVaTRzeNA7zB3YvIRgEwEgYD
-VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCB4AwFQYDVR0lAQH/BAswCQYH
-KIGMXQUBBjAdBgNVHQ4EFgQUGJ3Hw2UxlVaTRzeNA7zB3YvIRgEwHQYDVR0SBBYw
-FIISd3d3LmNyZWRlbmNlaWQuY29tMAoGCCqGSM49BAMDA2gAMGUCMQDXjvVkE81D
-c+DZugCTJ3Cnv90kZv5qwZ6gDgEi3p4OzJbImW6lBkYyrC0sJnMpaOoCMGCYc5ab
-E2RNKeXAKL2fgCOpzKHL7C2QlSG7pW+quLBASOwd8Y3KGxWKqJYhN4pswg==
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/fast_reader_auth_cer.pem b/appholder/src/main/res/raw/fast_reader_auth_cer.pem
deleted file mode 100644
index 083caf8e0..000000000
--- a/appholder/src/main/res/raw/fast_reader_auth_cer.pem
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBzTCCAXSgAwIBAgITOezQNTPogU2umR2wFeywdzWoVTAKBggqhkjOPQQDAjAo
-MSYwJAYDVQQDEx1GYXN0IE1vYmlsZSBDcmVkZW50aWFsIFJlYWRlcjAeFw0yMjEx
-MTQwNzAwMDBaFw0yNTExMTMwNzAwMDBaMCgxJjAkBgNVBAMTHUZhc3QgTW9iaWxl
-IENyZWRlbnRpYWwgUmVhZGVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8Zw6
-ZlgtLZgJshCdlgUKdhELYTcu0OEW7WjpjIepDYsfSZM7issSK7zzu5GE2Cf7Jerh
-Ywl7etazcpmhuGzlmqN9MHswDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTwhjP8
-1dFTOpN9JNk4S2HThUEQjzAkBgNVHRIEHTAbgRl0a29vbkBmYXN0ZW50ZXJwcmlz
-ZXMuY29tMBIGA1UdJQQLMAkGByiBjF0FAQYwEAYJKwYBBAGDxSEBBANYMkQwCgYI
-KoZIzj0EAwIDRwAwRAIgco7wbxPXaz/xZJNdHzdpYUQCn3sgJkiNsOowFLo9Q6sC
-IBDi7OICbo84omkEdMHrUBpopH0iFGlHsaZlotV5agBg
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/google_mdl_ds_cert.pem b/appholder/src/main/res/raw/google_mdl_ds_cert.pem
deleted file mode 100644
index cd98517c8..000000000
--- a/appholder/src/main/res/raw/google_mdl_ds_cert.pem
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICAzCCAYmgAwIBAgIQR29vZ2xlX1Rlc3RfRFNfMjAKBggqhkjOPQQDAzAsMQsw
-CQYDVQQGEwJVVDEdMBsGA1UEAwwUR29vZ2xlIFRFU1QgSUFDQSBtREwwHhcNMjIw
-OTA2MjIwMDAwWhcNMjMxMjA2MjMwMDAwWjAqMQswCQYDVQQGEwJVVDEbMBkGA1UE
-AwwSR29vZ2xlIFRFU1QgRFMgbURMMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
-zYHh/YYiYMr24+F6RbARWhKLIQu5eMpaHS8uZrdHqRCbMho36TsdfcD0s+uinhYW
-T7GiPukJmBhU1h1kI/0yLaOBjjCBizAfBgNVHSMEGDAWgBTJN+E9NRPRrHioja5K
-qdNPkTQVIDAdBgNVHQ4EFgQU/nFIc07m1DlcPpe3yKmdAp6+YCAwDgYDVR0PAQH/
-BAQDAgeAMCIGA1UdEgQbMBmGF2h0dHBzOi8vd3d3Lmdvb2dsZS5jb20vMBUGA1Ud
-JQEB/wQLMAkGByiBjF0FAQIwCgYIKoZIzj0EAwMDaAAwZQIxAPraxqF/I7g5kvhb
-QdgjkMMDWcUBJHySO9SX2ESvFX6GBlG++Iq1RDhAMERirxKAnAIwLQQ1grSeXp66
-rskhBgFWEDaeLVUtOlrtkawUDhzs5Pwc+RC1wrPUxsIyZNdNy63q
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2.pem b/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2.pem
deleted file mode 100644
index 338dcd812..000000000
--- a/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2.pem
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICIzCCAamgAwIBAgIQR29vZ2xlX1Rlc3RfRFNfMTAKBggqhkjOPQQDAzA8MQswCQYDVQQGEwJV
-UzEOMAwGA1UECBMFVVMtTUExHTAbBgNVBAMMFEdvb2dsZSBURVNUIElBQ0EgbURMMB4XDTIzMDcy
-NjAwMDAwMVoXDTI0MTAyNTAwMDAwMVowOjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVVTLU1BMRsw
-GQYDVQQDDBJHb29nbGUgVEVTVCBEUyBtREwwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQsgMEL
-9w9jvdzEHqINdqIuy6Kpf6iBG/GdVyQzsSwMHz+ZTAQ75+F90IOHKBusDDelKTYbPLNqD6w41BrA
-ZvkDo4GOMIGLMB8GA1UdIwQYMBaAFN7zq2033p46gW4QMtArSK81inGrMB0GA1UdDgQWBBQ5e+U8
-UN/w16LkqeRl6nN6ntiSyDAOBgNVHQ8BAf8EBAMCB4AwIgYDVR0SBBswGYYXaHR0cHM6Ly93d3cu
-Z29vZ2xlLmNvbS8wFQYDVR0lAQH/BAswCQYHKIGMXQUBAjAKBggqhkjOPQQDAwNoADBlAjEAolzb
-im8MI5BbnT9YKmCPDBb0NrXu41h9LKl3FnxvFqK53hD+Hd0W3gcuT4ZvBgWnAjBqSIlCH5uzdLi9
-4XWz1qsjIWupfmiVEJdK4PZZoQ6VhGm1vYDfwQWIgx2TGYGmOZg=
------END CERTIFICATE-----
\ No newline at end of file
diff --git a/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2_privkey.pem b/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2_privkey.pem
deleted file mode 100644
index 92908f3ba..000000000
--- a/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2_privkey.pem
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg7rg6vhE0yR+lRNlDU49LiQDib/Ca
-vWRZJFOtCKd5hhWgCgYIKoZIzj0DAQehRANCAAQsgMEL9w9jvdzEHqINdqIuy6Kpf6iBG/GdVyQz
-sSwMHz+ZTAQ75+F90IOHKBusDDelKTYbPLNqD6w41BrAZvkD
------END PRIVATE KEY-----
\ No newline at end of file
diff --git a/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2_pubkey.pem b/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2_pubkey.pem
deleted file mode 100644
index 86150199e..000000000
--- a/appholder/src/main/res/raw/google_mdl_ds_cert_iaca_2_pubkey.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELIDBC/cPY73cxB6iDXaiLsuiqX+ogRvxnVckM7Es
-DB8/mUwEO+fhfdCDhygbrAw3pSk2Gzyzag+sONQawGb5Aw==
------END PUBLIC KEY-----
\ No newline at end of file
diff --git a/appholder/src/main/res/raw/google_mdl_ds_privkey.pem b/appholder/src/main/res/raw/google_mdl_ds_privkey.pem
deleted file mode 100644
index 5e9a8912e..000000000
--- a/appholder/src/main/res/raw/google_mdl_ds_privkey.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PRIVATE KEY-----
-MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCALcWNTBfsacqBZPJ9Y
-M99Dm7XHo+CyMLlxAyS85qwWvg==
------END PRIVATE KEY-----
diff --git a/appholder/src/main/res/raw/google_mdl_ds_pubkey.pem b/appholder/src/main/res/raw/google_mdl_ds_pubkey.pem
deleted file mode 100644
index 20ed0b35b..000000000
--- a/appholder/src/main/res/raw/google_mdl_ds_pubkey.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzYHh/YYiYMr24+F6RbARWhKLIQu5
-eMpaHS8uZrdHqRCbMho36TsdfcD0s+uinhYWT7GiPukJmBhU1h1kI/0yLQ==
------END PUBLIC KEY-----
diff --git a/appholder/src/main/res/raw/google_mekb_ds_cert.pem b/appholder/src/main/res/raw/google_mekb_ds_cert.pem
deleted file mode 100644
index 2de0aaaf3..000000000
--- a/appholder/src/main/res/raw/google_mekb_ds_cert.pem
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICBzCCAY2gAwIBAgIQR29vZ2xlX1Rlc3RfRFNfMzAKBggqhkjOPQQDAzAtMQsw
-CQYDVQQGEwJVVDEeMBwGA1UEAwwVR29vZ2xlIFRFU1QgSUFDQSBtRUtCMB4XDTIy
-MDkwNjIyMDAwMFoXDTIzMTIwNjIzMDAwMFowKzELMAkGA1UEBhMCVVQxHDAaBgNV
-BAMME0dvb2dsZSBURVNUIERTIG1FS0IwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
-AARSRMmlDc61C4R1qU89zxx/dqLeb38byIuH4c7ij9o0CgV+VzS2CWOyXV7zhgsx
-A9ZdHpXzc01XYAreW7t9sN7do4GQMIGNMB8GA1UdIwQYMBaAFCllLpnnGJF8aeJb
-JJXv1NXrqAJpMB0GA1UdDgQWBBRv6jNdZwpk67kfEtIdu6tdwyjuETAOBgNVHQ8B
-Af8EBAMCB4AwIgYDVR0SBBswGYYXaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8wFwYD
-VR0lAQH/BA0wCwYJYIQQAYdyAgIBMAoGCCqGSM49BAMDA2gAMGUCMQDJmprOPdU6
-2If6qMwt7NGEyeB0jQHOdWCwE4lwYK2bIELHFbBmgXsWxczYswbPXkECMD6UjoeT
-Di7CSFcwuGfPjMcNb/VyiQpQIlUxV9CoFjb6KSKB2OErilL9cfhQTkbDxw==
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/google_mekb_ds_privkey.pem b/appholder/src/main/res/raw/google_mekb_ds_privkey.pem
deleted file mode 100644
index a8ac1e123..000000000
--- a/appholder/src/main/res/raw/google_mekb_ds_privkey.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PRIVATE KEY-----
-MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCC0JleDgCy13gIchZ3C
-rLMAQ3sUKROQXt4Uu/7JPACOnw==
------END PRIVATE KEY-----
diff --git a/appholder/src/main/res/raw/google_mekb_ds_pubkey.pem b/appholder/src/main/res/raw/google_mekb_ds_pubkey.pem
deleted file mode 100644
index 858a09861..000000000
--- a/appholder/src/main/res/raw/google_mekb_ds_pubkey.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUkTJpQ3OtQuEdalPPc8cf3ai3m9/
-G8iLh+HO4o/aNAoFflc0tgljsl1e84YLMQPWXR6V83NNV2AK3lu7fbDe3Q==
------END PUBLIC KEY-----
diff --git a/appholder/src/main/res/raw/google_micov_ds_cert.pem b/appholder/src/main/res/raw/google_micov_ds_cert.pem
deleted file mode 100644
index 1bf492270..000000000
--- a/appholder/src/main/res/raw/google_micov_ds_cert.pem
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICCjCCAZCgAwIBAgIQR29vZ2xlX1Rlc3RfRFNfNDAKBggqhkjOPQQDAzAuMQsw
-CQYDVQQGEwJVVDEfMB0GA1UEAwwWR29vZ2xlIFRFU1QgQ1NDQSBtaWNvdjAeFw0y
-MjA5MDYyMjAwMDBaFw0yMzEyMDYyMzAwMDBaMCwxCzAJBgNVBAYTAlVUMR0wGwYD
-VQQDDBRHb29nbGUgVEVTVCBEUyBtaWNvdjBZMBMGByqGSM49AgEGCCqGSM49AwEH
-A0IABPotOowsy1XgUGMogMCdyWNUdRDu3pIJWFBBWeBsHce12KfI5r4+BFfxztnH
-GzJgCuCXDr/rxqm9azIxPjvy6/SjgZEwgY4wHwYDVR0jBBgwFoAUVFBks9x7BEJv
-6IO9mnByzwkLYj0wHQYDVR0OBBYEFEa1625HGK1xbiT42NIJbbpQU4L2MA4GA1Ud
-DwEB/wQEAwIHgDAiBgNVHRIEGzAZhhdodHRwczovL3d3dy5nb29nbGUuY29tLzAY
-BgNVHSUBAf8EDjAMBgorBgEEAY43j2UBMAoGCCqGSM49BAMDA2gAMGUCMQDD7v0J
-e9FBv7RFGL069/jTRVQfLcF2G4GiwHmQISqIp4cmi1fGyTaG8+9j8SxQjRoCMA0/
-A72JuyobAnrxZUMSQMKD1PRYIrPLCP9cfwYdY7n3wlMzwS2AG1+3+pu44kG6zg==
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/google_micov_ds_privkey.pem b/appholder/src/main/res/raw/google_micov_ds_privkey.pem
deleted file mode 100644
index c84c7fa16..000000000
--- a/appholder/src/main/res/raw/google_micov_ds_privkey.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PRIVATE KEY-----
-MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAS6KP3aj1ZoyGTo+PR
-5PdrPe3aLnhhVcSMRXzCPTecoA==
------END PRIVATE KEY-----
diff --git a/appholder/src/main/res/raw/google_micov_ds_pubkey.pem b/appholder/src/main/res/raw/google_micov_ds_pubkey.pem
deleted file mode 100644
index f55088f7c..000000000
--- a/appholder/src/main/res/raw/google_micov_ds_pubkey.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+i06jCzLVeBQYyiAwJ3JY1R1EO7e
-kglYUEFZ4Gwdx7XYp8jmvj4EV/HO2ccbMmAK4JcOv+vGqb1rMjE+O/Lr9A==
------END PUBLIC KEY-----
diff --git a/appholder/src/main/res/raw/google_reader_ca.cer b/appholder/src/main/res/raw/google_reader_ca.cer
deleted file mode 100644
index 9d55e1cba..000000000
--- a/appholder/src/main/res/raw/google_reader_ca.cer
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICHzCCAaSgAwIBAgIQR29vZ2xlX1Rlc3RfUkNfMTAKBggqhkjOPQQDAzAxMQsw
-CQYDVQQGEwJVVDEiMCAGA1UEAwwZR29vZ2xlIFRFU1QgUmVhZGVyIENBIG1ETDAe
-Fw0yMTA5MDYyMjAwMDBaFw0zMDA5MDYyMjAwMDBaMDExCzAJBgNVBAYTAlVUMSIw
-IAYDVQQDDBlHb29nbGUgVEVTVCBSZWFkZXIgQ0EgbURMMHYwEAYHKoZIzj0CAQYF
-K4EEACIDYgAE0AdASfofu26APRV9qjazUaD7N514vTJ/QmDQpVRfnWb73fJPXA4T
-+2NORvY0qvmLb2fyUYiv0EFsD3WNuQBa6Y+gNLKKvfUVhTvWIpkkiGeg0GVY+BRW
-GrCjseb1bvfxo4GAMH4wHQYDVR0OBBYEFGAvSsPYAi+GMPeWEMZPsGRWD1Q2MA4G
-A1UdDwEB/wQEAwIBBjAiBgNVHRIEGzAZhhdodHRwczovL3d3dy5nb29nbGUuY29t
-LzASBgNVHRMBAf8ECDAGAQH/AgEAMBUGA1UdJQEB/wQLMAkGByiBjF0FAQYwCgYI
-KoZIzj0EAwMDaQAwZgIxAPlxZljnfOIPIdljiIWPMAsvHw9bpDBwRck+qOxrWRij
-eC1mXw4lPlYEwmGlH5TxFQIxAIg2FFpvGeJ1kb+oc2qo9bDpwIvAn/Kigh5vkYyh
-lyt8YcebLJgoGFeRsipFnWViCA==
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/hid_test_reader_ca_mdl_cert.pem b/appholder/src/main/res/raw/hid_test_reader_ca_mdl_cert.pem
deleted file mode 100644
index cfd76d37e..000000000
--- a/appholder/src/main/res/raw/hid_test_reader_ca_mdl_cert.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICZjCCAeygAwIBAgIJAMypC0xQrthEMAoGCCqGSM49BAMDMC4xCzAJBgNVBAYT
-AklOMR8wHQYDVQQDDBZISUQgVEVTVCBSZWFkZXIgQ0EgbURMMB4XDTIxMTExMTEy
-NDUyNloXDTMwMTExMTEyNDUyNlowLjELMAkGA1UEBhMCSU4xHzAdBgNVBAMMFkhJ
-RCBURVNUIFJlYWRlciBDQSBtREwwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQUUaWe
-B5ArngiVNh5Mp44Qi5lEH99biDl++uewMNmoNTwbfK2DTjMdEX78rCD/KVI4la+d
-E6baGjsRbnfV64Sv8q+yA2FOyjrY+D9NEW6cfvrcXpYDzyKz/1NJ/20Az1WjgdUw
-gdIwHQYDVR0OBBYEFLX08QsEBf0KIh/HFxr47wW1OkpYMBIGA1UdEwEB/wQIMAYB
-Af8CAQAwDgYDVR0PAQH/BAQDAgEGMBIGA1UdJQQLMAkGByiBjF0FAQcwJAYDVR0S
-BB0wG4YZaHR0cDovL3d3dy5oaWRnbG9iYWwuY29tLzBTBgNVHR8ETDBKMEigRqBE
-hkJodHRwczovL2Rldi1scy5nb2lkaHViLmhpZGNsb3VkLmNvbS9jaWQvaGlkdGVz
-dHJlYWRlcmNhbWRsLmNybC5wZW0wCgYIKoZIzj0EAwMDaAAwZQIxAPaBGLQjfDKf
-Zw+e28kCKHhQc2VMlph/4WVeREsDy497EytXN/3OI4cgio4e22trrgIwKuxx3AEd
-NEH7o6a1wUhuHA4KTwXz0L+kZZRoBLH8okxRjz60muortu27KknUR4/p
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/hidtestiacamdl_cert.pem b/appholder/src/main/res/raw/hidtestiacamdl_cert.pem
deleted file mode 100644
index 986f88fc3..000000000
--- a/appholder/src/main/res/raw/hidtestiacamdl_cert.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICVzCCAd6gAwIBAgIJAOfd4JeMLy8qMAoGCCqGSM49BAMCMCkxCzAJBgNVBAYT
-AklOMRowGAYDVQQDDBFISUQgVEVTVCBJQUNBIG1ETDAeFw0yMTEwMjkwODI5Mjda
-Fw0zMDEwMjkwODI5MjdaMCkxCzAJBgNVBAYTAklOMRowGAYDVQQDDBFISUQgVEVT
-VCBJQUNBIG1ETDB2MBAGByqGSM49AgEGBSuBBAAiA2IABE2zFKAiVvjT6BmNHVWa
-KgOVm4VF4oESD8CmndCRQAn8aMjlEmW2cFkEWShItLUKV71LfzAhL1i0+9TpbROw
-BVGY7EL4WpaQ7NYNyjrZlmhtSGLMdCI2paGdZ57rDcYj/6OB0TCBzjAdBgNVHQ4E
-FgQU1w1wYmEsUIcKgeAKjqUoXnelwFgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV
-HQ8BAf8EBAMCAQYwEgYDVR0lBAswCQYHKIGMXQUBBzAkBgNVHRIEHTAbhhlodHRw
-Oi8vd3d3LmhpZGdsb2JhbC5jb20vME8GA1UdHwRIMEYwRKBCoECGPmh0dHBzOi8v
-ZGV2LWxzLmdvaWRodWIuaGlkY2xvdWQuY29tL2NpZC9oaWR0ZXN0aWFjYW1kbC5j
-cmwucGVtMAoGCCqGSM49BAMCA2cAMGQCMGl+HJf227vqpM3Ev/0bHy8wXfgBBv+7
-Oo0lEhJ9ZbS01JlDv1GNmIyu1UanruHrIgIwIepzys1SetzuE4FJ4Vhwn6IoFKjb
-3jmUufkkZLZgASLB8R7zeiDPaOx+JvfCj8kY
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/iaca_zetes.der b/appholder/src/main/res/raw/iaca_zetes.der
deleted file mode 100644
index fd608cb5f..000000000
Binary files a/appholder/src/main/res/raw/iaca_zetes.der and /dev/null differ
diff --git a/appholder/src/main/res/raw/idemia_brisbane_interop_readerauthca.pem b/appholder/src/main/res/raw/idemia_brisbane_interop_readerauthca.pem
deleted file mode 100644
index 1dff19691..000000000
--- a/appholder/src/main/res/raw/idemia_brisbane_interop_readerauthca.pem
+++ /dev/null
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDZTCCAsagAwIBAgIEYUf/iDAKBggqhkjOPQQDAjB9MQswCQYDVQQGEwJVUzEL
-MAkGA1UECAwCTUExEjAQBgNVBAcMCUJpbGxlcmljYTEPMA0GA1UECgwGSURFTUlB
-MQ0wCwYDVQQLDARJU05BMS0wKwYDVQQDDCRJREVNSUEgUmVhZGVyIEF1dGhlbnRp
-Y2F0aW9uIFRlc3QgQ0EwHhcNMjEwOTIwMDMyNzA0WhcNMjYwOTIwMDMyNzA0WjB9
-MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTUExEjAQBgNVBAcMCUJpbGxlcmljYTEP
-MA0GA1UECgwGSURFTUlBMQ0wCwYDVQQLDARJU05BMS0wKwYDVQQDDCRJREVNSUEg
-UmVhZGVyIEF1dGhlbnRpY2F0aW9uIFRlc3QgQ0EwgZswEAYHKoZIzj0CAQYFK4EE
-ACMDgYYABAA3tAtmmTgwhDJHa32YNOs9p5iLpN6nfPesu243mRMyt192uxuyXi/7
-/p7FhvW0hw2sqNl1DOW5Qtm2Pi/Or4KXVwFnRWQyau93LRwec8CYEssDprAlm30N
-owbgoKJc8LHIxXpEaOXxw3yxCFPTKyuLsnlMjBQTvXL7dXPdYRflVpMAIaOB8DCB
-7TAPBgNVHRMBAf8EBTADAQH/MIGtBgNVHSMEgaUwgaKAFCJWAIzOUNYWl7JTdAR4
-7Z5Du6jXoYGDpIGAMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNQTESMBAGA1UE
-BwwJQmlsbGVyaWNhMQ8wDQYDVQQKDAZJREVNSUExDTALBgNVBAsMBElTTkExLjAs
-BgNVBAMMJUlERU1JQSBSZWFkZXIgQXV0aGVudGljYXRpb24gVGVzdCBDQSCCBGFH
-/4gwHQYDVR0OBBYEFCJWAIzOUNYWl7JTdAR47Z5Du6jXMAsGA1UdDwQEAwIBhjAK
-BggqhkjOPQQDAgOBjAAwgYgCQgCCNcaSoXGaFWOFX8lLj26JLerx7s031rYsxh1I
-Fau8fZyvbOO6Ox+9ODav4HfpF+l656fFuN0TA2SF5FQtqPX19QJCALcArIV0N0Q1
-887SPVYPWArqfoNNTV/1t3j9j9wQLZJUdKDc9yI9NjZeUMRDRhiot79EaBdEE2eU
-QBTUaHGoecYI
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/louisiana_department_of_motor_vehicles_cert.cer b/appholder/src/main/res/raw/louisiana_department_of_motor_vehicles_cert.cer
deleted file mode 100644
index 54616e10d..000000000
Binary files a/appholder/src/main/res/raw/louisiana_department_of_motor_vehicles_cert.cer and /dev/null differ
diff --git a/appholder/src/main/res/raw/nist_reader_ca_cer.pem b/appholder/src/main/res/raw/nist_reader_ca_cer.pem
deleted file mode 100644
index ddb3eb1ce..000000000
--- a/appholder/src/main/res/raw/nist_reader_ca_cer.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICDzCCAZWgAwIBAgIQTklTVF9UZXN0X1JEUkNfMTAKBggqhkjOPQQDAzArMQsw
-CQYDVQQGEwJVUzEcMBoGA1UEAwwTTklTVCBURVNUIFJlYWRlciBDQTAeFw0yMTA5
-MDYyMjAwMDBaFw0zMDA5MDYyMjAwMDBaMCsxCzAJBgNVBAYTAlVTMRwwGgYDVQQD
-DBNOSVNUIFRFU1QgUmVhZGVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEUl9f
-qD7ZHYLN1Q+0WKv7JzD2pypz6nucE4z6Oe+AP+zK5jbb8wfQhByNHC8YaQ5cTMm0
-dWh+2DAsTePbSmDMWvkVVveftnRG4jXW3AhibwKTTOSPksZR+YPRNXl4SPjbo34w
-fDAdBgNVHQ4EFgQUmQqvbk3DDplpnZrPkJWwg3LfYJswDgYDVR0PAQH/BAQDAgEG
-MCAGA1UdEgQZMBeGFWh0dHBzOi8vd3d3Lm5pc3QuZ292LzASBgNVHRMBAf8ECDAG
-AQH/AgEAMBUGA1UdJQEB/wQLMAkGByiBjF0FAQYwCgYIKoZIzj0EAwMDaAAwZQIw
-doPPRC46DhYnQX7KZNvbRYyW2Ra+9uYBRt0eSNuRfvU+xMqUuqE+NGsNyqlVp6os
-AjEA9np4Z76pMkGGc/4ko1ZuRbq5AzuHzK7BKtlAqleoefuqHcFqRQ6OuufYjIR5
-me8O
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/owf_identity_credential_reader_cert.pem b/appholder/src/main/res/raw/owf_identity_credential_reader_cert.pem
deleted file mode 100644
index b008b1da7..000000000
--- a/appholder/src/main/res/raw/owf_identity_credential_reader_cert.pem
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICCTCCAY+gAwIBAgIQMrJE21vV644Z1wKsQ0NmgDAKBggqhkjOPQQDAzA+MS8wLQYDVQQDDCZP
-V0YgSWRlbnRpdHkgQ3JlZGVudGlhbCBURVNUIFJlYWRlciBDQTELMAkGA1UEBhMCVVQwHhcNMjQw
-MjI4MTQxMjMzWhcNMjkwMjI4MTQxMjMzWjA+MS8wLQYDVQQDDCZPV0YgSWRlbnRpdHkgQ3JlZGVu
-dGlhbCBURVNUIFJlYWRlciBDQTELMAkGA1UEBhMCVVQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARP
-rmdJRF5/4GX+60fo9G5r10EevRrJ9TkGmW1Oyw7tK5FTtsUzG+DhlARSW1czAEVZOPvEOb4KwSH4
-Np4sgCtuZWrubLeXPwcxaF15DIv9F2mGqSB4vBIFIemMdXpl/HOjUjBQMB0GA1UdDgQWBBSh7AVU
-ngz0Xayp0/hfn2xxaSAIhzAfBgNVHSMEGDAWgBSh7AVUngz0Xayp0/hfn2xxaSAIhzAOBgNVHQ8B
-Af8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIxAPTyY0TzkxfnKdrcRttDBzMS8q/hF8l/odhk77lY
-tJPVgt+aOPumHHFiJvhqvKmQmAIwGKSkPXc6menkG2GpQRQQV7xBlALPO4zSJMoF90hEEK+SK+Ts
-u1Qtl0xtfSQC7wj0
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/reader_ca_nec_reader_ca_cer.pem b/appholder/src/main/res/raw/reader_ca_nec_reader_ca_cer.pem
deleted file mode 100644
index 87c2b3c68..000000000
--- a/appholder/src/main/res/raw/reader_ca_nec_reader_ca_cer.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICDTCCAZOgAwIBAgIJAPBfV+EzSTssMAoGCCqGSM49BAMDMC4xCzAJBgNVBAYT
-AkpQMR8wHQYDVQQDDBZORUMgVEVTVCBSZWFkZXIgQ0EgbURMMB4XDTIyMTAyNTA3
-MjAzMloXDTIzMTAyNjA3MjAzMlowLjELMAkGA1UEBhMCSlAxHzAdBgNVBAMMFk5F
-QyBURVNUIFJlYWRlciBDQSBtREwwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQxChP0
-Ksl/WNaIZ8Bu3NyzT5DQOPrJZ/FMD53IFip8ruga0Vw4fmsDB87hPCm73w7UWQpX
-Yy3O6F9BakiIb/CkJkG6vZyDAaxPFEGkzS734u45IQ5NJuXTzt7y+5lf0U+jfTB7
-MB0GA1UdDgQWBBR/WdP2I+IirqgRMLJWIz+mEIV13DAOBgNVHQ8BAf8EBAMCAQYw
-HwYDVR0SBBgwFoYUaHR0cHM6Ly9qcG4ubmVjLmNvbS8wEgYDVR0TAQH/BAgwBgEB
-/wIBADAVBgNVHSUBAf8ECzAJBgcogYxdBQEGMAoGCCqGSM49BAMDA2gAMGUCMQCg
-mJWgOHBTs+AUv5geHDyvmMLL+XaD/M7QqlEfKKuHNIT4nynCsYC4OVdqmUWhSswC
-MAObNwJU2+/iHJ5PkEuobYlR1/xmMVlVxhbY0MLRDBiRRM87w9mkXIy+6GzEb4PO
-aw==
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/samsung_iaca_test_cert.pem b/appholder/src/main/res/raw/samsung_iaca_test_cert.pem
deleted file mode 100755
index 041e6793f..000000000
--- a/appholder/src/main/res/raw/samsung_iaca_test_cert.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICJjCCAcygAwIBAgIJAPFiPwnFEodUMAoGCCqGSM49BAMCMFwxCzAJBgNVBAYT
-AktSMSYwJAYDVQQKDB1TYW1zdW5nIEVsZWN0cm9uaWNzIENvLiwgTHRkLjElMCMG
-A1UEAwwcSUFDQSByb290IGNlcnRpZmljYXRlIChURVNUKTAeFw0xOTA3MjkxMTQz
-NTZaFw0yOTA3MjkxMTQzNTZaMFwxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1TYW1z
-dW5nIEVsZWN0cm9uaWNzIENvLiwgTHRkLjElMCMGA1UEAwwcSUFDQSByb290IGNl
-cnRpZmljYXRlIChURVNUKTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIiJnufh
-kN2TH3VcWhH6qp9MpPTTvhh0wvgJe5OlIy3gkCnSHW+/AHF863jV6Ly4RYVc//XO
-EwBsA08X3aGhGECjdzB1MB0GA1UdDgQWBBSFGm9gprRVNGRyYId6NX82dsNWhjAP
-BgNVHRMECDAGAQH/AgEAMAsGA1UdDwQEAwIBBjA2BgNVHR8ELzAtMCugKaAnhiVo
-dHRwOi8vZXhhbXBsZS5jb20vaWFjYS50ZXN0LmNlcnQucGVtMAoGCCqGSM49BAMC
-A0gAMEUCIB7rDSR3f2WvwPfPoHdgwL7neidUfL8hIZbhqgwPaeldAiEA85eJPR5W
-cycCptDVrS56sMjsbZsTjgunJ0zjosuFsw8=
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/scytales_root_ca.pem b/appholder/src/main/res/raw/scytales_root_ca.pem
deleted file mode 100644
index 53894a147..000000000
--- a/appholder/src/main/res/raw/scytales_root_ca.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICDzCCAbWgAwIBAgIJfXsIRsWZBcBTMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT
-AlNFMREwDwYDVQQKEwhTY3l0YWxlczEZMBcGA1UEAxMQU2N5dGFsZXMgUm9vdCBD
-QTAeFw0yMjAxMDExMjAwMDBaFw0zMjAxMDExMjAwMDBaMDsxCzAJBgNVBAYTAlNF
-MREwDwYDVQQKEwhTY3l0YWxlczEZMBcGA1UEAxMQU2N5dGFsZXMgUm9vdCBDQTBZ
-MBMGByqGSM49AgEGCCqGSM49AwEHA0IABIzq9cNAf2RdNgiBUIs81s1OjR5BQfey
-2SqeUrilq8OF4+43EWE4xj7FpvME2fIU9xDj33qMDeDaEvXJXGr7iJ6jgaEwgZ4w
-EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUSm+0P25OvBhVycIPVZNZh3wV
-NTQwCwYDVR0PBAQDAgEGMCIGA1UdEgQbMBmGF2h0dHA6Ly93d3cuc2N5dGFsZXMu
-Y29tMDgGA1UdHwQxMC8wLaAroCmGJ2h0dHBzOi8vc3RhdGljLm1kb2MuaWQvY3Js
-L3NjeXRhbGVzLmNybDAKBggqhkjOPQQDAgNIADBFAiA34hF87sx96Z38W48kR88q
-gEoCIgOYjCLLP8iUuardIAIhAK0Z63sJ2cISZJsAf0fznPL83+/xMhoOtMmlmBr2
-BiWO
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/spruce_iaca_cert.pem b/appholder/src/main/res/raw/spruce_iaca_cert.pem
deleted file mode 100644
index 6eb779ab9..000000000
--- a/appholder/src/main/res/raw/spruce_iaca_cert.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICnzCCAkWgAwIBAgIJAIXkktSIMCEmMAoGCCqGSM49BAMCMHAxCzAJBgNVBAYT
-AlVTMRMwEQYDVQQIDApDQUxJRk9STklBMRYwFAYDVQQHDA1TQU4gRlJBTkNJU0NP
-MTQwMgYDVQQDDCtJQUNBIENhbGlmb3JuaWEgRE1WIElTTyBCcmlzYmFuZSBUZXN0
-IEV2ZW50MB4XDTIyMTExMDEwMTIxOVoXDTIzMTExMDEwMTIxOVowcDELMAkGA1UE
-BhMCVVMxEzARBgNVBAgMCkNBTElGT1JOSUExFjAUBgNVBAcMDVNBTiBGUkFOQ0lT
-Q08xNDAyBgNVBAMMK0lBQ0EgQ2FsaWZvcm5pYSBETVYgSVNPIEJyaXNiYW5lIFRl
-c3QgRXZlbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATvedQImO8bHtrJL0BP
-xAv/V5h2amvCmAwj3jLjNEf5AYmdD+JPVHxGHrNrKBflxrEwJ/SdC6BeaT+vILKc
-Ab0lo4HHMIHEMB0GA1UdDgQWBBSnbQRQfO/k20/BmM05BSbu6JGn5jASBgNVHRMB
-Af8ECDAGAQH/AgEAMDoGCWCGSAGG+EIBDQQtFitJQUNBIENhbGlmb3JuaWEgRE1W
-IElTTyBCcmlzYmFuZSBUZXN0IEV2ZW50MCcGA1UdHwQgMB4wHKAaoBiGFmh0dHBz
-Oi8vZG12LmNhLmdvdi9tZGwwCwYDVR0PBAQDAgEGMB0GA1UdEgQWMBSBEmV4YW1w
-bGVAZG12LmNhLmdvdjAKBggqhkjOPQQDAgNIADBFAiAEJVBYl5UJ3azvvYfU6UjT
-lljun2iL1A6r8eTvl/RSpgIhAKX4J8tMRYLQi9KbH9gHPnQmSRuu6onkiamqTIdm
-drEm
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/ul_cert_ca_01.crt b/appholder/src/main/res/raw/ul_cert_ca_01.crt
deleted file mode 100644
index 54ff4da7a..000000000
Binary files a/appholder/src/main/res/raw/ul_cert_ca_01.crt and /dev/null differ
diff --git a/appholder/src/main/res/raw/ul_cert_ca_01_cer.pem b/appholder/src/main/res/raw/ul_cert_ca_01_cer.pem
deleted file mode 100644
index 86889005d..000000000
--- a/appholder/src/main/res/raw/ul_cert_ca_01_cer.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICwTCCAiKgAwIBAgIJQAAAAAAAAAAAMAoGCCqGSM49BAMEMEAxCzAJBgNVBAYT
-AkdCMQswCQYDVQQKDAJVTDEkMCIGA1UEAwwbVGVzdCBSb290IENBIGNlcnQgMSBm
-b3IgbURMMB4XDTIxMDkyODEzNTUzM1oXDTM1MDYwNzEzNTUzM1owQDELMAkGA1UE
-BhMCR0IxCzAJBgNVBAoMAlVMMSQwIgYDVQQDDBtUZXN0IFJvb3QgQ0EgY2VydCAx
-IGZvciBtREwwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACnKlqfXxSIsy6V5POr
-+YuZbh6FVxcaIftXbzDLvS2VYne4+TuDhQKKWamicV+zF3wSHBBf22biNSjLyU9a
-r3UxlwFV1zMUBM7lb0TMfjqEfLs03XeGdEsAfTDjsv9US2z0FVCQJV2UnYVGm/ue
-7h3smA+VOQDmEhN6bQS8bM6fSYdqC6OBwTCBvjAdBgNVHQ4EFgQUYUwyQVy/fYl0
-XWI/7ockN1uE5okwDgYDVR0PAQH/BAQDAgEGMDIGA1UdEgQrMCmGJ2h0dHBzOi8v
-d3d3Lmlzby5vcmcvc3RhbmRhcmQvNzk4MDUuaHRtbDASBgNVHRMBAf8ECDAGAQH/
-AgEAMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9tZGwuc2VsZnRlc3RwbGF0Zm9y
-bS5jb20vY3JsL3VsX2NybF9jYV8wMS5jcmwwCgYIKoZIzj0EAwQDgYwAMIGIAkIA
-gzPtjHsLdAZhKA/Sv1Qsv4CaEaZbhOcmy+3q8f1eylPpYPtc38WLUHQlqEk+PpvO
-bX3piIlRXpjIEdbmWHZjFbYCQgG/amyDDYMhfCgU/uvpr10P2k94ZfGUZy7BVStd
-JxMkKN8GeB+o+D83+bVkcOY+Mpju/Gtnbk2Mss5AXhBuMGNG2g==
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/ul_cert_ca_02.crt b/appholder/src/main/res/raw/ul_cert_ca_02.crt
deleted file mode 100644
index 329beedae..000000000
Binary files a/appholder/src/main/res/raw/ul_cert_ca_02.crt and /dev/null differ
diff --git a/appholder/src/main/res/raw/ul_cert_ca_02_cer.pem b/appholder/src/main/res/raw/ul_cert_ca_02_cer.pem
deleted file mode 100644
index 2d3a32705..000000000
--- a/appholder/src/main/res/raw/ul_cert_ca_02_cer.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICvTCCAiKgAwIBAgIJUAAAAAAAAAAAMAoGCCqGSM49BAMEMEAxCzAJBgNVBAYT
-AkdCMQswCQYDVQQKDAJVTDEkMCIGA1UEAwwbVGVzdCBSb290IENBIGNlcnQgMiBm
-b3IgbURMMB4XDTIxMDkyODEzNTUzNFoXDTM1MDYwNzEzNTUzNFowQDELMAkGA1UE
-BhMCR0IxCzAJBgNVBAoMAlVMMSQwIgYDVQQDDBtUZXN0IFJvb3QgQ0EgY2VydCAy
-IGZvciBtREwwgZswFAYHKoZIzj0CAQYJKyQDAwIIAQENA4GCAAQkOebgkiDKYeff
-33xZYEvHa5YtNd2y14z/SurCOZpF+uo/H6QiU3c9qfRYz4lK6s3plWEZLXKLh68x
-JWXRW3MVme2NQSiAwcX8mGm4eGV3/Kor9azJWfpb7Dl5F04+vSgWQ7ffCbIW3fyj
-A2Qgv9xlbbeo95RxPRHkSUaSOVnXF6OBwTCBvjAdBgNVHQ4EFgQUmPTUAOqe18+I
-Dc903bkzUr9WxEQwDgYDVR0PAQH/BAQDAgEGMDIGA1UdEgQrMCmGJ2h0dHBzOi8v
-d3d3Lmlzby5vcmcvc3RhbmRhcmQvNzk4MDUuaHRtbDASBgNVHRMBAf8ECDAGAQH/
-AgEAMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9tZGwuc2VsZnRlc3RwbGF0Zm9y
-bS5jb20vY3JsL3VsX2NybF9jYV8wMi5jcmwwCgYIKoZIzj0EAwQDgYgAMIGEAkAO
-P61swvoNS/lsqFJjUp3ngyWon/zNvZfnOLOfZkTgBLGx2dnrJkLqx7NOwwWv2LQs
-F0/4KfeEPPeP6bWTLH1NAkBy87us/siHi86WHY4PsoZmRs7pp2n2DixZn0Eq1taM
-7f+9+70G+j7y6SD0knC1uuPDz1SGpF/4/vuaUMytgkIL
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/ul_cert_ca_03_cer.pem b/appholder/src/main/res/raw/ul_cert_ca_03_cer.pem
deleted file mode 100644
index 4dd3bd889..000000000
--- a/appholder/src/main/res/raw/ul_cert_ca_03_cer.pem
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIB1DCCAXqgAwIBAgIBATAKBggqhkjOPQQDBDBJMRMwEQYKCZImiZPyLGQBGRYD
-Y29tMQ8wDQYDVQQKDAZUaGFsZXMxDDAKBgNVBAsMA0RJUzETMBEGA1UEAwwKZUdv
-diBWQSBDQTAeFw0yMDEyMTQxNTE0NTVaFw0zMDEyMTIxNTE0NTVaMEkxEzARBgoJ
-kiaJk/IsZAEZFgNjb20xDzANBgNVBAoMBlRoYWxlczEMMAoGA1UECwwDRElTMRMw
-EQYDVQQDDAplR292IFZBIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4AT/
-npadGfsW7OiLCzak+Lh2HsIY08D0Q9BM32eOeumglq7MOAJgthN5G4FH7sk6zpya
-bYAyXZTNO6pp2KvPsqNTMFEwHQYDVR0OBBYEFL8YUVnZb3XAms827YWDclv6RLUB
-MB8GA1UdIwQYMBaAFL8YUVnZb3XAms827YWDclv6RLUBMA8GA1UdEwEB/wQFMAMB
-Af8wCgYIKoZIzj0EAwQDSAAwRQIgcliQ8qvTS5IVug0akgVwsRECBgcvRoq6jCXo
-FQtNCWcCIQDv5apMuWAn+wc5wt9hAfEkzJPDlXXHGIEh6vjIABAn5Q==
------END CERTIFICATE-----
\ No newline at end of file
diff --git a/appholder/src/main/res/raw/utms_reader_ca.der b/appholder/src/main/res/raw/utms_reader_ca.der
deleted file mode 100644
index 3883d7c24..000000000
Binary files a/appholder/src/main/res/raw/utms_reader_ca.der and /dev/null differ
diff --git a/appholder/src/main/res/raw/utms_reader_ca_cer.pem b/appholder/src/main/res/raw/utms_reader_ca_cer.pem
deleted file mode 100644
index 65090e0f6..000000000
--- a/appholder/src/main/res/raw/utms_reader_ca_cer.pem
+++ /dev/null
@@ -1,10 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBbzCCARWgAwIBAgIKcnWxZ7Gki2JgqjAKBggqhkjOPQQDAjAZMRcwFQYDVQQD
-DA5tRG9jIFJlYWRlciBDQTAeFw0yMTA5MjkwOTExNDFaFw0yNjA5MjgwOTExNDBa
-MBkxFzAVBgNVBAMMDm1Eb2MgUmVhZGVyIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0D
-AQcDQgAExRtc5nvXfdWqxAIy6xyldjgGMW1N/1hRf1pO0Voy9JI/+rPmHEGQFkwb
-yyU7LGFhUMAdyr/gebFgk6Y/NCXou6NFMEMwEgYDVR0TAQH/BAgwBgEB/wIBADAd
-BgNVHQ4EFgQU/Ar7NhMBf9y+SfiZ94NqeGYb2jkwDgYDVR0PAQH/BAQDAgEGMAoG
-CCqGSM49BAMCA0gAMEUCIQCq2jgHc401IB/GkU0tPvFtLhUvLN+C/HeD+3Z2JtGq
-yQIgRqulhUR/fV+lFDoN0m4Z4nJXLfFQAVwpaDbjBRSsB/0=
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/raw/zetes_reader_ca.der b/appholder/src/main/res/raw/zetes_reader_ca.der
deleted file mode 100644
index 3883d7c24..000000000
Binary files a/appholder/src/main/res/raw/zetes_reader_ca.der and /dev/null differ
diff --git a/appholder/src/main/res/raw/zetes_reader_ca_cer.pem b/appholder/src/main/res/raw/zetes_reader_ca_cer.pem
deleted file mode 100644
index 65090e0f6..000000000
--- a/appholder/src/main/res/raw/zetes_reader_ca_cer.pem
+++ /dev/null
@@ -1,10 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBbzCCARWgAwIBAgIKcnWxZ7Gki2JgqjAKBggqhkjOPQQDAjAZMRcwFQYDVQQD
-DA5tRG9jIFJlYWRlciBDQTAeFw0yMTA5MjkwOTExNDFaFw0yNjA5MjgwOTExNDBa
-MBkxFzAVBgNVBAMMDm1Eb2MgUmVhZGVyIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0D
-AQcDQgAExRtc5nvXfdWqxAIy6xyldjgGMW1N/1hRf1pO0Voy9JI/+rPmHEGQFkwb
-yyU7LGFhUMAdyr/gebFgk6Y/NCXou6NFMEMwEgYDVR0TAQH/BAgwBgEB/wIBADAd
-BgNVHQ4EFgQU/Ar7NhMBf9y+SfiZ94NqeGYb2jkwDgYDVR0PAQH/BAQDAgEGMAoG
-CCqGSM49BAMCA0gAMEUCIQCq2jgHc401IB/GkU0tPvFtLhUvLN+C/HeD+3Z2JtGq
-yQIgRqulhUR/fV+lFDoN0m4Z4nJXLfFQAVwpaDbjBRSsB/0=
------END CERTIFICATE-----
diff --git a/appholder/src/main/res/values-night/themes.xml b/appholder/src/main/res/values-night/themes.xml
deleted file mode 100644
index 7b1650ca3..000000000
--- a/appholder/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/values/arrays.xml b/appholder/src/main/res/values/arrays.xml
deleted file mode 100644
index d018104dd..000000000
--- a/appholder/src/main/res/values/arrays.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- - @string/document_type_mdl
- - @string/document_type_mvr
- - @string/document_type_micov
- - @string/document_type_eu_pid
-
-
- - Keystore-backed
- - Hardware-backed
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/values/colors.xml b/appholder/src/main/res/values/colors.xml
deleted file mode 100644
index f8c6127d3..000000000
--- a/appholder/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
-
\ No newline at end of file
diff --git a/appholder/src/main/res/values/dimen.xml b/appholder/src/main/res/values/dimen.xml
deleted file mode 100644
index c0edf4f2b..000000000
--- a/appholder/src/main/res/values/dimen.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 26dp
- 16dp
-
\ No newline at end of file
diff --git a/appholder/src/main/res/values/strings.xml b/appholder/src/main/res/values/strings.xml
deleted file mode 100644
index 257dff071..000000000
--- a/appholder/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,212 +0,0 @@
-
- Cancel
- Show QR Code
- Done
- Transfer Completed!
- List of Documents:
- Transferring
- Approve sending data to mDL verifier
- Approve
- @string/app_name
- mDL NFC Data Transfer
- mDL AID Group
- Add Document
- Add self-signed document
- Enter the server URL
- Enter the provisioning code
- http://192.168.1.169:18013/mdlServer
- 1001
- Next
- Retrieving data
- OK
- Authenticate to share data
- Anonymous verifier is requesting the following information
- Trusted verifier \"%1$s\" is requesting the following information
- Untrusted verifier \"%1$s\" is requesting the following information
- Use PIN
- User authentication has failed
- Biometric Authentication Error
- Fingerprint was removed from the device so the document cannot be shared anymore.
- Credential Shape
- Name
- DocType
- Check for Update
- Delete
- Date Provisioned
- Last Time Used
- MSO Usage Count
- Last Update Check
- Tap back button again to exit
- Refresh Auth Keys
- Last Refresh Auth Keys
- Present Documents
-
-
- Family Name
- Given Names
- Date of birth
- Date of issue
- Date of expiry
- Issuing country
- Issuing authority
- Licence number
- Portrait of mDL holder
- Categories of vehicles
- UN distinguishing sign
- Administrative number
- Sex
- Height (cm)
- Weight (kg)
- Eye colour
- Hair colour
- Place of birth
- Permanent place of residence
- Portrait image timestamp
- How old are you (in years)?
- In what year were you born?
- Age over 18?
- Age over 21?
- Issuing jurisdiction
- Nationality
- Resident city
- Resident state
- Resident postal code
- Resident country
- Family name in national characters
- Given name in national characters
- Signature / usual mark
- AAMVA Sex
- EDL Credential
- DHS Compliance
-
-
- Vehicle Registration Information
- Vehicle Registration Holder Information
- Basic Vehicle Information
- Vehicle Identification Number
- Connection to mdoc verifier is open
- Connection to mdoc verifier is closed
- %d request(s) served
-
-
-
- Family Name
- Given Name
- Date of Birth
- Sex
- First vaccination against RA01
- Second vaccination against RA01
- ID with pasport number
- ID with driver’s license number
- Indication of vaccination against Yellow Fever
- Indication of vaccination against COVID-19
- Indication of test event for COVID-19
- Safe entry indication
- Facial Image
- Family Name Initial
- Given Name Initial
- Birth Year
- Birth Month
- Birth Day
-
- Settings
- Close Connection
- Document type
- Document name
- Storage implementation
- Secure Area
- Use user authentication
- Number of Authentication Keys
- Max use per Authentication Key
- Issuer
- User Auth
- Hardware Backed
- Show Data Elements
- Send Data
- Present with\nReverse Engagement
- Scan QR code from mdoc reader application.\n\nNOTE: This is not supported for ISO 18013-7, only 23220-4
-
-
- Mdoc Holder
- Add Document
- Add Self Signed Document
- Reverse Engagement
- Settings
- NFC tap with mdoc reader
- No Documents
- Start by adding new one
- Card art
-
-
- Unique Identifier
- Family Name at Birth
- Family Name at Birth in national characters
- First Name at Birth
- First Name at Birth in national characters
- Gender
- PID portrait
- Fingerprint
- Age over 13?
- Age over 16?
- Age over 60?
- Age over 65?
- Age pver 68?
-
- mDL
- mVR
- micov
- euPid
-
- Green
- Yellow
- Blue
- Red
-
- Android Keystore
- Software Keystore
- Secure Area Not Implemented
- Timeout seconds
- Passphrase (optional)
-
- Require User Authentication
- No User Authentication
- Allow LSKF unlocking
- Allow biometric unlocking
- Use StrongBox
- mdoc authentication
- Authentication Key Curve
-
- Counter
- Valid from
- Valid until
- Issuer data
- %1$d bytes
- Usage count
- Key Purposes
- EcCurve
- Delete Confirmation
- Are you sure you want to delete the document?
- Document Deleted!
-
- Passphrase Required
- Credential Passphrase
- Incorrect Passphrase. Try again!
- A passphrase is required for unlocking the key.
- mdoc ECDSA authentication
- mdoc MAC authentication
- P-256
- P-384
- P-521
- BrainPoolP256R1
- BrainPoolP320R1
- BrainPoolP384R1
- BrainPoolP512R1
- Ed25519
- Ed448
- X25519
- X448
- Validity time in days
- Minimum validity in days
- Domain
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/values/styles.xml b/appholder/src/main/res/values/styles.xml
deleted file mode 100644
index dd7b6b644..000000000
--- a/appholder/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/values/themes.xml b/appholder/src/main/res/values/themes.xml
deleted file mode 100644
index c9e0c4cf3..000000000
--- a/appholder/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/xml/file_paths.xml b/appholder/src/main/res/xml/file_paths.xml
deleted file mode 100644
index ed349d1fa..000000000
--- a/appholder/src/main/res/xml/file_paths.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/xml/network_security_config.xml b/appholder/src/main/res/xml/network_security_config.xml
deleted file mode 100644
index dca93c079..000000000
--- a/appholder/src/main/res/xml/network_security_config.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/xml/nfc_data_transfer_apdu_service.xml b/appholder/src/main/res/xml/nfc_data_transfer_apdu_service.xml
deleted file mode 100644
index c1f521354..000000000
--- a/appholder/src/main/res/xml/nfc_data_transfer_apdu_service.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/main/res/xml/nfc_engagement_apdu_service.xml b/appholder/src/main/res/xml/nfc_engagement_apdu_service.xml
deleted file mode 100644
index 49b50ca1b..000000000
--- a/appholder/src/main/res/xml/nfc_engagement_apdu_service.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/appholder/src/purse/res/values/ic_launcher_background.xml b/appholder/src/purse/res/values/ic_launcher_background.xml
deleted file mode 100644
index 5dc7c0f5b..000000000
--- a/appholder/src/purse/res/values/ic_launcher_background.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- #FF9800
-
\ No newline at end of file
diff --git a/appholder/src/purse/res/values/strings.xml b/appholder/src/purse/res/values/strings.xml
deleted file mode 100644
index 80b2b93d2..000000000
--- a/appholder/src/purse/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- IC Purse
-
\ No newline at end of file
diff --git a/appholder/src/test/java/com/android/identity/wallet/ExampleUnitTest.kt b/appholder/src/test/java/com/android/identity/wallet/ExampleUnitTest.kt
deleted file mode 100644
index 74b597d8b..000000000
--- a/appholder/src/test/java/com/android/identity/wallet/ExampleUnitTest.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.android.identity.wallet
-
-import kotlin.test.Test
-import kotlin.test.assertEquals
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/test/java/com/android/identity/wallet/authconfirmation/PassphraseValidationTest.kt b/appholder/src/test/java/com/android/identity/wallet/authconfirmation/PassphraseValidationTest.kt
deleted file mode 100644
index 749de6d9f..000000000
--- a/appholder/src/test/java/com/android/identity/wallet/authconfirmation/PassphraseValidationTest.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.identity.wallet.authconfirmation
-
-import kotlin.test.Test
-import kotlin.test.assertEquals
-
-class PassphraseValidationTest {
-
- @Test
- fun defaultValue() {
- val viewModel = PassphrasePromptViewModel()
-
- assertEquals(viewModel.authorizationState.value, PassphraseAuthResult.Idle)
- }
-
- @Test
- fun authorizationSuccess() {
- val passphrase = ":irrelevant:"
- val viewModel = PassphrasePromptViewModel()
-
- viewModel.authorize(userPassphrase = passphrase)
-
- assertEquals(viewModel.authorizationState.value, PassphraseAuthResult.Success(passphrase))
- }
-
- @Test
- fun resetting() {
- val viewModel = PassphrasePromptViewModel().apply {
- val passphrase = ":irrelevant:"
- authorize(passphrase)
- }
-
- viewModel.reset()
-
- assertEquals(viewModel.authorizationState.value, PassphraseAuthResult.Idle)
- }
-}
\ No newline at end of file
diff --git a/appholder/src/wallet/res/values/ic_launcher_background.xml b/appholder/src/wallet/res/values/ic_launcher_background.xml
deleted file mode 100644
index 930f6a4d9..000000000
--- a/appholder/src/wallet/res/values/ic_launcher_background.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- #4285F4
-
\ No newline at end of file
diff --git a/appholder/src/wallet/res/values/strings.xml b/appholder/src/wallet/res/values/strings.xml
deleted file mode 100644
index 9db99f25a..000000000
--- a/appholder/src/wallet/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- IC Wallet
-
\ No newline at end of file