Very experimental, untested, here be dragons#722
Draft
dainnilsson wants to merge 261 commits intomainfrom
Draft
Conversation
Move OATH credential ID formatting/parsing, HMAC-SHA1, PBKDF2 key derivation, TOTP/HOTP challenge generation, OTP code formatting, and base32 key parsing from Python to Rust in yubikey-mgmt crate. Python wrapper functions in yubikit/oath.py delegate to Rust via PyO3 bindings, maintaining the existing API for callers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement core Rust infrastructure: - SmartCardConnection trait with send_and_receive + transport - SmartCardProtocol with command/response chaining, touch workaround, and SCP03 secure messaging integration - Version, Aid, Sw types and SmartCardError enum - PcscConnection now implements SmartCardConnection trait Add complete OathSession in Rust (yubikey-mgmt crate): - Session lifecycle: new, reset, validate, set_key, unset_key - Credential operations: put, list, rename, delete - Code calculation: calculate, calculate_all, calculate_code - Credential, Code, CredentialData types A pure Rust program can now enumerate PC/SC readers, open a PcscConnection, and use OathSession to manage OATH credentials. Python wrappers unchanged — still using their own SmartCardProtocol. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement complete YubiOTP protocol in yubikey-mgmt crate: Types: Slot, ConfigSlot, TktFlag/CfgFlag/ExtFlag, NdefType, ConfigState. Slot configurations: YubiOTP, HMAC-SHA1, HOTP, static password, static ticket, and update configs with builder pattern for flags. OTP HID protocol: OtpProtocol with 70-byte frame send/receive over HidConnection, sequence tracking, keepalive, and cancellation. Two session types: - YubiOtpSession<C: SmartCardConnection> for SmartCard backend - YubiOtpOtpSession for HID OTP backend Both support: get_serial, get_config_state, put/update/delete configuration, swap_slots, set_scan_map, set_ndef_configuration, and calculate_hmac_sha1. 26 Rust unit tests covering config building, CRC, NDEF, flags. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement ManagementSession in yubikey-mgmt crate: Types: Capability (bitflags), FormFactor, DeviceFlag, ReleaseType, UsbInterface, Mode, VersionQualifier. Data structures: DeviceConfig with TLV serialization, DeviceInfo with full parse() handling edge cases (v4.2.4 workaround, YK4 USB_ENABLED skip, FIPS/SKY flags, version qualifier override). ManagementSession<C: SmartCardConnection> with read_device_info (paginated), write_device_config, set_mode, device_reset, and NEO v3 fallback logic. 20 Rust unit tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement HsmAuthSession in yubikey-mgmt crate with EC P-256 support: Types: Algorithm, Credential, SessionKeys, HsmAuthError. Full session: put/delete/list credentials, symmetric and asymmetric key operations, PBKDF2 credential password derivation, session key calculation, management key operations, PIN retry handling. EC P-256 via p256 crate for asymmetric credential key pairs with SEC1 uncompressed point encoding. 10 Rust unit tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement SecurityDomainSession in yubikey-mgmt crate: Types: KeyType, Curve (with OIDs), ScpKid, KeyRef, StaticKeys. Full session: get_data, get_key_information, get_card_recognition_data, get_supported_ca_identifiers, get_certificate_bundle, reset (with SCP03 default key restore + SCP11b generation), store_data, store_certificate_bundle, store_allowlist, store_ca_issuer, delete_key, generate_ec_key, put_key (static/EC private/public). Certificates handled as raw DER bytes for flexibility. 17 Rust unit tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Complete PivSession<C: SmartCardConnection> implementation with: - All enums: KeyType, ManagementKeyType, Slot, ObjectId, PinPolicy, TouchPolicy - Management key authentication (TDES/AES ECB) - PIN/PUK management with retry tracking - Crypto operations: sign, decrypt, calculate_secret - Key management: generate, import, attest, move, delete - Certificate management with gzip compression support - Object data storage - Version-based feature support checking - Metadata queries (pin, puk, management key, slot, bio) - 18 unit tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Complete OpenPgpSession<C: SmartCardConnection> implementation with: - All enums: KeyRef, Uif, PinPolicy, Pw, Do, HashAlgorithm, KeyStatus - Algorithm attributes: RsaAttributes, EcAttributes with OID support - Full Data Object (DO) system for read/write operations - PIN management: verify, change, reset, unverify, set_pin_attempts - KDF support: KdfNone and KdfIterSaltedS2k (RFC 4880 S2K) - Key generation (RSA/EC), import with PrivateKeyTemplate, deletion - Crypto operations: sign, decrypt, authenticate with PKCS#1v1.5 padding - Certificate management: get, put, delete, attest - UIF (touch) configuration - Algorithm information queries - Factory reset - ApplicationRelatedData parsing with full discretionary data objects - 31 unit tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create Python-accessible wrappers for all Rust session types via _ykman_native.sessions module: - OathSession, PivSession, OpenPgpSession - HsmAuthSession, ManagementSession, SecurityDomainSession Uses PySmartCardConnection bridge that implements Rust's SmartCardConnection trait by calling back to Python's send_and_receive method. This allows Rust sessions to work with any Python connection type transparently. All sessions tested working against physical YubiKey via ScardSmartCardConnection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- OathSession now delegates to Rust via PyO3 for non-SCP sessions - Falls back to Python SmartCardProtocol for SCP-encrypted sessions - Fix smartcard_err() to raise proper Python ApduError, NotSupportedError, and BadResponseError instead of generic RuntimeError - Add set_version() to Rust OathSession for development device version override - Apply _override_version.patch() for dev devices reporting version 0.0.1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- HsmAuthSession now delegates to Rust via PyO3 for non-SCP sessions - Falls back to Python SmartCardProtocol for SCP-encrypted sessions - Add set_version() to Rust HsmAuthSession for dev device version override - Fix hsmauth_err() to raise InvalidPinError from yubikit.core - Convert EC key types at Python/Rust boundary (SEC1 bytes <-> cryptography objects) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- SmartCard+no-SCP path delegates to Rust via PyO3 - OTP and CTAP backends remain Python-only - Add set_version() and read_device_info_unchecked() to Rust ManagementSession - Fix parse_version_string() to handle legacy firmware strings - Add _device_info_from_native() helper for DeviceInfo conversion - Update diagnostics to handle native session Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add set_version() to Rust PivSession for dev device version override - Fix piv_err() to properly map InvalidPin and NotSupported errors - Delegate all 31 public methods with proper type conversions - Handle DER bytes <-> cryptography objects for certs and keys - Gracefully fall back to Python path for unknown object IDs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add set_version() to Rust OpenPgpSession for dev device version override - Fix openpgp_err() to properly map InvalidPin and NotSupported errors - Delegate all 30+ public methods with proper type conversions - Handle crypto objects (keys, certs) via DER bytes at Python/Rust boundary - Helper functions for hash algorithm and private key conversion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add version field and set_version() to Rust SecurityDomainSession - Add version getter/setter to PyO3 wrapper - Fix get_key_information() TLV parsing bug (was re-parsing already-unpacked values) - Delegate all public methods except put_key (requires SCP processor) - Handle KeyRef as (kid, kvn) tuples, certs as DER bytes at boundary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create PyO3 wrapper for both SmartCard and OTP HID backends - Add yubiotp_err() for proper error mapping - Add set_version() to both Rust session types - Make write_config() and send_and_receive() public for PyO3 access - Delegate SmartCard path to Rust, keep OTP HID as Python fallback - Register YubiOtpSession classes in sessions submodule Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- New device module with list_devices(), read_info(), get_name() - YubiKeyDevice type with open_smartcard()/open_otp() methods - Full product name logic ported from Python (NEO, YK4, YK5, Bio, FIPS, etc.) - Fix read_config() to use modern path for dev devices (version 0.0.1) - Three examples: list_devices, device_info, oath_list - 15 unit tests for product name logic A pure Rust program can now: list_devices() -> open_smartcard() -> OathSession::new() -> list_credentials() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add apply_device_info_fixups() for FIPS detection and NFC workarounds - Fix read_config() to use v3-only check (NEO) instead of < 4.0.0 - Add PyO3 device submodule with read_info() and get_name() functions - Make device_info_to_dict() public for cross-module reuse Python can now call _ykman_native.device.read_info(reader_name) to get DeviceInfo directly from Rust, bypassing the Python Management session. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The version_qualifier (release type, iteration) was not included in the device_info_to_dict() output, causing Python to always create a FINAL qualifier. This prevented _override_version from being applied for non-final (alpha/beta) firmware, making version-gated features fail. - Add version_qualifier dict to device_info_to_dict() in PyO3 - Parse version_qualifier in _device_info_from_native() in Python - Fixes 10 test failures in OATH and HsmAuth device tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rust DeviceInfo parsing converted auto_eject_timeout=0 to None, while Python always returns it as an int (0). This caused different _key() fingerprints in _PidGroup, preventing the OTP HID interface from being matched to the same physical device resolved via SmartCard. Fix: always return Some(value) for auto_eject_timeout and challenge_response_timeout, matching Python's behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- PIV: re-query management key metadata after version override in set_version() - PIV: map BadResponseError properly in piv_err() instead of generic RuntimeError - TLV: validate parsed length doesn't exceed data bounds (prevents panics on invalid data) - SecurityDomain: return full DER bytes (tag+length+value) from get_certificate_bundle() - OpenPGP: fix generate_ec_key to pass dotted OID string instead of enum name - OpenPGP: fix put_key to set algorithm attributes before import (matching Python path) - OpenPGP: fix RSA import to use standard format for YK5+ (not CRT) - OpenPGP: fix X25519 private key byte order reversal in native prep - OpenPGP: fix get_challenge to pass Le (expected response length) in APDU - SmartCardProtocol: add send_apdu_with_le() for APDUs needing explicit Le Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The device fingerprint key uses str() on the supported_capabilities dict, which is order-dependent. Rust's HashMap iteration order is non-deterministic, so the OTP HID and SmartCard paths could produce different dict orderings, causing device resolution to fail with 'Failed to connect to the device'. Fix by sorting transport entries (USB before NFC) before building Python dicts, and deriving Ord on Transport enum for clean sorting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When rapidly switching USB interfaces, the previously-resolved device handle may fail to open because the USB interface isn't ready yet. Previously this propagated as an uncaught exception. Now we catch the failure, move the device back to unresolved, and let the normal resolution logic (with retries) handle it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename the main Rust crate to yubikit-rs to better reflect its purpose as a Rust implementation of the YubiKit library. Updates all Cargo.toml references, workspace members, and use statements across both the core crate and the PyO3 wrapper crate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create core_types.rs with Version and Transport (moved from iso7816.rs) - Create otp_protocol.rs with OtpProtocol, YubiOtpError, and HID framing helpers (moved from yubiotp.rs) - Re-export moved types from original modules for backwards compatibility - iso7816.rs now focuses on APDU/SmartCard protocol types - yubiotp.rs now focuses on YubiOTP session logic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add ManagementOtpSession struct that wraps OtpProtocol for management operations (read_config, write_config, read_device_info, set_mode) over HID OTP connections. Extract shared config parsing into helper functions used by both SmartCard and OTP management sessions. The Rust ManagementOtpSession is exposed via PyO3 for direct use, while the Python ManagementSession continues using the Python OtpProtocol backend to avoid connection ownership issues. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add init_scp03() and init_scp11() to SmartCardProtocol in Rust - SCP03: INITIALIZE UPDATE + EXTERNAL AUTHENTICATE handshake - SCP11: ECDH key agreement (P-256), X9.63 KDF, receipt verification - Support SCP11a, SCP11b, and SCP11c variants - Fix empty data encryption bug (counter desync) - Fix KCV computation (was using wrong IV) - Fix SCP11c params byte (0x03 not 0x11) - Fix key_agreement_data to include card ephemeral key TLV - Fix store_allowlist to handle arbitrary-length serial numbers - Update all 7 PyO3 session constructors to accept SCP params - Python sessions always use native Rust SCP (no fallback) - Delete _init_python() methods and ScpProcessor fallback paths - All 401 device tests pass (375 + 11 SD + 15 SCP APDU) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Integration tests (crates/yubikit-rs/tests/device_tests.rs): - 18 tests covering all session types: Management, OATH, PIV, OpenPGP, YubiOTP, HSM Auth, and Security Domain - Requires YUBIKEY_SERIAL env var to prevent accidental runs - Skips tests based on device capabilities - Tests include: session open, version read, CRUD operations (OATH), PIN verification (PIV), application data read (OpenPGP), key info (SD) CLI example (crates/yubikit-rs/examples/ykinfo.rs): - Lists connected YubiKeys with device info - --serial filter to target a specific device - --all flag to query each application session - Demonstrates the full yubikit-rs API: device enumeration, connection opening, and session creation for all supported application types Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove try/except ImportError guards from all session modules - Remove all 'if self._native:' conditional checks - Delete dead Python fallback implementations (APDU-level code) - Clean up unused imports and helper functions - Preserve legitimate OTP/FIDO backends in management and yubiotp - Re-export InvalidPinError from piv.py and hsmauth.py for CLI usage - Update test fixtures to use ccid_connection for disconnect/reconnect - All 401 device tests pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Global version override in Rust (set_override_version/patch_version) with PyO3 bridge so Python and Rust share the same override - Each Rust session applies patch_version in init - NFC reader support: open_reader(), list_readers re-export, --reader and --list-readers flags in ykinfo example - ManagementSession and YubiOtpSession now use native Rust for OTP connections (ManagementOtpSession, YubiOtpOtpSession) - Deleted Python OtpProtocol-based backends Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restructure the monolithic Python project into a uv workspace: - packages/yubikit/: Contains the yubikit Python package with the native Rust extension (_yubikit_native) built via maturin. This is the core library providing YubiKey protocol implementations. - packages/yubikey-manager/: Contains the ykman Python package built with hatchling. Depends on yubikit. 'pip install yubikey-manager' pulls in yubikit automatically, preserving the same end-user experience. Root pyproject.toml becomes the workspace root with shared dev dependencies, test config, and linting settings. Update CI workflows: - wheels.yml: Build yubikit platform wheels via maturin-action with --manifest-path. Build yubikey-manager pure-Python wheel via uv. - source-package.yml: Build sdists for both packages. Other updates: - Remove MANIFEST.in (no longer needed with per-package build systems) - Update docs/conf.py paths for new package locations - Update .pre-commit-config.yaml ty-check paths - Fix import ordering (ruff) after package relocation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Version bumps: - yubikit, yubikey-manager: 5.9.1-dev.0 → 6.0.0-dev.0 - ykman-cli crate: 0.1.0 → 6.0.0-dev.0 Rename packages/yubikey-manager/ → packages/ykman/ (project name remains 'yubikey-manager' in pyproject.toml). Move device enumeration and connection classes to yubikit.device: - NativeFidoConnection (was ykman.hid.fido) - _NativeOtpConnection (was ykman.hid.otp) - ScardSmartCardConnection, list_readers (was ykman.pcsc) - YkmanDevice, REINSERT_STATUS, CancelledException (was ykman.base) - scan_devices, list_all_devices (was ykman.device) ykman.device and ykman.base become thin re-export shims for backward compatibility. Delete ykman.diagnostics (Rust CLI has its own diagnostics). Delete ykman.hid and ykman.pcsc (merged into yubikit.device). InvalidPinError no longer inherits ValueError (v6 breaking change that was documented and had a test waiting for it). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move pid property, reinsert (abstract), REINSERT_STATUS, and CancelledException from yubikit.device into yubikit.core.YubiKeyDevice so all device implementations share the same base interface. Remove the intermediate YkmanDevice class. ykman.base and ykman.device provide YkmanDevice as a type alias to YubiKeyDevice for backward compatibility. Rename internal classes to private: - NativeFidoConnection → _NativeFidoConnection - ScardSmartCardConnection → _NativeSmartCardConnection - _NativeCompositeDevice → _NativeYubiKeyDevice Drop public exports of connection classes from ykman.device. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…vice YubiKeyDevice no longer has an __init__. Subclasses must implement transport, pid, and fingerprint as concrete properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Switch yubikey-manager from hatchling to maturin with bindings='bin' so pip install yubikey-manager installs the native ykman CLI binary directly into the scripts directory. This follows the same pattern used by uv (pip install uv). The wheel includes both: - The ykman Python library package (via python-packages) - The native ykman binary (via maturin bin bindings) Add ykman/_find_ykman.py to locate the installed binary, and ykman/__main__.py for 'python -m ykman' support. Update CI workflows: - wheels.yml: Add ykman-wheels job (platform-specific maturin builds) - source-package.yml: Build sdist via maturin for both packages Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove SmartCardCtapDevice (FIDO-over-NFC via CCID) — this was a Python reimplementation of NFC CTAP framing that is now handled natively in Rust. Remove the fido2 package dependency from yubikit. The only fido2 imports were for SmartCardCtapDevice (CtapError, STATUS, CAPABILITY, CTAPHID constants). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
maturin-action's --manifest-path expects a Cargo.toml, not a pyproject.toml. Use working-directory to let maturin discover the pyproject.toml in each package directory. Fix autoapi_dirs in docs/conf.py to point to the new package paths under packages/. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add dbus-devel to the manylinux container packages for ykman-wheels (required by libdbus-sys, a dependency of the keyring crate). Fix ruff lint error in examples/piv_certificate.py (extra blank line). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
b43a90c to
0d49492
Compare
Use SetupDiGetClassDevs to enumerate HID devices on Windows instead of hidapi's list_all_hid_devices(). This works for FIDO devices even when not running as Administrator, since it doesn't open device handles. Changes: - Add setupdi.rs transport module using Windows SetupDi/HID APIs to enumerate Yubico HID devices by parsing VID/PID from device paths - Replace list_all_hid_devices() fallback in scan_usb_devices() with list_setupdi_devices() - Add usb_interfaces_from_pid() and name_from_pid() public helpers - Update CLI list command to show '<access denied>' for devices found by scan but not accessible via list_devices() - Update select_device() to give clear admin requirement error when only blocked devices are found Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0d49492 to
3485845
Compare
- Add make_credential(), get_assertion(), get_next_assertion() to Ctap2Session - Add Info::get_identifier() and Info::get_cred_store_state() using HKDF+AES decryption - Add static is_supported() methods to all CTAP2 command classes - Add CredentialManagement::is_readonly_supported() static method - Make ClientPin::is_supported() and is_token_supported() static (take &Info) - Use Zeroizing<Vec<u8>> internally for PIN tokens in CTAP2 structs (not in public API) - Zeroize PIN hashes and KDF intermediates after use in ClientPin and PinProtocol - Add build_args_map() helper for positional CBOR argument encoding - Refactor client_pin response parsing to use shared parse_int_map() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename to_value/from_value/from_int_map/from_template_info to to_cbor/from_cbor across all CTAP2 types - Make to_cbor/from_cbor pub(crate) for internal serialization only - Make encode_allow_exclude_list/encode_pub_key_cred_params pub(crate) - Change send_cbor to parse CBOR and return Value instead of raw bytes - Remove parse_int_map from Ctap2Session - Update client_pin() to return Value, callers use map_get_int() - Update credential_management/bio_enrollment call() to return Value - Remove BTreeMap<u32, Value> from all internal CBOR parsing - PyO3 send_cbor re-encodes Value to bytes for Python API Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename cmd module to ctap2_cmd in mod.rs - Rename result_key to bio_result_key in bio_enrollment.rs - Rename result_key to cred_mgmt_result_key in credential_management.rs - Convert ClientPinResult enum to client_pin_result_key constants module - Move build_args_map from session.rs to mod.rs as pub(crate) - Use build_args_map in credential_management, bio_enrollment, and config call() methods Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge yubikit-wheels and ykman-wheels into a single parameterized wheels job using package × platform matrix. Combine source-package build and docs into a single job to eliminate duplicated setup steps (uv, Python, apt deps, Rust, uv sync). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce webauthn/types.rs with WebAuthn-level types: - PublicKeyCredentialCreationOptions, PublicKeyCredentialRequestOptions - Entity types (RpEntity, UserEntity), credential descriptors - Enums for attestation, user verification, resident key requirements - CollectedClientData with JSON serialization and hashing - Response types (RegistrationResponse, AuthenticationResponse) - Conversion methods (to_ctap2) bridging WebAuthn to CTAP2 types Introduce webauthn/client.rs with WebAuthnClient: - Generic over Connection, UserInteraction, and ClientDataCollector traits - UserInteraction trait for prompt_up, request_pin, request_uv callbacks - ClientDataCollector trait for collecting client data per ceremony - make_credential() implementing the registration ceremony - get_assertion() implementing the authentication ceremony - PIN/UV token acquisition with UV-first, PIN-fallback strategy - Session ownership transfer pattern for ClientPin interaction Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Demonstrates the full WebAuthn flow using a FIDO2 security key: - Console-based UserInteraction with PIN prompt on stdin - Simple ClientDataCollector for example.com - Registration ceremony (make_credential) creating an ES256 credential - Authentication ceremony (get_assertion) using the new credential Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Unify PublicKeyCredentialUserEntity, PublicKeyCredentialParameters, and PublicKeyCredentialDescriptor — remove duplicates from ctap2/types.rs and use the webauthn versions everywhere. The webauthn types now carry both CBOR (to_cbor/from_cbor) and JSON (serde) serialization. The ctap2 module re-exports them for backward compatibility. PublicKeyCredentialRpEntity remains separate in both modules because the field requirements differ: CTAP2 requires id, WebAuthn requires name. Remove new() constructors from all WebAuthn structs — use direct struct initialization instead. Add serde/serde_json for WebAuthn JSON serialization only (CBOR keeps the existing manual implementation which suits CTAP2's integer keys and canonical ordering). New public API: - PublicKeyCredentialCreationOptions::from_json() - PublicKeyCredentialRequestOptions::from_json() - RegistrationResponse::to_json() - AuthenticationResponse::to_json() Extensions field changed from HashMap<String, Vec<u8>> to serde_json::Value to support arbitrary JSON extension data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add py_webauthn.rs with WebAuthnClient (FIDO HID) and WebAuthnCcidClient (SmartCard) Python classes - Python callbacks bridge UserInteraction trait (prompt_up, request_pin, request_uv) - Built-in ClientDataCollector uses origin parameter - Options/responses use JSON serialization (from_json/to_json) - Register classes in _yubikit_native.sessions module - Add type stubs for new classes - Add examples/webauthn.py demonstrating registration + authentication Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add yubikit/webauthn.py with WebAuthnClient facade abstracting HID/CCID - Define UserInteraction ABC with prompt_up(), request_pin(permissions, rp_id), request_uv(permissions, rp_id) matching the Rust traits - Define ClientDataCollector ABC with collect_create(options_json) and collect_get(options_json), returning (client_data_json: bytes, rp_id: str) - Add CollectedClientData::from_json() to parse raw JSON bytes from Python - Add to_json() for Options structs (needed to pass options to Python) - Update pyo3 wrappers to accept UserInteraction/ClientDataCollector objects - Update examples to use yubikit facade and ykman device discovery Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tion Implement filter_creds() which uses silent getAssertion calls (UP=false) to pre-filter exclude/allow credential lists before the actual ceremony, matching python-fido2's _filter_creds behavior. Changes: - Refactor get_auth_params into get_token (returns token, protocol, internal_uv) - Add compute_pin_uv_param helper for reuse across ceremonies and filtering - Add filter_creds method that chunks credential lists by max_creds_in_list, handles NoCredentials (skip chunk) and RequestTooLarge (reduce chunk size) - make_credential: request GET_ASSERTION permission when exclude list present, filter exclude list, pass only matching credential - get_assertion: filter allow list, send dummy cred ID if no matches to force UP-then-fail behavior - Rename CtapStatus::PukRequired to RequestTooLarge (correct CTAP2 code 0x39) - Reorder CtapStatus enum variants for sorted-by-value consistency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, credProps, minPinLength) Implement WebAuthn extensions as stateless data types in webauthn/extensions/, with CTAP2 CBOR building/parsing inlined. Extensions are integrated into WebAuthnClient's make_credential and get_assertion ceremonies. Extensions: - PRF: wraps hmac-secret with salt hashing and ECDH key agreement - credProtect: credential protection level (3 policies) - credBlob: small credential-associated data - largeBlob: large blob read/write via LargeBlobs API - credProps: client-side credential properties (rk) - minPinLength: minimum PIN length enforcement Key changes: - Options.extensions typed as RegistrationExtensionInputs/AuthenticationExtensionInputs - Response structs gain client_extension_results field - Auth data extension parsing from raw authenticator data bytes - All extension types have serde JSON serialization - 24 new unit tests for extension CBOR/JSON round-trips Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- webauthn_prf: PRF/hmac-secret with salt evaluation and determinism check - webauthn_cred_protect: all three credential protection levels - webauthn_cred_blob: store and retrieve small credential-associated data - webauthn_large_blob: write and read large blobs via LargeBlobs API - webauthn_cred_props: credProps (rk detection) and minPinLength Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ports of the Rust extension examples, using the JSON-based Python API: - webauthn_prf.py: PRF/hmac-secret with determinism verification - webauthn_cred_protect.py: all three credential protection levels - webauthn_cred_blob.py: store and retrieve small credential data - webauthn_large_blob.py: write and read large blobs - webauthn_cred_props.py: credProps (rk detection) and minPinLength Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the extensions request a largeBlob write, the PIN/UV token must include the LARGE_BLOB_WRITE (0x10) permission bit. Without this the authenticator rejects the write with PIN_AUTH_INVALID. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The process_large_blob method previously required both protocol and large_blob_key to be present. Read operations don't use a PIN token, so when userVerification is 'discouraged' and no extra permissions are needed, protocol would be None and the method would bail out. Fix by making protocol optional — use V2 as a default since reads don't exercise the token path. Also add tests for largeBlob JSON deserialization. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move duplicated boilerplate (ConsoleInteraction, SimpleCollector, constants, device setup, helpers) into shared modules: - crates/yubikit/examples/example_utils.rs (Rust) - examples/example_utils.py (Python) All 12 examples now import from their respective shared module, reducing each by ~70 lines of duplicated code. Set autoexamples=false in Cargo.toml to prevent example_utils.rs from being compiled as a standalone example binary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The public info() accessor only served the examples. Move the Info retrieval into open_client() which now returns (WebAuthnClient, Info). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ctap2Session::new(), ClientPin::new(), CredentialManagement::new(), BioEnrollment::new(), Config::new(), Config::new_unauthenticated(), and LargeBlobs::new() now return the owned session alongside the error on failure, preventing session loss. ClientPin::new_with_protocol() is now infallible and returns Self directly. PyO3 wrappers restore the session back to the Python object on constructor failure, preventing session loss in Python code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.