vuer_emrtd
Orientation
vuer_emrtdis a standalone Kotlin/JVM command-line application that validates electronic Machine Readable Travel Documents (eMRTD) — i.e. NFC-read e-passports and Hungarian eID cards. It takes a JSON file of raw data-group bytes, performs ICAO Doc 9303 Passive Authentication (certificate chain + signature + hash checks) plus optional revocation checking, and writes a JSON result. It is invoked as a child process via thebin/vuer_emrtdshell script, not as a long-running service. See architecture-overview.
Repo path: /Users/levander/coding/facekom-v2-clones/vuer_emrtd · base branch master · HEAD 07da762 (“upgraded bouncy for CVE (#45)”, 2026-04-29). App version (from config.properties): 1.3.4.
No tags in this clone
git for-each-ref refs/tagsreturns nothing in this source-only clone, even thoughbin/tag-latest.shand.travis.ymlreference alatesttag and version tags. Treat tag/release history as unverified here.
1. Purpose & role in FaceKom
- What it does (verified from
EmrtdCommandLineExecutor.kt:24-34description block andREADME.md:88-102): “Validates electronic machine readable travel documents (eMRTD).” Reads a raw input JSON describing the chip’s EF files, validates the document against a folder of CSCA certificates, and emits a processed JSON result containing extracted person/document details, biometric images, and a verification verdict. - Role: It is the document-authentication engine. The README frames it as a CLI tool with
-c/-i/-oflags. It is not a web server, has no RabbitMQ/HTTP/DB code, and exposes no network listener — it is meant to be shelled out to by another FaceKom component that supplies the input JSON and consumes the output JSON. (Verified by absence: no server/queue/DB libraries inbuild.gradle.kts:47-93;App.ktis a one-shotmainthat callsexitProcess.) - Domain scope (verified
PassportNFC.kt:160-192,IdCardChecker.kt): handles ICAO data groups DG1, DG2, DG3, DG5, DG7, DG11, DG12, DG13 (Hungarian eID mother’s-name variant), DG14, DG15, plus EF.COM and EF.SOD. Hungarian-specific DG13 parsing is a first-party addition (HungarianEszemelyiDG13File.kt). - Cert provenance (
README.md:3): CSCA certs come from the Hungarian government registryhttps://www.nyilvantarto.hu/hu/csca_tanusitvany_listak_hu.
jMRTD fork-by-vendoring
First-party Kotlin code lives under two package roots:
vuer_emrtd.*(the app) andorg.jmrtd.*/org.jmrtd.cert.*(local re-implementations/overrides of select jMRTD classes that ship alongside theorg.jmrtd:jmrtd:0.8.0dependency). Theseorg.jmrtdfiles are committed source in this repo, not the library jar — see §6.
2. Tech stack & runtime
| Aspect | Value | Source |
|---|---|---|
| Language | Kotlin (JVM), org.jetbrains.kotlin.jvm 1.8.20 | build.gradle.kts:7,49 |
| Build tool | Gradle (Kotlin DSL), wrapper 8.10.2 | build.gradle.kts, gradle/wrapper/gradle-wrapper.properties |
| JVM target | Java 11 (jvmToolchain languageVersion 11; README §1.2.4 “require JAVA 11 runtime”) | build.gradle.kts:21-25, README.md:53-56 |
| Packaging | Gradle application plugin → distZip named vuer_emrtd.zip; distTar disabled | build.gradle.kts:11,108-119 |
| Main class | vuer_emrtd.AppKt | build.gradle.kts:109 |
| CLI framework | PicoCLI 4.7.6 (+ codegen annotation processor) | build.gradle.kts:84-86 |
| Logging | SLF4J + Logback classic 1.5.32 (console appender, root level info) | build.gradle.kts:82, logback.xml |
| JSON | Gson 2.10.1 (app I/O) + Jackson core/databind 2.21.1, annotations 2.21 (explicitly pinned; overrides the vulnerable transitive copy excluded from jnbis at :60-62) | build.gradle.kts:69-72 |
| eMRTD lib | org.jmrtd:jmrtd:0.8.0 | build.gradle.kts:52 |
| Crypto | BouncyCastle bcpkix-jdk18on:1.84 | build.gradle.kts:66 |
| Revocation (CRL/OCSP) | EU DSS suite 6.0 (dss-tsl-validation, dss-crl-parser-x509crl, dss-service, dss-alert, dss-utils-apache-commons) | build.gradle.kts:39,75-79 |
| Image decoding | imageio-openjpeg:0.6.6 (JPEG2000) + jnbis:2.1.2 (WSQ fingerprint) | build.gradle.kts:55,58-63 |
| Tests | JUnit 5 (Jupiter) 5.11.4, kotlin("test"), WireMock standalone 3.9.1, Guava 33.3.1-jre | build.gradle.kts:89-92 |
ktlint is effectively disabled
The ktlint plugin is applied but its filter excludes
**/*.*(all files):build.gradle.kts:27-32— “disable lint - if a large refactor is needed for something else, code style can be introduced after.” So lint does not run despite the plugin.
checktask is disabled;dependencyCheckgates on CVSS ≥ 5.0
tasks.named("check") { enabled = false }(build.gradle.kts:15-19) — the comment defers to GitHub Dependabot/dependency-review. OWASPdependencyCheck(build.gradle.kts:121-130) fails the build on CVSS > 5.0, scansruntimeClasspath, caches NVD data at$HOME/nvd, and contains a hard-coded NVD API key (build.gradle.kts:127).
3. Build & run
This is a Gradle project — there is no package.json, no pom.xml, no Dockerfile. There are no Gradle build-script tasks/scripts in a package.json sense; the relevant build entrypoints and customizations are:
- Main class / entrypoint:
vuer_emrtd.AppKt(build.gradle.kts:109). SourceApp.kt:fun main(args: Array<String>) { val exitCode = CommandLine(EmrtdCommandLineExecutor()).execute(*args) exitProcess(exitCode) } - Version source: project version is read from
src/main/resources/config.propertiesat configure time (build.gradle.kts:101-106), not declared in the build script. README §“Important for versioning” (README.md:17-18) stresses bumpingconfig.propertieson release. Same file is read at runtime byVersionProvider.ktfor--version. - Build command (from CI
.travis.yml:23-24):./gradlew build -x check --info(skips the disabledcheck). - SBOM:
./gradlew cyclonedxBomproduces a CycloneDX BOM overruntimeClasspathonly (build.gradle.kts:95-98); used by the vuln-scan workflow. - Run command (README
README.md:96-102): use the generated launcher scriptbin/vuer_emrtd(produced by theapplicationplugin inside the dist zip — not committed in repo; onlybin/tag-latest.shis committed, see §5).bin/vuer_emrtd -v→ print versionbin/vuer_emrtd -c certs_folder -i passport_input.json -o output_result.json -r
CLI options (verified EmrtdCommandLineExecutor.kt:40-62)
| Flag | Long | Required | Default | Meaning |
|---|---|---|---|---|
-i | --input | yes | — | Raw input JSON path (deserialized to RawEMRTDData) |
-c | --certs | yes | — | Directory of CSCA certificate files to load as trust anchors |
-o | --output | yes | — | Output JSON file path (must be a file, not dir; deleted+recreated if exists) |
-r | --revocation | no | false | Enable CRL/OCSP revocation checking of DS cert + intermediaries |
-ct | --connection-timeout | no | 60 (s) | Revocation connection timeout (should be < response timeout) |
-rt | --response-timeout | no | 60 (s) | Revocation response timeout |
-h | --help | no | — | Usage help |
-v | --version | no | — | Version (via VersionProvider) |
Exit codes
call()returns0on success,1on any caughtThrowable(logged “Exception during application run”):EmrtdCommandLineExecutor.kt:64-79.
4. Top-level structure
| Path | What it is | Verified |
|---|---|---|
src/main/kotlin/vuer_emrtd/ | App core: CLI executor, cert-store loader, entrypoint | listed + read |
src/main/kotlin/vuer_emrtd/models/ | Plain data/DTO classes (input RawEMRTDData, output Passport, etc.) | read |
src/main/kotlin/vuer_emrtd/nfc/ | eMRTD parsing + Passive Auth engine (PassportNFC, IdCardChecker, image/LDS utils) | read |
src/main/kotlin/vuer_emrtd/nfc/revocation/ | DSS-based CRL/OCSP revocation checking + timeout-aware data loaders | read |
src/main/kotlin/vuer_emrtd/exceptions/ | Two custom exceptions (CertificateRevokedException, RevocationTechnicalException) | listed |
src/main/kotlin/vuer_emrtd/utils/ | Gson Base64 adapter, PicoCLI version provider | read |
src/main/kotlin/org/jmrtd/ | Local jMRTD overrides + Hungarian DG13 file + status models (ExecutionStatus, MRTDTrustStore, providers) | read |
src/main/kotlin/org/jmrtd/cert/ | jMRTD CertStore SPI + PKD/keystore CertStore parameters | listed |
src/main/resources/ | config.properties (version), logback.xml | read |
src/test/kotlin/... | JUnit 5 tests for IdCardChecker, revocation checkers, version provider | read |
src/test/resources/emrtd/{input,output}/ | Golden input/output JSON pairs for the data-driven IdCardChecker test | listed |
src/test/resources/revocation/ | PEM/CRT/DER fixtures (CAs, CRLs, OCSP responses) for revocation tests | listed |
certs/ | 12 committed Hungarian CSCA certs (.cer/.crt/.der) | listed |
certs/link/ | 12 CSCA link certificates (cross-signed bridges between CSCA generations) | listed |
bin/ | Only tag-latest.sh (release tagging helper) | listed + read |
docs/ | ICAO Doc 9303-10 PDF (real PDF, 2 MB) + Idomsoft_0711.PNG (DG13 format diagram) | file-verified binaries, not LFS |
.github/ | Dependabot config, 2 scheduled workflows + their Python scripts | read |
gradle/, gradlew, gradlew.bat | Gradle wrapper 8.10.2 | verified |
.travis.yml | Travis CI: build + GitHub Releases deploy on tags | read |
40 Kotlin files under src/main/kotlin (verified by find ... | wc -l).
No Git-LFS pointers
git lfs ls-filesreturns empty andfile docs/9303_p10_cons_en.pdfreports a realPDF document, version 1.6. No model-weight/binary LFS blobs exist in this repo — the only binaries are the ICAO PDF, the PNG diagram, and the DER/cert fixtures (all small, real files).
5. Helper scripts under bin/
Only one first-party script exists in bin/ (the runnable vuer_emrtd launcher is generated by Gradle and not committed).
bin/tag-latest.sh (verified line-by-line)
Release tagging helper. Usage: ./bin/tag-latest <tag> [--force].
- Aborts unless current branch is
master(bin/tag-latest.sh:13-16). - Requires a
<tag>arg (:21-24). - If
masteralready contains<tag>and--forcenot given → abort; with--force→ delete the tag locally and onorigin(:26-36). - Deletes the
latesttag locally and onorigin, then re-createslateston HEAD, also creates<tag>, andgit push --tags(:40-44).
This script pushes to
originand deletes remote tags
tag-latest.shperformsgit push --delete origin ...andgit push --tags. It is a release-automation script, not safe to run casually. (Read-only doc — not executed.)
CI Python scripts under
.github/scripts/(notbin/)
.github/scripts/download-and-compare-csca.py— scrapes the Hungarian registry page, compares remote CSCA filenames againstcerts/contents, and Slack-notifies (SLACK_WEBHOOK_URL) about missing new certs; references ticket FKITDEV-2888 for making releases. Runs weekly (screen-for-csca.yml, Mondays 12:00 UTC)..github/scripts/vulnerability-scan.py— runsgrype sbom:build/reports/bom.json, filters High/Critical, Slack-notifies. Runs weekly (vulnerability-scan.yml, Mondays 10:00 UTC, after./gradlew cyclonedxBom). Requires Python depsrequests,beautifulsoup4(former) and the grype binary (latter).
6. Key modules / services / entities
Entry & orchestration (vuer_emrtd package)
App.kt—main(); hands argv to PicoCLIEmrtdCommandLineExecutor, exits with its code.EmrtdCommandLineExecutor.kt— PicoCLI@Command(name = "vuer_emrtd").call()pipeline (:64-79):loadCertStore()→ read input file → prepare output file → Gson-parseRawEMRTDData→ buildPassportNfcInput→IdCardChecker.check(...)→ Gson-serializePassport→ write output. Gson is configured with aByteArrayToBase64TypeAdapter,excludeFieldsWithModifiers(STATIC),serializeNulls(),setPrettyPrinting()(:163-170).MRTDCertStore.kt— wraps a jMRTDMRTDTrustStore.load(dir)recursively reads every file under the certs dir (sorted), parses each as an X.509 cert via BouncyCastle, wraps asTrustAnchor, and registers them as both a CollectionCertStoreand CSCA anchors (:22-58). Adds the BC provider ininit. Throws if the dir is missing/empty/not-a-dir.
eMRTD parsing & Passive Authentication (vuer_emrtd.nfc)
IdCardChecker.kt— object façade.check(PassportNfcInput): Passport(:14-144): constructsPassportNFC, callsverifySecurity(), then maps jMRTD parsed data groups into the outputPassport:- DG1 →
PersonDetails(DOB, expiry, doc code/number, names, nationality, gender, issuing state, optional data). - DG2/DG5/DG7/DG3 → face / portrait / signature / fingerprints images (each wrapped in try/catch that silently ignores failures).
- DG11 →
AdditionalPersonDetails; DG12 →AdditionalDocumentDetails. - DG13 (Hungarian eID) → sets
personDetails.hungarianEszemelyiMotherName. - Always sets
executionStatus = ExecutionStatus("SUCCESS", null)at the end.
- DG1 →
PassportNFC.kt— the core verification engine (~825 lines). Constructed fromPassportNfcInput; parses EF.SOD, EF.COM, DG1, DG2, DG7, DG11, DG12, DG14, DG15 (and DG13 only when DG1 says issuing stateHUNand document codeI) viaLDSFileWrapper.verifySecurity()(:200-225) runs, in order:verifyCS()— builds the cert chain from the DS cert to a CSCA trust anchor using PKIX (delegates toPassportNfcUtils.getCertificateChain), checks SOD-vs-DS issuer/serial consistency, sets theCSverdict + chain +PassportChainInfo. Usesdg12.dateOfIssue(parsedBASIC_ISO_DATE, end-of-day 23:59) as the PKIX validity date when present (:491-497).verifyDS()— verifies EF.SOD signature against the embedded DS certificate (checkDocSignature, with RSA-PSS salt brute-forcingfindSaltRSAPSSover 0..512, and special handling forSSAwithRSA/PSS/RSAalgorithms; SHA-256 MGF1).verifyHT()— recomputes each data-group hash and compares to the stored hashes in EF.SOD (verifyHash). Skips DG3/DG4 gracefully when EAC failed/absent.verifyPACE()— sets the SAC/PACE feature status from EF.CardAccess (alwaysNOT_PRESENThere sincecardAccessFileis never populated from the JSON input).verifyAA()— Active Authentication, but guarded byservice != nullwhich is always null in this offline batch mode, so AA never actually runs.
PassportNfcUtils.kt— image extraction (retrieveFaceImage/Portrait/Signature/FingerPrint) and the PKIX chain buildergetCertificateChain(...)(:189-289): usesCertPathBuilder.getInstance("PKIX","BC"), adds the DS cert + all CSCA-store certs as intermediaries, and attaches a revocation checker —DssCertRevocationCheckerwhen-ris on, elseEmptyRevocationChecker. SetsbuildParams.isRevocationEnabled = checkRevocationandbuildParams.date. OnRevocationTechnicalException(timeout) it marksexecutionStatus = FAILUREwith sub-indication. ReturnsCertificateChainResult(chain, executionStatus).LDSFileWrapper.kt— maps aRawEMRTDDatafield / EF FID to the right jMRTD*Fileparser (getLDSFile) and to raw bytes (getBinary). DG13 →HungarianEszemelyiDG13File. DG8/9/10/16 throw “not yet supported”.ImageUtil.kt— decodes WSQ fingerprint images viaJnbisand everything else viaImageIO, always producing a PNG alongside the original bytes (PassportImage).
Revocation (vuer_emrtd.nfc.revocation)
DssCertRevocationChecker.kt— aPKIXRevocationCheckerbacked by EU DSS. Behavior (class doc:26-32): if no revocation data present, cert passes; if CRL/OCSP present and the DS cert or any intermediary is revoked, throwsCertPathBuilderException. ConfiguresCommonCertificateVerifierwith trusted/adjunct sources,ExceptionOnStatusAlerton revoked/expired,LogOnStatusAlerton missing data, online CRL+OCSP+AIA sources (all using the timeout-aware data loader), and a custom “CRL-first” loading-strategy factory. Maps DSS indications/sub-indications to invalid viaisInvalid/isFailedIndication/isRevokedSubIndication.EmptyRevocationChecker.kt— no-opPKIXRevocationCheckerused when-ris off.RevocationDataLoaderProvider.kt— builds aCustomDataLoaderwith socket/connection/response timeouts derived from the CLI seconds (socket = response − connection, or default 60 s).CustomDataLoader.kt— extends DSSCommonsDataLoader; wrapsSocketTimeoutException/ConnectTimeoutExceptionintoRevocationTechnicalExceptionso timeouts are distinguishable downstream.CustomCrlFirstRevocationDataLoadingStrategy(.Factory).kt,CustomDataLoader.kt— strategy that prefers CRL over OCSP (files listed; factory wired inDssCertRevocationChecker.kt:75).
Models (vuer_emrtd.models)
RawEMRTDData.kt— input contract: nullableByteArrayfieldssodFile, comFile, dg1File, dg2File, dg3File, dg5File, dg7File, dg11File, dg12File, dg13File, dg14File, dg15File. (Input JSON keys are these names; bytes are Base64 via the Gson adapter.)Passport.kt— output contract:face, portrait, signature, fingerprints, personDetails, additionalPersonDetails, additionalDocumentDetails, featureStatus, verificationStatus, executionStatus.PassportNfcInput.kt— bundles trust store + raw data +checkRevocation+ the two timeouts (the object destructured inPassportNFC’s constructor).PassportImage.kt—{ mimeType, original: ByteArray, png: ByteArray? }.PassportChainInfo.kt—{ docIssuer, sodIssuer, docSigningCertificate (string), sodSerialNumber }.CertificateChainResult.kt—{ certificates, executionStatus }.- Also:
PersonDetails,AdditionalPersonDetails,AdditionalDocumentDetails,BacInfo(listed; not all read line-by-line — see gaps).
Local jMRTD overrides (org.jmrtd, org.jmrtd.cert)
HungarianEszemelyiDG13File.kt— first-party DG13 parser for the Hungarian eID; reads a TLV tag list and a singleMOTHERS_NAME_TAG(0x5F5B) UTF-8 field →optionalData. EF.DG13 tag fromDataGroupsuperclass.ExecutionStatus.kt—data class ExecutionStatus(status, subIndication)— the technical-execution status surfaced in output (e.g. revocation timeout →FAILURE).MRTDTrustStore.kt— vendored jMRTD trust-store (CSCA anchors/stores + CVCA key stores; LDAP/PKD/keystore/file loaders). Used via the app’sMRTDCertStorewhich only exercises the collection/anchor path.Constants.kt—sha256="SHA-256",withRSA="withRSA/PSS", image-decode error strings, cert-store SPI class name.JMRTDSecurityProvider.kt,FeatureStatus.kt,VerificationStatus.kt,MRTDTrustStore.ktandorg/jmrtd/cert/*(KeyStoreCertStoreSpi,KeyStoreCertStoreParameters,PKDCertStoreParameters,PKDMasterListCertStoreParameters) — vendored jMRTD internals (listed;VerificationStatus/FeatureStatusused heavily byPassportNFCfor verdicts — see gaps).
Utils
ByteArrayToBase64TypeAdapter.kt— Gson (de)serializer mappingByteArray↔ Base64 string. This is why everyByteArrayin input/output JSON is Base64.VersionProvider.kt— PicoCLIIVersionProviderreadingconfig.propertiesfrom the classpath →"vuer_emrtd v<version>".
7. Configuration
src/main/resources/config.properties— single keyversion=1.3.4. Drives both the build’sproject.version(build.gradle.kts:101-106) and the runtime--versionoutput (VersionProvider.kt). Must be bumped on release (README.md:17-18).src/main/resources/logback.xml— console appender, pattern%d [%thread] %-5level %logger %msg%n, root levelinfo.gradle.properties—org.gradle.jvmargs=-Xmx4g.- No
getconfig/AJV/JSON-schema config (this is a JVM CLI, not a Node service). Runtime “config” is entirely CLI flags (§3) plus the certs directory contents. - Hard-coded secrets in build: NVD API key in
build.gradle.kts:127; Travisdeploy.token.secureencrypted blob in.travis.yml. Slack webhook is injected viaSLACK_WEBHOOK_URLenv in the GitHub workflows.
Hard-coded NVD API key
build.gradle.kts:127commits a real-looking NVD API key. Worth rotating / moving to a secret.
8. Tests
- Framework: JUnit 5 (Jupiter);
tasks.test { useJUnitPlatform() }(build.gradle.kts:132-134). Alsokotlin("test"), JUnit Jupiter params, Guava (map-diffing), and WireMock standalone for mocking CRL/OCSP HTTP endpoints. - Location:
src/test/kotlin/vuer_emrtd/...:nfc/IdCardCheckerTest.kt— data-driven (@TestFactory): for every file insrc/test/resources/emrtd/input, runsIdCardChecker.checkwith and without revocation, and deep-diffs the resulting JSON against the matching.../output/<name>.jsongolden file (GuavaMaps.difference), excluding the cert chain / DS cert from comparison.@Isolated. Loads certs from./certs.nfc/DssCertRevocationCheckerTest.kt,nfc/EmptyRevocationCheckerTest.kt— revocation behavior (use thesrc/test/resources/revocation/*PEM/CRT/DER fixtures + WireMock).nfc/revocation/RevocationDataLoaderProviderTest.kt— timeout math.utils/VersionProviderTest.kt— version string.
- How to run:
./gradlew test(standard). CI runs the full./gradlew build -x check --infowhich includes tests. - Test fixtures: golden input/output JSON pairs under
src/test/resources/emrtd/{input,output}/(1.json,sample_passport.json,dg13sample.json,dg13missing.json,dg13error.json,error1.json,sample3.json,ve-error1.json,ve-error2.json, plusmanual/test-input.json); revocation fixtures (CAs, CRLs, OCSP responses) undersrc/test/resources/revocation/.
Tests depend on a working
./certsdirectory and may hit the network
IdCardCheckerTestruns each case with revocation enabled as well; with online CRL/OCSP/AIA sources, those paths can attempt outbound HTTP unless the fixtures’ certs carry no revocation URLs.MRTDCertStore.load(File("./certs"))requires the committed certs to be present and parseable.
9. Dependencies on other FaceKom services
None at runtime
Verified by reading all of
vuer_emrtd/**andbuild.gradle.kts: no RabbitMQ, no HTTP server/client to FaceKom endpoints, no DB, no Redis. The app’s only external network activity is outbound CRL/OCSP/AIA HTTP fetches to the URLs embedded in the document’s certificates, and only when-r/--revocationis set (DssCertRevocationChecker.kt,RevocationDataLoaderProvider.kt). Integration with the rest of FaceKom is out-of-process: a caller writes the input JSON (RawEMRTDDatashape), invokes the CLI, and reads the output JSON (Passportshape). Trust anchors are supplied as a local certs directory (-c).
Operational coupling (not a code dependency):
- Hungarian CSCA registry
nyilvantarto.hu— source of truth forcerts/(README +download-and-compare-csca.py). - Slack — the two scheduled GitHub workflows post to
SLACK_WEBHOOK_URL. - YouTrack — cert-refresh process tracked at
FKITDEV-2888(referenced indownload-and-compare-csca.py:65).
10. Verified gotchas
AA and PACE never actually execute in this offline mode
verifyAA()is gated onservice != null(PassportNFC.kt:220-222) andservice(aPassportService) is never set from JSON input → AA is effectively skipped.cardAccessFileis never populated, soverifyPACE()always yieldsNOT_PRESENTSAC. Only Passive Authentication (CS + DS + HT) is real here.
DG13 only parsed for Hungarian
I-type documents
PassportNFC.kt:179-188parses DG13 asHungarianEszemelyiDG13Fileonly when DG1’s issuing state isHUNand document code isI; otherwise DG13 is ignored. A decode failure setsexecutionStatus = FAILURE“failed to decode dg13 file” but does not abort.
Many DG-extraction failures are silently swallowed
In
IdCardChecker.kt, image extraction for DG2/DG3/DG5/DG7 and DG12 front/rear images are wrapped incatch { /* Don't do anything */ }— a missing/corrupt biometric simply yields a null field, not an error.
RSA-PSS salt length is brute-forced
findSaltRSAPSS(PassportNFC.kt:771-793) tries salt lengths 0..512 until a signature verifies, returning 0 if none match. Functional but a notable performance/robustness quirk.
SecureRandom.getInstance("NativePRNGNonBlocking")
PassportNFC.kt:149hard-codes theNativePRNGNonBlockingalgorithm — this is Unix/Linux/macOS-specific and would fail on platforms without that provider (consistent with the Linux-only Travis/CI posture). Only used for the AA challenge, which doesn’t run here anyway.
Output file is deleted then recreated
createOutputFile()deletes any existing-otarget before writing and refuses a directory path (EmrtdCommandLineExecutor.kt:119-141). Callers must not point-oat something they want preserved.
Chain "depth" heuristic for the CS verdict
verifyCS()treats a chain of depth ≤ 1 as FAILED (PassportNFC.kt:521-534): a valid DS cert + valid intermediaries but an invalid/absent trust anchor yields depth 1 → invalid. The trust anchor’s presence in the built path is what proves trust.
Unverified / gaps
- Tags / release history: no git tags in this clone (
for-each-refempty);latest/version-tag behavior intag-latest.shand.travis.ymlis not verifiable here. - Files listed but not read line-by-line (mapped structurally, key behavior inferred from usage in code I did read):
org/jmrtd/JMRTDSecurityProvider.kt,org/jmrtd/FeatureStatus.kt,org/jmrtd/VerificationStatus.kt, all oforg/jmrtd/cert/*(KeyStoreCertStoreSpi,KeyStoreCertStoreParameters,PKDCertStoreParameters,PKDMasterListCertStoreParameters); modelsPersonDetails.kt,AdditionalPersonDetails.kt,AdditionalDocumentDetails.kt,BacInfo.kt;nfc/EACCredentials.kt;nfc/revocation/CustomCrlFirstRevocationDataLoadingStrategy.kt+...Factory.kt; exceptionsCertificateRevokedException.kt,RevocationTechnicalException.kt(the latter’s role is confirmed via its usages). - Tests read: only
IdCardCheckerTest.ktline-by-line;DssCertRevocationCheckerTest.kt,EmptyRevocationCheckerTest.kt,RevocationDataLoaderProviderTest.kt,VersionProviderTest.ktwere not opened (existence + framework verified from the tree and build config). - Binary/cert contents:
docs/9303_p10_cons_en.pdf,docs/Idomsoft_0711.PNG, and allcerts/**andsrc/test/resources/**.der/.crt/.cer/.pemfiles were not decoded — only their existence/type recorded (per binary-safety rule). - Input/output JSON field exhaustiveness:
RawEMRTDData/Passportfield lists are from the Kotlin classes; the exact nested shape offeatureStatus/verificationStatusin output JSON comes from the (unread) jMRTDFeatureStatus/VerificationStatusclasses.
Sources
Files I actually read in /Users/levander/coding/facekom-v2-clones/vuer_emrtd:
README.md,build.gradle.kts,settings.gradle.kts,gradle.properties,.travis.yml,.gitattributes,.gitignore,.editorconfig,gradle/wrapper/gradle-wrapper.propertiesbin/tag-latest.sh.github/dependabot.yml,.github/workflows/screen-for-csca.yml,.github/workflows/vulnerability-scan.yml,.github/scripts/download-and-compare-csca.py,.github/scripts/vulnerability-scan.pysrc/main/resources/config.properties,src/main/resources/logback.xmlsrc/main/kotlin/vuer_emrtd/App.kt,EmrtdCommandLineExecutor.kt,MRTDCertStore.ktsrc/main/kotlin/vuer_emrtd/nfc/IdCardChecker.kt,PassportNFC.kt,PassportNfcUtils.kt,LDSFileWrapper.kt,ImageUtil.kt,EmptyRevocationChecker.ktsrc/main/kotlin/vuer_emrtd/nfc/revocation/DssCertRevocationChecker.kt,RevocationDataLoaderProvider.kt,CustomDataLoader.ktsrc/main/kotlin/vuer_emrtd/models/RawEMRTDData.kt,Passport.kt,PassportNfcInput.kt,PassportImage.kt,PassportChainInfo.kt,CertificateChainResult.ktsrc/main/kotlin/vuer_emrtd/utils/VersionProvider.kt,ByteArrayToBase64TypeAdapter.ktsrc/main/kotlin/org/jmrtd/HungarianEszemelyiDG13File.kt,ExecutionStatus.kt,Constants.kt,MRTDTrustStore.ktsrc/test/kotlin/vuer_emrtd/nfc/IdCardCheckerTest.kt- Directory listings:
src/tree,bin/,certs/,certs/link/,docs/,.github/,src/test/resources/(+file/git lfs ls-fileschecks on binaries)