Production Windows Configuration for Key Fob Provisioning

# Production Windows Configuration for Key Fob Provisioning

## Problem

ACS ACR1281U-C1 reader on Windows: the first `CLA=0x80` (GlobalPlatform) APDU takes **23 seconds** because Windows Smart Card Service (SCardSvr) searches Windows Update for a smart card minidriver on every proprietary command. The search times out after ~23s (network SYN retry).

This makes the 7-second contactless provisioning budget impossible.

## Root Cause

SCardSvr intercepts `CLA=0x80` commands and attempts to load a minidriver via Windows Update. The network query times out after ~23 seconds. After the first command, the result is cached and subsequent commands return in ~20ms.

## Fix: Registry Settings (run as Admin)

### 1. Disable Windows Update driver search (eliminates the 23s delay)

```
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\DriverSearching
  SearchOrderConfig = 0 (DWORD)

HKLM\SOFTWARE\Policies\Microsoft\Windows\DriverSearching
  DriverSearchMode = 0 (DWORD)
  SearchOrderConfig = 0 (DWORD)
```

### 2. Disable Smart Card Credential Provider node enumeration

```
HKLM\SOFTWARE\Policies\Microsoft\Windows\SmartCardCredentialProvider
  TurnOffSmartCardNode = 1 (DWORD)
```

### 3. Restart Smart Card Service

```powershell
Restart-Service SCardSvr -Force
```

System restart recommended after applying.

## Deployment Script

`scripts/configure-windows.ps1` in the repo automates all three steps.

## Code-Level Requirements (implemented)

- **Per-thread `SCARDCONTEXT`** - monitor and reader threads use separate contexts
- **`SCARD_STATE_UNAWARE`** - initial state in `SCardGetStatusChange`
- **Single INITIALIZE UPDATE** - P1=0x00 only, never retry with other key versions
- **Case-4 APDU with `Le=0x00`** - card requires the Le byte
- **Correct key version: `P1=0x00`** - uses card default (resolves to 0xFF)
- **SELECT ISD before GP commands** - AID `A000000151000000` (NXP JCOP)
- **Card cryptogram verification** - local check before EXTERNAL AUTHENTICATE (safety gate)

## Expected Performance

| Operation | Before Fix | After Fix |
|-----------|-----------|-----------|
| INITIALIZE UPDATE (1st CLA=0x80) | ~23,000ms | ~50ms |
| INITIALIZE UPDATE (subsequent) | ~20ms | ~20ms |
| EXTERNAL AUTHENTICATE | ~20ms | ~20ms |
| SELECT (CLA=0x00) | ~15ms | ~15ms |
| Pseudo-APDU (CLA=0xFF) | ~1ms | ~1ms |

## Key Fob Details

- **Card type**: JCOP 4.5 P71 (OEF ID B252)
- **ISD AID**: `A000000151000000` (NXP JCOP, NOT standard GP `A000000003000000`)
- **SCP03 keys**: Shared static (not per-card derived) - `255/1` (ENC), `255/2` (MAC), `255/3` (DEK)
- **ATR**: `3B8580018073C821100E` (ISO 14443-4, Part 4 format)
- **Protocol**: T=1 (contactless ISO 14443-4)
- **Reader**: ACS ACR1281U-C1 DualBoost II, PICC 0 slot (contactless)
- **GP auth counter**: ~15 (estimated), resets on successful auth, does NOT reset on power cycle

## SCP03 Session Establishment Flow

1. SELECT ISD (`A000000151000000`) - safe, no counter impact
2. GET DATA IDENTIFY (tag DF28) - safe, returns OEF ID
3. INITIALIZE UPDATE (P1=0x00) - safe, 9000 does not decrement counter
4. Card cryptogram verification (local) - safety gate, no card interaction
5. EXTERNAL AUTHENTICATE (C-MAC) - only sent if step 4 passes

Verified on real hardware: card cryptogram matched, MAC accepted (9000), counter reset.

## Provisioning Flow

Container folder: `FESN.txt`, `irk_keyfob.key`, `keyfob_private.der`, `keyfob_public.der`
Certs folder: `ica_cert.der`, `cms_root_cert.der`

Personalization order (device cert LAST):
1. A003 - Private key (S-DEK encrypted)
2. A001 - SPID
3. A004 - ICA certificate
4. A005 - CMS root certificate
5. A006 - BLE IRK
6. A002 - Device certificate (last, for read-back verification)

FACTORY transition is separate/explicit - not done during personalization.

## Lessons Learned

1. SCardSvr intercepts CLA=0x80 for minidriver search, not the CCID driver
2. The delay is one-time but 23s is unacceptable for 7s budget
3. T=0 protocol corrupts CCID state - never use on contactless
4. SCardReconnect causes card removal - do NOT reconnect during transmit
5. Multiple key versions risk locking the card - always use P1=0x00
6. GET DATA requires SELECT ISD first (returns 6D00 without it)
7. All JCShell scripts use contactless (`/term SCComm:1` = PICC slot)


id: 46c5c42fcaec4f298b5cac05534bca67
parent_id: beb251c3b3f9490285e6cb68942a5145
created_time: 2026-08-03T16:18:21.131Z
updated_time: 2026-08-04T07:55:42.668Z
is_conflict: 0
latitude: 0.00000000
longitude: 0.00000000
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 1785773901131
user_created_time: 2026-08-03T16:18:21.131Z
user_updated_time: 2026-08-04T07:55:42.668Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
is_locked: 0
extracted_resource_ids: 
type_: 1