Skip to content

Commit eceac9d

Browse files
committed
Work around broken VICALs and RICALs.
For VICALs and RICALs according to ISO 18013-5 the SKI (Subject Key Identifier) is present in both `CertificateInfo` / `RICALCertificateInfo` CBOR and the X.509 certificate itself. Our parsers use the CBOR value which is less than ideal for VICALs and RICALs that encode this field incorrectly. Instead, use the value from the certificate and log a warning if the two SKIs differ. Fixes #1806. Test: Manually tested and unit tests. Signed-off-by: David Zeuthen <zeuthen@google.com>
1 parent 74361ec commit eceac9d

2 files changed

Lines changed: 22 additions & 3 deletions

File tree

multipaz/src/commonMain/kotlin/org/multipaz/mdoc/rical/SignedRical.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.multipaz.crypto.SignatureVerificationException
2020
import org.multipaz.crypto.X509Cert
2121
import org.multipaz.crypto.X509CertChain
2222
import org.multipaz.util.Logger
23+
import org.multipaz.util.toHex
2324

2425
data class SignedRical(
2526
val rical: Rical,
@@ -167,12 +168,20 @@ data class SignedRical(
167168

168169
val certificateInfos = mutableListOf<RicalCertificateInfo>()
169170
(ricalMap["certificateInfos"] as CborArray).items.forEachIndexed { certInfoIndex, certInfo ->
170-
val ski = ByteString(certInfo["ski"].asBstr)
171171
// Be lenient about missing isTrustAnchor for now
172172
val isTrustAnchor = certInfo.getOrNull("isTrustAnchor")?.asBoolean ?: true.also {
173173
Logger.w(TAG, "isTrustAnchor not present in RICAL entry $certInfoIndex")
174174
}
175175
val certBytes = certInfo["certificate"].asBstr
176+
val certificate = X509Cert(ByteString(certBytes))
177+
val ski = certificate.subjectKeyIdentifier?.let { ByteString(it) }
178+
?: throw IllegalArgumentException("No SKI in certificate")
179+
val skiInCertInfo = ByteString(certInfo["ski"].asBstr)
180+
if (ski != skiInCertInfo) {
181+
Logger.w(TAG, "For certificate with subject ${certificate.subject.name} the SKI in "
182+
+ "RICALCertificateInfo (${skiInCertInfo.toHex()}) differs from SKI in X.509 certificate " +
183+
"(${ski.toHex()})")
184+
}
176185
val extensionsInCertInfo = certInfo.getOrNull("extensions")?.let {
177186
it.asMap.entries.associate { (extName, extValue) -> Pair(extName.asTstr, extValue) }
178187
} ?: emptyMap()
@@ -194,7 +203,7 @@ data class SignedRical(
194203
}
195204
require(serialNumberTaggedItem.tagNumber == Tagged.UNSIGNED_BIGNUM)
196205
certificateInfos.add(RicalCertificateInfo(
197-
certificate = X509Cert(ByteString(certBytes)),
206+
certificate = certificate,
198207
serialNumber = ByteString(serialNumberTaggedItem.taggedItem.asBstr),
199208
isTrustAnchor = isTrustAnchor,
200209
ski = ski,

multipaz/src/commonMain/kotlin/org/multipaz/mdoc/vical/SignedVical.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import org.multipaz.crypto.SignatureVerificationException
1919
import org.multipaz.crypto.AsymmetricKey
2020
import org.multipaz.crypto.X509Cert
2121
import org.multipaz.crypto.X509CertChain
22+
import org.multipaz.util.Logger
23+
import org.multipaz.util.toHex
2224
import kotlin.collections.component1
2325
import kotlin.collections.component2
2426

@@ -153,8 +155,16 @@ data class SignedVical(
153155
val certificateInfos = mutableListOf<VicalCertificateInfo>()
154156

155157
for (certInfo in (vicalMap["certificateInfos"] as CborArray).items) {
156-
val ski = ByteString(certInfo["ski"].asBstr)
157158
val certBytes = certInfo["certificate"].asBstr
159+
val certificate = X509Cert(ByteString(certBytes))
160+
val ski = certificate.subjectKeyIdentifier?.let { ByteString(it) }
161+
?: throw IllegalArgumentException("No SKI in certificate")
162+
val skiInCertInfo = ByteString(certInfo["ski"].asBstr)
163+
if (ski != skiInCertInfo) {
164+
Logger.w(TAG, "For certificate with subject ${certificate.subject.name} the SKI in "
165+
+ "CertificateInfo (${skiInCertInfo.toHex()}) differs from SKI in X.509 certificate " +
166+
"(${ski.toHex()})")
167+
}
158168
val docTypes = (certInfo["docType"] as CborArray).items.map { it.asTstr }
159169
val certProfiles = certInfo.getOrNull("certificateProfile")?.let {
160170
(it as CborArray).items.map { it.asTstr }

0 commit comments

Comments
 (0)