An Electron-based desktop application for macOS to read and verify ISO 18013-5 Mobile Driver's Licenses (mDL) over NFC Negotiated Handover transitioning to Bluetooth Low Energy (BLE), developed via Google Antigravity. This project leverages OWF mdoc-ts.
This verifier application establishes a secure connection with a mobile wallet, exchanges encrypted requests/responses, decrypts mDL attributes, and runs a comprehensive suite of cryptographic integrity audits.
Important: This code works for my purposes but is experimental at best.
- NFC TNEP Negotiated Handover: Negotiates BLE transfer parameters over NFC using a PC/SC compliant NFC reader (e.g., ACS ACR1252). Performs AID selection, capability container parsing, TNEP service selection, and reads the Handover Select (
Hs) message.- HCE Connection Recovery: Terminates connections with the
SCARD_RESET_CARDdisposition to recycle the card state and reset the phone's HCE stack. This prevents the phone from getting into a wedged state that causesSCARD_E_NOT_TRANSACTED (0x80100016)on subsequent taps. - Stabilization Delays: Implements a 150ms reader channel stabilization delay and a 100ms NDEF preparation delay to allow mobile wallets sufficient time to bind background services and generate keys.
- HCE Connection Recovery: Terminates connections with the
- Dynamic BLE Service UUID Injection: Generates a random 128-bit UUID for each session, serializes it in little-endian format, and injects it into the Handover Request (
Hr) BLE Carrier Configuration Record so the phone's wallet knows which service UUID to advertise. - Robust BLE Transfer & Spec Compliance:
- CoreBluetooth MTU Fallback: Falls back to a conservative, spec-compliant MTU of 23 bytes (19-byte data fragments) to ensure reliable writes on macOS CoreBluetooth without relying on explicit MTU negotiation.
- Spec-Compliant BLE Teardown: Implements a GATT disconnect wait sequence where the verifier waits for the phone's wallet app to cleanly terminate the BLE session after writing the
0x02(END) command, preventing phone GATT resource lockups, with a 5-second fallback forced disconnect. - Session Active Tracking: Tracks the active session state (
sessionActive) so that unexpected disconnects (e.g. before data transmission finishes) trigger a connection drop error rather than a silent failure. - Scanning Timeout & Recovery: Implements a 15-second scanning timeout that cancels pending scans and triggers error callbacks if the target peripheral cannot be found.
- GATT & Transfer Error Handling: Full error boundary propagation for adapter state changes, connection failures, service/characteristic discovery errors, notification subscription failures, and individual fragment write failures, ensuring that the reader lock is released and the UI is notified.
- Secure Key Agreement & Encryption:
- Derives
SKReaderandSKDevicesession keys via ECDH (P-256) and HKDF-SHA256 with the CBOR Tag 24 wrappedSessionTranscript. - Derives the device authentication MAC key (
EMacKey) using the raw, unhashedSessionTranscriptbytes as the KDF salt, complying with ISO/IEC 18013-5 Section 9.1.1.4. - Encrypts/decrypts payloads using AES-256-GCM.
- Derives
- Cryptographic Audit Suite:
- Validates
DeviceAuthsignatures orDeviceMACtag verification. Includes a parser translation workaround incose.mac0.verifyto reconstruct the standard 4-elementMAC0_structurearray["MAC0", protectedHeaders, h'', payload]when the underlying library generates a buggy 3-element array. - Fixes verification fall-through in
@owf/mdoclibrary by patchingDeviceAuth.verifywith a clean return exit on successfulDeviceMACvalidation, preventing fall-through to the default signature error handler. - Verifies
IssuerAuthsignatures (Mobile Security Object - MSO) against trusted DS certificates. - Recalculates value digest hashes for each namespace element to ensure data integrity.
- Validates
- Interactive Dashboard UI:
- Side-by-Side Responsive Flex Layout: Places the decoded mDL card and the Cryptographic Audit log panel side-by-side, wrapping cleanly on narrower viewports.
- State-Driven UI Resets: Instantly clears old attributes and resets status indicators to unverified
'-'states on starting a new reader session or simulator demo to prevent data persistence across scans. Instantly resets UI results on bothNFC_WAITINGandNFC_READINGstates to handle subsequent scans cleanly. - CSS Specificity Bug Fix: Configures
#view-profileto default todisplay: none;and#view-profile.active { display: flex; }to resolve the CSS ID specificity collision that prevented result panels from hiding. - Bulletproof Inline Style Overrides: Uses explicit JavaScript inline style overrides (
display: none/display: flex) with comprehensive try/catch debug logging to bypass stylesheet caching issues in Electron. - DateObject Serialization Translation: Formats claims to
YYYY-MM-DDstrings before IPC transport to avoid serialization prototype loss across Electron processes. - Terminal-like logs console displaying real-time APDU command blocks and BLE status.
- Built-in Hardware Simulator mode for testing without physical readers.
main.js: Main Electron process (IPC controller, lifecycle, BLE peripheral scanning).preload.js: Exposes secure IPC API bridges to the renderer process.renderer.js: Binds DOM elements, controls button clicks, and prints logs. Handles state-driven clearing and updates.nfc-handler.js: Interfaces with@pokusew/pcscliteto run the NFC TNEP state machine and handle connection drop retap prompts.ble-handler.js: GATT client interfacing with@abandonware/nobleto handle fragmented data transfer, conservative MTU fallback, compliance teardown, session tracking, scan timeouts, and error propagation.mdl-parser.js: Parses CBOR, derives MAC/session keys, executes cryptographic audits, and formats date string outputs.simulator.js: Mimics NFC APDU transactions, BLE packet reassembly, and cryptographical validations for local demoing.index.html&index.css: Page structure and responsive side-by-side stylesheet rules.
- OS: macOS
- Hardware: A PC/SC-compatible NFC Reader (e.g. ACS ACR1252) connected via USB.
- Software Dependencies:
- Node.js (v18+)
- PC/SC Smart Card Daemon (running natively on macOS)
-
Clone the repository and navigate to the directory:
git clone https://github.com/timbl-ont/mdoc-reader-macos.git cd mdoc-reader-macos -
Install dependencies:
npm install
-
Rebuild native modules (
@pokusew/pcscliteand@abandonware/noble) for Electron:npm run rebuild
To launch the Electron desktop interface:
npm start- Simulator Mode: Click "Run Hardware Simulator" to execute a full mock verification protocol.
- Live Device Tap: Click "Start Listening", tap a mobile wallet (e.g. Android Multipaz wallet) on your USB NFC reader, and follow the phone's authentication/consent prompts.