vuer_emrtd

Orientation

vuer_emrtd is 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 the bin/vuer_emrtd shell 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/tags returns nothing in this source-only clone, even though bin/tag-latest.sh and .travis.yml reference a latest tag and version tags. Treat tag/release history as unverified here.


1. Purpose & role in FaceKom

  • What it does (verified from EmrtdCommandLineExecutor.kt:24-34 description block and README.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/-o flags. 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 in build.gradle.kts:47-93; App.kt is a one-shot main that calls exitProcess.)
  • 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 registry https://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) and org.jmrtd.* / org.jmrtd.cert.* (local re-implementations/overrides of select jMRTD classes that ship alongside the org.jmrtd:jmrtd:0.8.0 dependency). These org.jmrtd files are committed source in this repo, not the library jar — see §6.


2. Tech stack & runtime

AspectValueSource
LanguageKotlin (JVM), org.jetbrains.kotlin.jvm 1.8.20build.gradle.kts:7,49
Build toolGradle (Kotlin DSL), wrapper 8.10.2build.gradle.kts, gradle/wrapper/gradle-wrapper.properties
JVM targetJava 11 (jvmToolchain languageVersion 11; README §1.2.4 “require JAVA 11 runtime”)build.gradle.kts:21-25, README.md:53-56
PackagingGradle application plugin → distZip named vuer_emrtd.zip; distTar disabledbuild.gradle.kts:11,108-119
Main classvuer_emrtd.AppKtbuild.gradle.kts:109
CLI frameworkPicoCLI 4.7.6 (+ codegen annotation processor)build.gradle.kts:84-86
LoggingSLF4J + Logback classic 1.5.32 (console appender, root level info)build.gradle.kts:82, logback.xml
JSONGson 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 liborg.jmrtd:jmrtd:0.8.0build.gradle.kts:52
CryptoBouncyCastle bcpkix-jdk18on:1.84build.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 decodingimageio-openjpeg:0.6.6 (JPEG2000) + jnbis:2.1.2 (WSQ fingerprint)build.gradle.kts:55,58-63
TestsJUnit 5 (Jupiter) 5.11.4, kotlin("test"), WireMock standalone 3.9.1, Guava 33.3.1-jrebuild.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.

check task is disabled; dependencyCheck gates on CVSS ≥ 5.0

tasks.named("check") { enabled = false } (build.gradle.kts:15-19) — the comment defers to GitHub Dependabot/dependency-review. OWASP dependencyCheck (build.gradle.kts:121-130) fails the build on CVSS > 5.0, scans runtimeClasspath, 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). Source App.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.properties at configure time (build.gradle.kts:101-106), not declared in the build script. README §“Important for versioning” (README.md:17-18) stresses bumping config.properties on release. Same file is read at runtime by VersionProvider.kt for --version.
  • Build command (from CI .travis.yml:23-24): ./gradlew build -x check --info (skips the disabled check).
  • SBOM: ./gradlew cyclonedxBom produces a CycloneDX BOM over runtimeClasspath only (build.gradle.kts:95-98); used by the vuln-scan workflow.
  • Run command (README README.md:96-102): use the generated launcher script bin/vuer_emrtd (produced by the application plugin inside the dist zip — not committed in repo; only bin/tag-latest.sh is committed, see §5).
    • bin/vuer_emrtd -v → print version
    • bin/vuer_emrtd -c certs_folder -i passport_input.json -o output_result.json -r

CLI options (verified EmrtdCommandLineExecutor.kt:40-62)

FlagLongRequiredDefaultMeaning
-i--inputyesRaw input JSON path (deserialized to RawEMRTDData)
-c--certsyesDirectory of CSCA certificate files to load as trust anchors
-o--outputyesOutput JSON file path (must be a file, not dir; deleted+recreated if exists)
-r--revocationnofalseEnable CRL/OCSP revocation checking of DS cert + intermediaries
-ct--connection-timeoutno60 (s)Revocation connection timeout (should be < response timeout)
-rt--response-timeoutno60 (s)Revocation response timeout
-h--helpnoUsage help
-v--versionnoVersion (via VersionProvider)

Exit codes

call() returns 0 on success, 1 on any caught Throwable (logged “Exception during application run”): EmrtdCommandLineExecutor.kt:64-79.


4. Top-level structure

PathWhat it isVerified
src/main/kotlin/vuer_emrtd/App core: CLI executor, cert-store loader, entrypointlisted + 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 loadersread
src/main/kotlin/vuer_emrtd/exceptions/Two custom exceptions (CertificateRevokedException, RevocationTechnicalException)listed
src/main/kotlin/vuer_emrtd/utils/Gson Base64 adapter, PicoCLI version providerread
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 parameterslisted
src/main/resources/config.properties (version), logback.xmlread
src/test/kotlin/...JUnit 5 tests for IdCardChecker, revocation checkers, version providerread
src/test/resources/emrtd/{input,output}/Golden input/output JSON pairs for the data-driven IdCardChecker testlisted
src/test/resources/revocation/PEM/CRT/DER fixtures (CAs, CRLs, OCSP responses) for revocation testslisted
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 scriptsread
gradle/, gradlew, gradlew.batGradle wrapper 8.10.2verified
.travis.ymlTravis CI: build + GitHub Releases deploy on tagsread

40 Kotlin files under src/main/kotlin (verified by find ... | wc -l).

No Git-LFS pointers

git lfs ls-files returns empty and file docs/9303_p10_cons_en.pdf reports a real PDF 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].

  1. Aborts unless current branch is master (bin/tag-latest.sh:13-16).
  2. Requires a <tag> arg (:21-24).
  3. If master already contains <tag> and --force not given → abort; with --force → delete the tag locally and on origin (:26-36).
  4. Deletes the latest tag locally and on origin, then re-creates latest on HEAD, also creates <tag>, and git push --tags (:40-44).

This script pushes to origin and deletes remote tags

tag-latest.sh performs git push --delete origin ... and git push --tags. It is a release-automation script, not safe to run casually. (Read-only doc — not executed.)

CI Python scripts under .github/scripts/ (not bin/)

  • .github/scripts/download-and-compare-csca.py — scrapes the Hungarian registry page, compares remote CSCA filenames against certs/ 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 — runs grype sbom:build/reports/bom.json, filters High/Critical, Slack-notifies. Runs weekly (vulnerability-scan.yml, Mondays 10:00 UTC, after ./gradlew cyclonedxBom). Requires Python deps requests, beautifulsoup4 (former) and the grype binary (latter).

6. Key modules / services / entities

Entry & orchestration (vuer_emrtd package)

  • App.ktmain(); hands argv to PicoCLI EmrtdCommandLineExecutor, exits with its code.
  • EmrtdCommandLineExecutor.kt — PicoCLI @Command (name = "vuer_emrtd"). call() pipeline (:64-79): loadCertStore() → read input file → prepare output file → Gson-parse RawEMRTDData → build PassportNfcInputIdCardChecker.check(...) → Gson-serialize Passport → write output. Gson is configured with a ByteArrayToBase64TypeAdapter, excludeFieldsWithModifiers(STATIC), serializeNulls(), setPrettyPrinting() (:163-170).
  • MRTDCertStore.kt — wraps a jMRTD MRTDTrustStore. load(dir) recursively reads every file under the certs dir (sorted), parses each as an X.509 cert via BouncyCastle, wraps as TrustAnchor, and registers them as both a Collection CertStore and CSCA anchors (:22-58). Adds the BC provider in init. 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): constructs PassportNFC, calls verifySecurity(), then maps jMRTD parsed data groups into the output Passport:
    • 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.
  • PassportNFC.ktthe core verification engine (~825 lines). Constructed from PassportNfcInput; parses EF.SOD, EF.COM, DG1, DG2, DG7, DG11, DG12, DG14, DG15 (and DG13 only when DG1 says issuing state HUN and document code I) via LDSFileWrapper. verifySecurity() (:200-225) runs, in order:
    1. verifyCS() — builds the cert chain from the DS cert to a CSCA trust anchor using PKIX (delegates to PassportNfcUtils.getCertificateChain), checks SOD-vs-DS issuer/serial consistency, sets the CS verdict + chain + PassportChainInfo. Uses dg12.dateOfIssue (parsed BASIC_ISO_DATE, end-of-day 23:59) as the PKIX validity date when present (:491-497).
    2. verifyDS() — verifies EF.SOD signature against the embedded DS certificate (checkDocSignature, with RSA-PSS salt brute-forcing findSaltRSAPSS over 0..512, and special handling for SSAwithRSA/PSS / RSA algorithms; SHA-256 MGF1).
    3. verifyHT() — recomputes each data-group hash and compares to the stored hashes in EF.SOD (verifyHash). Skips DG3/DG4 gracefully when EAC failed/absent.
    4. verifyPACE() — sets the SAC/PACE feature status from EF.CardAccess (always NOT_PRESENT here since cardAccessFile is never populated from the JSON input).
    5. verifyAA() — Active Authentication, but guarded by service != null which 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 builder getCertificateChain(...) (:189-289): uses CertPathBuilder.getInstance("PKIX","BC"), adds the DS cert + all CSCA-store certs as intermediaries, and attaches a revocation checker — DssCertRevocationChecker when -r is on, else EmptyRevocationChecker. Sets buildParams.isRevocationEnabled = checkRevocation and buildParams.date. On RevocationTechnicalException (timeout) it marks executionStatus = FAILURE with sub-indication. Returns CertificateChainResult(chain, executionStatus).
  • LDSFileWrapper.kt — maps a RawEMRTDData field / EF FID to the right jMRTD *File parser (getLDSFile) and to raw bytes (getBinary). DG13 → HungarianEszemelyiDG13File. DG8/9/10/16 throw “not yet supported”.
  • ImageUtil.kt — decodes WSQ fingerprint images via Jnbis and everything else via ImageIO, always producing a PNG alongside the original bytes (PassportImage).

Revocation (vuer_emrtd.nfc.revocation)

  • DssCertRevocationChecker.kt — a PKIXRevocationChecker backed 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, throws CertPathBuilderException. Configures CommonCertificateVerifier with trusted/adjunct sources, ExceptionOnStatusAlert on revoked/expired, LogOnStatusAlert on 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 via isInvalid/isFailedIndication/isRevokedSubIndication.
  • EmptyRevocationChecker.kt — no-op PKIXRevocationChecker used when -r is off.
  • RevocationDataLoaderProvider.kt — builds a CustomDataLoader with socket/connection/response timeouts derived from the CLI seconds (socket = response − connection, or default 60 s).
  • CustomDataLoader.kt — extends DSS CommonsDataLoader; wraps SocketTimeoutException/ConnectTimeoutException into RevocationTechnicalException so timeouts are distinguishable downstream.
  • CustomCrlFirstRevocationDataLoadingStrategy(.Factory).kt, CustomDataLoader.kt — strategy that prefers CRL over OCSP (files listed; factory wired in DssCertRevocationChecker.kt:75).

Models (vuer_emrtd.models)

  • RawEMRTDData.ktinput contract: nullable ByteArray fields sodFile, 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.ktoutput 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 in PassportNFC’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 single MOTHERS_NAME_TAG (0x5F5B) UTF-8 field → optionalData. EF.DG13 tag from DataGroup superclass.
  • ExecutionStatus.ktdata 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’s MRTDCertStore which only exercises the collection/anchor path.
  • Constants.ktsha256="SHA-256", withRSA="withRSA/PSS", image-decode error strings, cert-store SPI class name.
  • JMRTDSecurityProvider.kt, FeatureStatus.kt, VerificationStatus.kt, MRTDTrustStore.kt and org/jmrtd/cert/* (KeyStoreCertStoreSpi, KeyStoreCertStoreParameters, PKDCertStoreParameters, PKDMasterListCertStoreParameters) — vendored jMRTD internals (listed; VerificationStatus/FeatureStatus used heavily by PassportNFC for verdicts — see gaps).

Utils

  • ByteArrayToBase64TypeAdapter.kt — Gson (de)serializer mapping ByteArray ↔ Base64 string. This is why every ByteArray in input/output JSON is Base64.
  • VersionProvider.kt — PicoCLI IVersionProvider reading config.properties from the classpath → "vuer_emrtd v<version>".

7. Configuration

  • src/main/resources/config.properties — single key version=1.3.4. Drives both the build’s project.version (build.gradle.kts:101-106) and the runtime --version output (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 level info.
  • gradle.propertiesorg.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; Travis deploy.token.secure encrypted blob in .travis.yml. Slack webhook is injected via SLACK_WEBHOOK_URL env in the GitHub workflows.

Hard-coded NVD API key

build.gradle.kts:127 commits 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). Also kotlin("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 in src/test/resources/emrtd/input, runs IdCardChecker.check with and without revocation, and deep-diffs the resulting JSON against the matching .../output/<name>.json golden file (Guava Maps.difference), excluding the cert chain / DS cert from comparison. @Isolated. Loads certs from ./certs.
    • nfc/DssCertRevocationCheckerTest.kt, nfc/EmptyRevocationCheckerTest.kt — revocation behavior (use the src/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 --info which 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, plus manual/test-input.json); revocation fixtures (CAs, CRLs, OCSP responses) under src/test/resources/revocation/.

Tests depend on a working ./certs directory and may hit the network

IdCardCheckerTest runs 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/** and build.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/--revocation is set (DssCertRevocationChecker.kt, RevocationDataLoaderProvider.kt). Integration with the rest of FaceKom is out-of-process: a caller writes the input JSON (RawEMRTDData shape), invokes the CLI, and reads the output JSON (Passport shape). Trust anchors are supplied as a local certs directory (-c).

Operational coupling (not a code dependency):

  • Hungarian CSCA registry nyilvantarto.hu — source of truth for certs/ (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 in download-and-compare-csca.py:65).

10. Verified gotchas

AA and PACE never actually execute in this offline mode

verifyAA() is gated on service != null (PassportNFC.kt:220-222) and service (a PassportService) is never set from JSON input → AA is effectively skipped. cardAccessFile is never populated, so verifyPACE() always yields NOT_PRESENT SAC. Only Passive Authentication (CS + DS + HT) is real here.

DG13 only parsed for Hungarian I-type documents

PassportNFC.kt:179-188 parses DG13 as HungarianEszemelyiDG13File only when DG1’s issuing state is HUN and document code is I; otherwise DG13 is ignored. A decode failure sets executionStatus = 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 in catch { /* 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:149 hard-codes the NativePRNGNonBlocking algorithm — 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 -o target before writing and refuses a directory path (EmrtdCommandLineExecutor.kt:119-141). Callers must not point -o at 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-ref empty); latest/version-tag behavior in tag-latest.sh and .travis.yml is 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 of org/jmrtd/cert/* (KeyStoreCertStoreSpi, KeyStoreCertStoreParameters, PKDCertStoreParameters, PKDMasterListCertStoreParameters); models PersonDetails.kt, AdditionalPersonDetails.kt, AdditionalDocumentDetails.kt, BacInfo.kt; nfc/EACCredentials.kt; nfc/revocation/CustomCrlFirstRevocationDataLoadingStrategy.kt + ...Factory.kt; exceptions CertificateRevokedException.kt, RevocationTechnicalException.kt (the latter’s role is confirmed via its usages).
  • Tests read: only IdCardCheckerTest.kt line-by-line; DssCertRevocationCheckerTest.kt, EmptyRevocationCheckerTest.kt, RevocationDataLoaderProviderTest.kt, VersionProviderTest.kt were 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 all certs/** and src/test/resources/** .der/.crt/.cer/.pem files were not decoded — only their existence/type recorded (per binary-safety rule).
  • Input/output JSON field exhaustiveness: RawEMRTDData/Passport field lists are from the Kotlin classes; the exact nested shape of featureStatus/verificationStatus in output JSON comes from the (unread) jMRTD FeatureStatus/VerificationStatus classes.

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.properties
  • bin/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.py
  • src/main/resources/config.properties, src/main/resources/logback.xml
  • src/main/kotlin/vuer_emrtd/App.kt, EmrtdCommandLineExecutor.kt, MRTDCertStore.kt
  • src/main/kotlin/vuer_emrtd/nfc/IdCardChecker.kt, PassportNFC.kt, PassportNfcUtils.kt, LDSFileWrapper.kt, ImageUtil.kt, EmptyRevocationChecker.kt
  • src/main/kotlin/vuer_emrtd/nfc/revocation/DssCertRevocationChecker.kt, RevocationDataLoaderProvider.kt, CustomDataLoader.kt
  • src/main/kotlin/vuer_emrtd/models/RawEMRTDData.kt, Passport.kt, PassportNfcInput.kt, PassportImage.kt, PassportChainInfo.kt, CertificateChainResult.kt
  • src/main/kotlin/vuer_emrtd/utils/VersionProvider.kt, ByteArrayToBase64TypeAdapter.kt
  • src/main/kotlin/org/jmrtd/HungarianEszemelyiDG13File.kt, ExecutionStatus.kt, Constants.kt, MRTDTrustStore.kt
  • src/test/kotlin/vuer_emrtd/nfc/IdCardCheckerTest.kt
  • Directory listings: src/ tree, bin/, certs/, certs/link/, docs/, .github/, src/test/resources/ (+ file/git lfs ls-files checks on binaries)