{"id":47847936,"url":"https://github.com/jesssullivan/zig-ctap2","last_synced_at":"2026-04-25T20:03:06.413Z","repository":{"id":346093625,"uuid":"1188462886","full_name":"Jesssullivan/zig-ctap2","owner":"Jesssullivan","description":"Hermetic CTAP2/FIDO2 library in Zig — direct USB HID for YubiKey/security keys, no Apple entitlements needed","archived":false,"fork":false,"pushed_at":"2026-04-03T12:21:28.000Z","size":3651,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T21:49:54.467Z","etag":null,"topics":["ctap2","ffi","fido2","library","webauthn"],"latest_commit_sha":null,"homepage":"https://transscendsurvival.org/zig-ctap2/","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Jesssullivan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-22T05:27:36.000Z","updated_at":"2026-04-03T12:21:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Jesssullivan/zig-ctap2","commit_stats":null,"previous_names":["jesssullivan/zig-ctap2"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/Jesssullivan/zig-ctap2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jesssullivan%2Fzig-ctap2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jesssullivan%2Fzig-ctap2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jesssullivan%2Fzig-ctap2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jesssullivan%2Fzig-ctap2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jesssullivan","download_url":"https://codeload.github.com/Jesssullivan/zig-ctap2/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jesssullivan%2Fzig-ctap2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32274987,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T18:29:39.964Z","status":"ssl_error","status_checked_at":"2026-04-25T18:29:32.149Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ctap2","ffi","fido2","library","webauthn"],"created_at":"2026-04-03T21:31:49.259Z","updated_at":"2026-04-25T20:03:06.401Z","avatar_url":"https://github.com/Jesssullivan.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zig-ctap2\n\nPortable CTAP2/FIDO2 library in Zig — direct USB HID communication with security keys (YubiKey, SoloKeys, etc.), no Apple entitlements or platform authentication frameworks needed.\n\n**License:** Zlib OR MIT\n\n## Why\n\nApple's `ASAuthorizationController` requires a restricted entitlement + provisioning profile for WebAuthn in general-purpose browsers. This library talks directly to FIDO2 devices over USB HID via IOKit (macOS) and hidraw (Linux), bypassing platform authentication frameworks entirely.\n\n## Features\n\n- **CTAP2 protocol**: makeCredential, getAssertion, getInfo, with structured response parsing\n- **PIN protocol v2**: ECDH P-256 key agreement, AES-256-CBC, HMAC-SHA-256 for PIN-authenticated operations\n- **CTAPHID framing**: 64-byte packet fragmentation/reassembly, CID management, keepalive handling\n- **Minimal CBOR codec**: encoder/decoder for the CTAP2 subset (integers, byte/text strings, arrays, maps, booleans)\n- **Platform HID transports**: macOS (IOKit), Linux (hidraw)\n- **C FFI**: 16 exported functions callable from Swift, C, C++, or any language with C interop\n- **Error mapping**: All CTAP2 status codes mapped to human-readable messages\n- **Property-based tests**: 1000-iteration roundtrip tests for CBOR and CTAPHID framing\n\n## Requirements\n\n- Zig 0.14.1+\n- macOS 13+ (IOKit) or Linux (hidraw)\n- USB security key (tested with YubiKey 5C NFC)\n\n## Installation\n\n### Zig Package Manager (recommended)\n\n```bash\nzig fetch --save git+https://github.com/Jesssullivan/zig-ctap2.git\n```\n\nThen in your `build.zig`:\n\n```zig\nconst dep = b.dependency(\"zig-ctap2\", .{ .target = target, .optimize = optimize });\nexe.root_module.addImport(\"zig-ctap2\", dep.module(\"zig-ctap2\"));\n```\n\n### Git Submodule (C FFI consumers)\n\n```bash\ngit submodule add https://github.com/Jesssullivan/zig-ctap2.git vendor/ctap2\ncd vendor/ctap2 \u0026\u0026 zig build -Doptimize=ReleaseFast\n```\n\nLink `-lctap2` and include `ctap2.h`. At final link time, add platform frameworks:\n- **macOS:** `-framework IOKit -framework CoreFoundation`\n- **Linux:** no extra libraries needed (uses hidraw via kernel)\n\n## Build\n\n```bash\n# Static library (libctap2.a)\nzig build -Doptimize=ReleaseFast\n\n# Run unit tests\nzig build test\n\n# Run property-based tests\nzig build test-pbt\n\n# Run hardware tests (requires YubiKey connected)\nYUBIKEY_TESTS=1 zig build test-hardware\n```\n\nWith [just](https://just.systems) (recommended):\n\n```bash\njust test-all     # unit + PBT tests\njust build        # ReleaseFast static library\njust info         # show library stats\njust              # list all recipes\n```\n\nWith Nix:\n\n```bash\nnix develop       # dev shell (zig, just, detect-secrets, pre-commit)\nnix build         # build library package\n```\n\n## Architecture\n\n```mermaid\ngraph TD\n    A[Application / Browser] --\u003e|C FFI| B[ffi.zig]\n    B --\u003e C[ctap2.zig\u003cbr/\u003eCommands + Response Parsing]\n    B --\u003e D[pin.zig\u003cbr/\u003ePIN Protocol v2]\n    C --\u003e E[cbor.zig\u003cbr/\u003eCBOR Codec]\n    D --\u003e E\n    C --\u003e F[ctaphid.zig\u003cbr/\u003eHID Framing]\n    D --\u003e F\n    F --\u003e G{Platform}\n    G --\u003e|macOS| H[hid_macos.zig\u003cbr/\u003eIOKit HID]\n    G --\u003e|Linux| I[hid_linux.zig\u003cbr/\u003ehidraw]\n```\n\n### Registration Flow\n\n```mermaid\nsequenceDiagram\n    participant App\n    participant FFI as ffi.zig\n    participant CTAP as ctap2.zig\n    participant CBOR as cbor.zig\n    participant HID as ctaphid.zig\n    participant Key as YubiKey\n\n    App-\u003e\u003eFFI: ctap2_make_credential_parsed()\n    FFI-\u003e\u003eCTAP: encodeMakeCredential()\n    CTAP-\u003e\u003eCBOR: CBOR encode request\n    CBOR--\u003e\u003eCTAP: bytes\n    CTAP-\u003e\u003eHID: CTAPHID_CBOR (fragmented)\n    HID-\u003e\u003eKey: USB HID packets\n    Note over Key: User touches key\n    Key--\u003e\u003eHID: Response packets\n    HID--\u003e\u003eCTAP: Reassembled CBOR\n    CTAP-\u003e\u003eCTAP: parseMakeCredentialResponse()\n    CTAP--\u003e\u003eFFI: credential_id, attestation_object\n    FFI--\u003e\u003eApp: Structured result\n```\n\n### Error Handling\n\n```mermaid\ngraph TD\n    E[CTAP2 Status Byte] --\u003e|0x00| OK[Success]\n    E --\u003e|0x2E| NC[No Credentials\u003cbr/\u003efor this site]\n    E --\u003e|0x27| OD[Operation Denied\u003cbr/\u003eby user]\n    E --\u003e|0x31| IP[Incorrect PIN]\n    E --\u003e|0x32| PB[PIN Blocked]\n    E --\u003e|0x35| PNS[PIN Not Set]\n    E --\u003e|0x36| PV[PIN Policy\u003cbr/\u003eViolation]\n\n    style OK fill:#2d5,stroke:#1a3\n    style NC fill:#d52,stroke:#a31\n    style OD fill:#d52,stroke:#a31\n    style IP fill:#d85,stroke:#a63\n    style PB fill:#d52,stroke:#a31\n    style PNS fill:#d85,stroke:#a63\n    style PV fill:#d85,stroke:#a63\n```\n\n## C API\n\nAll functions are blocking (with timeouts) and thread-safe. See [`include/ctap2.h`](include/ctap2.h) for full signatures.\n\n### Core Operations\n\n```c\n#include \"ctap2.h\"\n\n// Enumerate connected FIDO2 devices\nint count = ctap2_device_count();\n\n// Register a credential (raw CBOR response)\nint bytes = ctap2_make_credential(\n    client_data_hash, rp_id, rp_name,\n    user_id, user_id_len, user_name, user_display_name,\n    alg_ids, alg_count, resident_key,\n    result_buf, result_buf_len\n);\n\n// Authenticate (raw CBOR response)\nint bytes = ctap2_get_assertion(\n    client_data_hash, rp_id,\n    allow_list_ids, allow_list_id_lens, allow_list_count,\n    result_buf, result_buf_len\n);\n\n// Get device capabilities\nint bytes = ctap2_get_info(result_buf, result_buf_len);\n```\n\n### Parsed Responses\n\nThese perform the CTAP2 command AND parse the CBOR, returning structured fields:\n\n```c\n// Register + parse → credential_id, attestation_object\nint status = ctap2_make_credential_parsed(\n    client_data_hash, rp_id, rp_name,\n    user_id, user_id_len, user_name, user_display_name,\n    alg_ids, alg_count, resident_key,\n    out_credential_id, \u0026out_credential_id_len,\n    out_attestation_object, \u0026out_attestation_object_len\n);\n\n// Authenticate + parse → credential_id, auth_data, signature, user_handle\nint status = ctap2_get_assertion_parsed(\n    client_data_hash, rp_id,\n    allow_list_ids, allow_list_id_lens, allow_list_count,\n    out_credential_id, \u0026out_credential_id_len,\n    out_auth_data, \u0026out_auth_data_len,\n    out_signature, \u0026out_signature_len,\n    out_user_handle, \u0026out_user_handle_len\n);\n```\n\n### Pure Parsing (no I/O)\n\nParse raw CTAP2 response bytes you already have:\n\n```c\nctap2_parse_make_credential_response(response, len, ...);\nctap2_parse_get_assertion_response(response, len, fallback_cred, ...);\n```\n\n### PIN Protocol\n\n```c\n// Check remaining PIN retries\nint retries;\nctap2_get_pin_retries(\u0026retries);\n\n// Get PIN token (ECDH + AES-256-CBC handshake)\nuint8_t pin_token[32];\nctap2_get_pin_token(\"123456\", pin_token, 32);\n\n// PIN-authenticated registration\nctap2_make_credential_with_pin(\n    client_data_hash, rp_id, rp_name, ...,\n    pin_token, 2,  // pin_protocol = 2\n    out_credential_id, \u0026out_credential_id_len,\n    out_attestation_object, \u0026out_attestation_object_len\n);\n\n// PIN-authenticated assertion\nctap2_get_assertion_with_pin(\n    client_data_hash, rp_id, ...,\n    pin_token, 2,\n    out_credential_id, \u0026out_credential_id_len, ...\n);\n```\n\n### Utilities\n\n```c\n// Human-readable error messages\nconst char *msg = ctap2_status_message(0x35);\n// → \"PIN not set - configure a PIN on your security key first\"\n\n// Debug: last IOKit return code\nint ioret = ctap2_debug_last_ioreturn();\n```\n\n### Status Codes\n\n| Code | Meaning |\n|------|---------|\n| `CTAP2_OK` (0) | Success |\n| `CTAP2_ERR_NO_DEVICE` (-1) | No FIDO2 device connected |\n| `CTAP2_ERR_TIMEOUT` (-2) | Device communication timeout |\n| `CTAP2_ERR_PROTOCOL` (-3) | CTAPHID protocol error |\n| `CTAP2_ERR_BUFFER_TOO_SMALL` (-4) | Output buffer too small |\n| `CTAP2_ERR_OPEN_FAILED` (-5) | Failed to open HID device |\n| `CTAP2_ERR_WRITE_FAILED` (-6) | USB write failed |\n| `CTAP2_ERR_READ_FAILED` (-7) | USB read failed |\n| `CTAP2_ERR_CBOR` (-8) | CBOR encoding/decoding error |\n| `CTAP2_ERR_DEVICE` (-9) | CTAP2 device error (check status byte) |\n| `CTAP2_ERR_PIN` (-10) | PIN protocol error |\n\n## Entitlements\n\nOn macOS with hardened runtime, add to your entitlements:\n\n```xml\n\u003ckey\u003ecom.apple.security.device.usb\u003c/key\u003e\n\u003ctrue/\u003e\n```\n\nThe user must grant **Input Monitoring** permission in System Settings \u003e Privacy \u0026 Security.\n\nNo other entitlements needed — no `com.apple.developer.web-browser.public-key-credential`, no provisioning profile, no Apple Developer portal configuration.\n\n## Integration with cmux\n\nThis library powers the FIDO2/WebAuthn support in [cmux](https://github.com/Jesssullivan/cmux) (fork), integrated as a git submodule at `vendor/ctap2`. The JS bridge in WKWebView intercepts `navigator.credentials.create/get` and routes to libctap2 via Swift C FFI.\n\n## Tested Devices\n\n- YubiKey 5C NFC (USB, firmware 5.x)\n\n## Status\n\n- [x] makeCredential (registration)\n- [x] getAssertion (authentication)\n- [x] getInfo (device capabilities)\n- [x] CBOR response parsing (structured result types)\n- [x] CTAP2 error code mapping (human-readable messages)\n- [x] PIN protocol v2 (ECDH P-256, AES-256-CBC, HMAC-SHA-256)\n- [x] Property-based tests (CBOR + CTAPHID, 1000 iterations each)\n- [x] Hardware integration tests (YubiKey roundtrips)\n- [ ] Extensions (credProtect, hmac-secret)\n- [ ] NFC transport\n\n## License\n\nDual-licensed under [Zlib](https://opensource.org/licenses/Zlib) and [MIT](https://opensource.org/licenses/MIT). Choose whichever you prefer.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjesssullivan%2Fzig-ctap2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjesssullivan%2Fzig-ctap2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjesssullivan%2Fzig-ctap2/lists"}