diff --git a/app/build.gradle b/app/build.gradle index d4203333..7619615e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -83,7 +83,7 @@ dependencies { implementation 'androidx.camera:camera-view:1.4.2' implementation 'com.github.markusfisch:ScalingImageView:1.4.3' implementation 'com.github.markusfisch:zxing-cpp:v3.0.2.3' - implementation 'de.tsenger:vdstools:0.14.0' + implementation 'de.tsenger:vdstools:0.16.0-SNAPSHOT' implementation 'org.bouncycastle:bcpkix-jdk15to18:1.83' // Testing diff --git a/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdbVerifier.kt b/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdbVerifier.kt index df4c28a8..d1a255bf 100644 --- a/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdbVerifier.kt +++ b/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdbVerifier.kt @@ -17,10 +17,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider import java.security.Provider import java.security.Security import java.security.cert.X509Certificate -import java.text.SimpleDateFormat import java.util.Date -import java.util.Locale -import java.util.TimeZone object IdbVerifier { private const val MASTER_LIST_ASSET = "DE_ML_2026-01-08-12-20-54.ml" @@ -44,32 +41,25 @@ object IdbVerifier { context.getString(R.string.idb_verification_unsigned) ) ) - val headerCertificateReference = seal.payLoad.idbHeader.certificateReference - val embeddedSignerCertificate = seal.payLoad.idbSignerCertificate - ?.certBytes - ?.let { - parseCertificate(it) - } - if (seal.payLoad.idbSignerCertificate != null && embeddedSignerCertificate == null) { - return listOf( - SealField( - R.string.idb_verification, - context.getString( - R.string.idb_verification_invalid_signer_certificate - ) - ) - ) - } - val signerCertificate = embeddedSignerCertificate - ?: context.findCertificateByReference(headerCertificateReference) + val headerCertificateReference = seal.signerCertReference + ?.hexToByteArrayOrNull() + val signerCertificate = context.findCertificateByReference( + headerCertificateReference + ) val curveName = signerCertificate?.let { curveNameFrom(it) } - val signatureResult = if (signerCertificate == null || curveName.isNullOrBlank()) { + val signedBytes = seal.signedBytes + val signatureResult = if ( + signerCertificate == null || + curveName.isNullOrBlank() || + signedBytes == null + ) { Verifier.Result.VerifyError } else { Verifier( - seal, + signedBytes, + signatureInfo.plainSignatureBytes, signerCertificate.publicKey.encoded, curveName ).verify() @@ -113,9 +103,7 @@ object IdbVerifier { ), SealField( R.string.vds_signing_date, - parseSigningDate( - signatureInfo.signingDate.toString() - ).toString() + signatureInfo.signingDate.toString() ), SealField( R.string.idb_signature_verification, @@ -139,12 +127,14 @@ object IdbVerifier { return fields } - private fun parseSigningDate(value: String): Date? { + private fun String.hexToByteArrayOrNull(): ByteArray? { + if (length % 2 != 0) { + return null + } return runCatching { - SimpleDateFormat("yyyy-MM-dd", Locale.US).apply { - isLenient = false - timeZone = TimeZone.getTimeZone("UTC") - }.parse(value) + ByteArray(length / 2) { index -> + substring(index * 2, index * 2 + 2).toInt(16).toByte() + } }.getOrNull() } @@ -172,14 +162,6 @@ object IdbVerifier { } } - private fun parseCertificate(certificateBytes: ByteArray): X509Certificate? { - return runCatching { - JcaX509CertificateConverter() - .setProvider(provider) - .getCertificate(X509CertificateHolder(certificateBytes)) - }.getOrNull() - } - private fun Context.findCertificateByReference( certificateReference: ByteArray? ): X509Certificate? { diff --git a/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdlParser.kt b/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdlParser.kt index 3e6ae4f8..e824bc44 100644 --- a/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdlParser.kt +++ b/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/IdlParser.kt @@ -109,8 +109,9 @@ object IdlParser { // Detect this by checking that the marker matches the key prefix // (jurisdiction fields always share their 2-char subfile prefix). val seg = if (skipSubtype && p - start >= 4 && - data[start] == data[start + 2] && - data[start + 1] == data[start + 3]) { + data[start] == data[start + 2] && + data[start + 1] == data[start + 3] + ) { start + 2 } else { start diff --git a/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/SealParser.kt b/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/SealParser.kt index 1d602c9f..4bbb4e6b 100644 --- a/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/SealParser.kt +++ b/app/src/main/kotlin/de/markusfisch/android/binaryeye/content/SealParser.kt @@ -2,7 +2,7 @@ package de.markusfisch.android.binaryeye.content import android.content.Context import de.markusfisch.android.binaryeye.R -import de.tsenger.vdstools.DataEncoder +import de.tsenger.vdstools.asn1.DerTlv import de.tsenger.vdstools.generic.Message import de.tsenger.vdstools.generic.MessageCoding import de.tsenger.vdstools.generic.MessageValue @@ -10,6 +10,7 @@ import de.tsenger.vdstools.generic.Seal import de.tsenger.vdstools.idb.IdbSeal import org.json.JSONArray import org.json.JSONObject +import de.tsenger.vdstools.generic.SealParser as VdsSealParser data class SealField(val name: Any, val value: String) private data class IdbChildMessageDefinition( @@ -27,7 +28,7 @@ object SealParser { bytes: ByteArray ): List? = try { context.buildFields( - Seal.fromString(bytes.toString(Charsets.ISO_8859_1)) + VdsSealParser().parse(bytes.toString(Charsets.ISO_8859_1)) ) } catch (_: Exception) { null @@ -66,7 +67,7 @@ object SealParser { private fun Message.childMessages(): List { val childDefinitionsByTag = idbChildMessagesByName[name] ?: return emptyList() return runCatching { - DataEncoder.parseDerTLvs(value.rawBytes).mapNotNull { derTlv -> + DerTlv.parseAll(value.rawBytes).mapNotNull { derTlv -> val childTag = derTlv.tag.toInt() and 0xff val childDefinition = childDefinitionsByTag[childTag] ?: return@mapNotNull null diff --git a/settings.gradle b/settings.gradle index 6487d7d0..dd8faf56 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,10 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven { + name = 'Central Portal Snapshots' + url = uri('https://central.sonatype.com/repository/maven-snapshots/') + } maven { url 'https://jitpack.io' } } }