Skip to content

feat(fido2): WebAuthn previewSign extension (ARKG-P256)#468

Draft
DennisDyallo wants to merge 15 commits intodevelopfrom
feature/webauthn-preview-sign
Draft

feat(fido2): WebAuthn previewSign extension (ARKG-P256)#468
DennisDyallo wants to merge 15 commits intodevelopfrom
feature/webauthn-preview-sign

Conversation

@DennisDyallo
Copy link
Copy Markdown
Collaborator

@DennisDyallo DennisDyallo commented Apr 28, 2026

Summary

Adds the previewSign WebAuthn extension to the v1 SDK (ARKG-P256 key generation, offline derivation, sign-by-credential, offline signature verification) targeting YubiKey 5.8.0-beta firmware. Ported from yubikit-swift release/1.3.0, cross-verified against python-fido2, the Rust cnh-authenticator-rs-extension reference, and the upstream Yubico/5-8-hello-world quickstart.

ARKG-P256 (RFC draft bradleylundberg-cfrg-arkg-09) implemented in Yubico.Core.Cryptography.ArkgPrimitivesOpenSsl via OpenSSL P/Invoke through Yubico.NativeShims. No BouncyCastle dependency.

Stacking: depends on #472. Merge #472 first — this branch consumes its new Native_EC_POINT_is_on_curve export, the Yubico.NativeShims 1.16.1-prerelease.20260428.1 version bump, and the relocated HkdfUtilities.

Verification

Suite Result
Yubico.YubiKey.UnitTests 3588 PASS
Yubico.Core.UnitTests 488 PASS / 21 SKIP (Windows-only on macOS)
ARKG KAT vectors (ArkgP256Tests + ArkgPrimitivesTests) 8 PASS — byte-for-byte vs Rust hid-test reference
Hardware integration: PreviewSignTests (3 tests) ✅ on YubiKey 5.8.0-beta (5C NFC)
DocFX /t:DocFXBuild warnings-as-errors ✅ clean

CI note

submit-nuget will fail until Yubico.NativeShims 1.16.1-prerelease.20260428.1 is reachable from the public NuGet feed (currently published only to Yubico's internal feed). All other CI checks green across Windows/Linux/macOS.

In-branch design docs

Plans/preview-sign-v1-port-plan.md (design), codeaudit-previewsign-port.md (vslsp + crypto audit), python-fido2-reference-findings.md (cross-impl archaeology), previewsign-followups.md (deferred cleanup with suggested PR sequence). Untracked — intentionally kept out of this PR's tree.

@DennisDyallo DennisDyallo force-pushed the feature/webauthn-preview-sign branch 2 times, most recently from 1cf9541 to 9960876 Compare April 28, 2026 11:58
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

Test Results: Windows

    2 files      2 suites   41s ⏱️
4 138 tests 4 116 ✅ 22 💤 0 ❌
4 140 runs  4 118 ✅ 22 💤 0 ❌

Results for commit a3da940.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

Test Results: Ubuntu

    2 files      2 suites   1m 12s ⏱️
4 130 tests 4 108 ✅ 22 💤 0 ❌
4 132 runs  4 110 ✅ 22 💤 0 ❌

Results for commit a3da940.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

Test Results: MacOS

    4 files      4 suites   50s ⏱️
4 112 tests 4 109 ✅ 3 💤 0 ❌
4 114 runs  4 111 ✅ 3 💤 0 ❌

Results for commit a3da940.

♻️ This comment has been updated with latest results.

@DennisDyallo DennisDyallo requested a review from Copilot April 28, 2026 12:09
<PackageReference Include="System.Memory" Version="4.6.3" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageReference Include="Yubico.NativeShims" Version="1.16.0" />
<PackageReference Include="Yubico.NativeShims" Version="1.16.1-prerelease.20260428.1" />
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for the WebAuthn previewSign extension to the FIDO2 surface area of Yubico.NET.SDK, including ARKG-P256 offline public-key derivation and sign-by-credential plumbing, plus associated cryptographic/native support and tests.

Changes:

  • Introduces previewSign extension APIs for MakeCredential (generate key) and GetAssertion (sign by derived credential), plus parsing of signed/unsigned extension outputs.
  • Adds ARKG-P256 primitives (OpenSSL-backed via NativeShims) and wires them through CryptographyProviders.
  • Adds unit/integration tests for previewSign + ARKG, and a test-only cache warmup workaround for a macOS device-listener race.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Yubico.YubiKey/tests/utilities/Yubico/YubiKey/TestUtilities/IntegrationTestDeviceEnumeration.cs Updates allow-list path documentation for macOS/Linux.
Yubico.YubiKey/tests/utilities/Yubico/YubiKey/TestUtilities/DeviceListenerCacheWarmup.cs Adds test utility to wait for initial device enumeration to populate.
Yubico.YubiKey/tests/unit/Yubico/YubiKey/Fido2/PreviewSignExtensionTests.cs Unit tests for previewSign CBOR encoding/decoding and regression scenarios.
Yubico.YubiKey/tests/unit/Yubico/YubiKey/Fido2/Arkg/ArkgP256Tests.cs Known-answer tests for ARKG-P256 derivation behavior.
Yubico.YubiKey/tests/unit/Yubico/YubiKey/Cryptography/HkdfUtilitiesTests.cs Updates HKDF tests to reference the moved HKDF utility.
Yubico.YubiKey/tests/integration/Yubico/YubiKey/Fido2/PreviewSignTests.cs Adds end-to-end integration tests for previewSign generate/derive/sign/verify.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/PreviewSignGeneratedKey.cs New public type representing generated key material and offline derivation entrypoint.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/PreviewSignExtension.cs Implements CBOR encoder/decoder for previewSign extension input/output.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/PreviewSignDerivedKey.cs New public type representing derived key material and signature verification helper.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/MakeCredentialParameters.cs Adds API to request previewSign key generation during registration.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/MakeCredentialData.cs Adds parsing of CTAP unsigned extension outputs and accessor for generated key.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/GetAssertionParameters.cs Adds API to request previewSign signing-by-credential during assertions.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/Extensions.cs Adds previewSign extension identifier constant.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/Cose/CoseAlgorithmIdentifier.cs Adds previewSign-related COSE algorithm identifiers (Esp256, ArkgP256Esp256).
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/AuthenticatorInfo.cs Updates imports to use the relocated HKDF utility.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/AuthenticatorData.cs Adds accessor to extract previewSign signature from signed extension outputs.
Yubico.YubiKey/src/Yubico/YubiKey/Fido2/Arkg/ArkgP256.cs Adds ARKG-P256 wrapper to route derivation through Core primitives.
Yubico.YubiKey/src/Yubico/YubiKey/Cryptography/CryptographyProviders.cs Adds ArkgPrimitivesCreator delegate to allow swapping ARKG primitive implementation.
Yubico.NativeShims/ssl.ecpoint.c Exposes EC_POINT_is_on_curve via NativeShims for on-curve validation.
Yubico.NativeShims/exports.msvc Exports new Native_EC_POINT_is_on_curve symbol on Windows (MSVC).
Yubico.NativeShims/exports.llvm Exports new symbol for LLVM toolchains.
Yubico.NativeShims/exports.gnu Exports new symbol for GNU toolchains.
Yubico.Core/tests/unit/Yubico/Core/Cryptography/ArkgPrimitivesTests.cs Adds Core-level tests for on-curve validation and ECDH behavior.
Yubico.Core/src/Yubico/PlatformInterop/Desktop/Cryptography/EcPoint.Interop.cs Adds P/Invoke wrapper for Native_EC_POINT_is_on_curve.
Yubico.Core/src/Yubico/Core/Cryptography/IArkgPrimitives.cs Introduces interface contract for ARKG primitives (validation, ECDH, derive).
Yubico.Core/src/Yubico/Core/Cryptography/HkdfUtilities.cs Moves/renames HKDF utility into Core and updates implementation details.
Yubico.Core/src/Yubico/Core/Cryptography/ArkgPrimitivesOpenSsl.cs Implements ARKG-P256 primitives and derivation using OpenSSL via NativeShims.
Yubico.Core/src/Yubico/Core/Cryptography/ArkgPrimitives.cs Adds factory for default ARKG primitives implementation.
Yubico.Core/src/Yubico.Core.csproj Updates NativeShims package dependency version for the new export.
Comments suppressed due to low confidence (2)

Yubico.Core/src/Yubico/Core/Cryptography/HkdfUtilities.cs:74

  • HkdfExtract creates temporary byte[] copies of both the salt and the input keying material via ToArray(). Since IKM can be secret material, these temporary arrays should be zeroed after ComputeHash to avoid extending the lifetime of sensitive data in memory.
    Yubico.Core/src/Yubico/Core/Cryptography/HkdfUtilities.cs:87
  • HkdfExpand currently calls pseudoRandomKey.ToArray() to build the HMAC instance, which creates an extra copy of the PRK. Since DeriveKey already zeroes the original PRK, this additional copy will remain in memory unless explicitly cleared. Consider avoiding the extra copy (e.g., accept byte[] and pass it through) and/or ensuring any extra key buffer is zeroed when no longer needed.

Comment thread Yubico.YubiKey/src/Yubico/YubiKey/Fido2/GetAssertionParameters.cs
Comment thread Yubico.YubiKey/src/Yubico/YubiKey/Fido2/PreviewSignGeneratedKey.cs
@DennisDyallo DennisDyallo force-pushed the feature/webauthn-preview-sign branch 8 times, most recently from 3a99633 to 9c68ea2 Compare April 29, 2026 14:07
@DennisDyallo DennisDyallo changed the title Feature/webauthn preview sign feat(fido2): WebAuthn previewSign extension (ARKG-P256) Apr 29, 2026
@DennisDyallo DennisDyallo force-pushed the feature/webauthn-preview-sign branch 3 times, most recently from 1d36eaf to dd829ec Compare April 29, 2026 15:00
@DennisDyallo DennisDyallo changed the base branch from develop to feature/nativeshims-tests April 29, 2026 15:02
DennisDyallo and others added 7 commits April 29, 2026 17:03
Phase 1 of the previewSign port establishes the test-first behavioral
spec. All public API stubs throw NotImplementedException; tests compile
and fail at runtime. Subsequent phases turn red tests green:
  Phase 2 — NativeShims native additions
  Phase 3 — Managed P/Invoke + ArkgPrimitivesOpenSsl
  Phase 4 — ARKG-P256 algorithm port (from Yubico Rust reference)
  Phase 5 — Fido2 extension wiring (CBOR encoders, parsers)
  Phase 6 — Doc pass + final green-test verification

Includes two required regression tests:
  - ParseGenerateKeyFromUnsignedExtensions_KeyAt6 — locks CTAP key 6
    (matches yubikit-swift release/1.3.0, Java, Python; v2 .NET uses 8
    and is the outlier — separate bug report queued)
  - AddPreviewSignByCredential_ThrowsWhenExtensionUnsupported — guards
    against a missing-validation bug found in the upstream PoC

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the OpenSSL-backed ARKG primitives needed by the previewSign WebAuthn
extension: SEC1 on-curve validation via the new EcPointIsOnCurve P/Invoke
wrapper, and ECDH shared-secret computation that rejects invalid-curve
inputs before any scalar multiplication. Wires a CryptographyProviders
factory delegate following the EcdhPrimitivesCreator pattern.

IArkgPrimitives is exposed publicly to mirror the existing IEcdhPrimitives
contract; making it internal would require a Yubico.YubiKey
InternalsVisibleTo entry that collides with the Nullable polyfill.

Phase 3 of the previewSign port. Phase 4 (ArkgP256 algorithm) and Phase 5
(extension wiring) tests remain RED with NotImplementedException.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements draft-bradleylundberg-cfrg-arkg-09 derivation entirely in
managed code, with point arithmetic via Yubico.NativeShims and scalar
reduction via System.Numerics.BigInteger. The full algorithm body lives
in ArkgPrimitivesOpenSsl.Derive (Yubico.Core) so it can use the internal
OpenSSL P/Invoke surface that is not visible from Yubico.YubiKey;
ArkgP256.cs (Yubico.YubiKey) becomes a thin wrapper that routes through
CryptographyProviders.ArkgPrimitivesCreator.

Adds three KAT vectors generated from cnh-authenticator-rs-extension's
arkg.rs reference. Vector A reproduces the embedded Rust unit-test
expected output byte-for-byte; vectors B/C exercise distinct-IKM and
distinct-ctx paths with bit-exact expected derived public keys.

Phase 4 of the previewSign port. Phase 5 (extension wiring) tests
remain RED with NotImplementedException.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces all Phase 1 stubs with real implementations:
- New PreviewSignExtension (internal) — CBOR encoder/decoder using
  per-context enums (MakeCredentialKey {3,4,7}, GetAssertionKey {2,6,7})
  to keep the deliberately reused integers readable.
- AddPreviewSignGenerateKeyExtension and AddPreviewSignByCredentialExtension
  both call IsExtensionSupported (the second was Alan's bug — guarded by a
  named regression test).
- MakeCredentialData parses UnsignedExtensionOutputs at CTAP response
  key 6 (NOT 8 — verified against yubikit-swift release/1.3.0; explicit
  regression test asserts the value).
- AuthenticatorData.GetPreviewSignSignature pulls the DER-encoded ECDSA
  signature from authData.extensions["previewSign"] map key 6.
- PreviewSignGeneratedKey.DerivePublicKey routes to ArkgP256.
- PreviewSignDerivedKey.VerifySignature uses the existing EcdsaVerify
  helper for portability across netstandard2.0/2.1/net472.

Flags rule: requireUv -> 0b101 (RequireUserVerification), else 0b001
(RequireUserPresence). Derived from the AddExtension call argument, never
a user knob.

Phase 5 of the previewSign port. All 9 PreviewSignExtensionTests pass,
including ParseGenerateKeyFromUnsignedExtensions_KeyAt6 and
AddPreviewSignByCredential_ThrowsWhenExtensionUnsupported.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Added comprehensive XML documentation for all public APIs in the
previewSign WebAuthn extension implementation:

- Enhanced CoseAlgorithmIdentifier.Esp256/ArkgP256Esp256 docs to
  clarify usage distinction (Esp256 for signing, ArkgP256Esp256
  for key generation requests)
- Expanded IArkgPrimitives.Derive with algorithm details and
  cross-references to usage contexts
- Added class and method remarks to PreviewSignGeneratedKey and
  PreviewSignDerivedKey with workflow guidance
- Enhanced MakeCredentialParameters.AddPreviewSignGenerateKeyExtension
  and GetAssertionParameters.AddPreviewSignByCredentialExtension
  with usage flows and cross-references
- Documented ArkgPrimitives.Create factory method

All docs follow existing SDK patterns from CredBlob extension.
DocFX build clean (0 warnings, 0 errors).
Test suites green (Core: 488 PASS, YubiKey: 3587 PASS).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the 3 Phase 1 NotImplementedException stubs in
PreviewSignTests.cs with real test bodies exercising the public
previewSign API surface landed in Phases 3-6:

- MakeCredentialWithPreviewSign_ReturnsGeneratedKey: registers a
  credential with previewSign and validates the returned
  PreviewSignGeneratedKey shape (KeyHandle non-empty,
  BlindingPublicKey + KemPublicKey are 65-byte SEC1 uncompressed
  P-256 points starting with 0x04).

- FullCeremony_RegisterDeriveSignVerify_RoundTrip: full Step A->B->C->D
  ceremony — register with previewSign, derive public key offline with
  deterministic ikm/ctx, sign a fixed message with the derived
  credential, verify the signature offline.

- MakeCredentialWithUnsupportedAlgorithm_Fails: registers with ES256
  instead of ArkgP256Esp256, asserts hardware rejects it. Pivoted from
  the original GetAssertion-with-unsupported-alg framing because the
  production AddPreviewSignGenerateKeyExtension does no client-side
  algorithm filtering, so device rejection is the meaningful test.

Tests use [SkippableFact(typeof(DeviceNotFoundException))] +
Skip.IfNot(IsExtensionSupported(Extensions.PreviewSign)) so they skip
cleanly without a YubiKey 5.8.0-beta+ present. Helpers
(GetMakeCredentialParams / GetGetAssertionParams / GetPinToken) follow
the HmacSecretTests integration pattern.

Reviewed via /DevTeam Ship — Engineer wrote, Reviewer approved
CLEAN PASS on logic correctness without execution. Tests can only
be run by a human with physical YK + user touch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…scovery

The previous implementation inherited SimpleIntegrationTestConnection,
which calls IntegrationTestDeviceEnumeration.GetTestDevice() EAGERLY
in its constructor. On macOS the YubiKeyDeviceListener populates its
device cache asynchronously, so FindByTransport(All) returned empty
before the cache was populated, throwing DeviceNotFoundException and
silently skipping every test even when a supported YubiKey was
plugged in and present in the allow-list.

Refactor to inherit FidoSessionIntegrationTestBase (the same base
class used by MakeCredentialBlobTests, HmacSecretTests, MinPinLenTests).
That base exposes Device / Session / Connection as LAZY properties
accessed only when the test method runs, by which time the device
listener has populated its cache.

Side benefits of the rebase:
- No private constructor needed; base handles PIN setup via TestPin1
- Removes 3 helper methods; base provides MakeCredentialParameters /
  GetAssertionParameters templates and a TestKeyCollector
- Higher-level Session.MakeCredential / Session.GetAssertions API
  instead of Connection.SendCommand(...)
- File moved from Fido2/Commands/ to Fido2/ to match the location
  convention of other FidoSessionIntegrationTestBase-using tests

Hardware behavior unchanged. Skip semantics preserved: still uses
[SkippableFact(typeof(DeviceNotFoundException))] +
Skip.IfNot(Session.AuthenticatorInfo.IsExtensionSupported(...))
on every test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DennisDyallo and others added 8 commits April 29, 2026 17:03
Companion to 312991d (which captured only the file rename).
This commit carries the actual refactor: change of base class to
FidoSessionIntegrationTestBase, removal of the eager constructor and
helper methods, switch to Session-level Fido2Session API for the
3 test bodies. See 312991d commit message for full rationale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FidoSessionIntegrationTestBase's constructor calls GetSession() to
clean up stale credentials, which eagerly resolves Device through
IntegrationTestDeviceEnumeration.GetTestDevice(). On macOS the
YubiKeyDeviceListener populates its device cache asynchronously, so
the Device lookup races the listener and throws
DeviceNotFoundException ("Could not find any connected Yubikeys")
even when a supported YubiKey is plugged in and present in the
allow-list.

Working FIDO2 tests (MakeCredentialBlobTests, etc.) only avoid this
by happenstance: an earlier test class in the same xunit run calls
YubiKeyDevice.FindAll() (which triggers a synchronous poll) and
warms the cache for subsequent FindByTransport(All) lookups. When
PreviewSignTests is the first class to instantiate, the warm-up
never happens.

Add a static constructor to PreviewSignTests that calls
YubiKeyDevice.FindAll().ToList() once per AppDomain, before any
instance constructor runs. The synchronous enumeration populates
the cache that FindByTransport(All) reads, eliminating the race.

Self-contained fix — does not modify shared TestUtilities, does not
affect other test classes. The deeper cache-discrepancy bug between
FindAll and FindByTransport on macOS is worth filing separately
against the SDK team.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two issues surfaced during the first hardware run on macOS with
YubiKey 103 (firmware 5.8.0.beta.0):

1. Race condition: FidoSessionIntegrationTestBase's constructor
   eagerly resolves Device through IntegrationTestDeviceEnumeration
   .GetTestDevice -> YubiKeyDevice.FindByTransport(All), which reads
   the YubiKeyDeviceListener's _internalCache. On macOS that cache
   is populated asynchronously via background USB notifications, so
   the first test class to instantiate races the listener and gets
   an empty result -> DeviceNotFoundException -> tests skip with
   "Could not find any connected Yubikeys (Transport: All)" even
   when the device is plugged in and present in the allow-list.

   Fix: poll YubiKeyDevice.FindAll() in a static constructor (runs
   once per AppDomain before any instance constructor) until at
   least one device appears or 5 seconds elapse. The loop is
   harmless when the cache is already populated and only adds wall
   time when the device is missing (in which case the test would
   skip anyway).

2. Wrong exception type: Fido2Session.MakeCredential wraps the
   underlying CTAP error code in a Fido2Exception, not a
   Ctap2DataException as the test originally expected. The test
   path (ES256 rejected by 5.8.0-beta firmware) does fire the
   exception correctly; only the assertion type was wrong.

   Fix: assert Fido2Exception in MakeCredentialWithUnsupportedAlgorithm
   _Fails.

Verified on hardware: MakeCredentialWithUnsupportedAlgorithm_Fails
passes (779ms, no touch required since the YK rejects ES256 before
requesting user presence).

Note: the IntegrationTestDeviceEnumeration source docstring claims
the allow-list file lives at ~/.local/share/Yubico/...txt on macOS,
but Environment.SpecialFolder.LocalApplicationData on .NET 8 macOS
actually resolves to ~/Library/Application Support/. The docstring
should be updated upstream.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two stacked bugs were preventing FullCeremony_RegisterDeriveSignVerify_RoundTrip
from passing against YK 103 firmware 5.8.0-beta. Both fixed:

1) GetAssertionParameters.EncodeArkgSignArgs wrote -9 (Esp256) at key 3 of
   the COSE_Sign_Args map. Firmware rejects everything but ArkgP256Esp256
   (-65539) at protocol-decode time. Verified across four independent
   reference implementations:
   - cnh-authenticator-rs-extension/native/crates/hid-test/src/main.rs:28
   - cnh-authenticator-rs-extension/scripts/arkg_p256.py:339
   - cnh-authenticator-rs-extension/test-page/index.html:555
   - python-fido2/fido2/cose.py:391+459

2) PreviewSignTests omitted GetAssertionParameters.AllowCredential() before
   AddPreviewSignByCredentialExtension(). python-fido2's reference
   implementation (extensions.py:788-789) explicitly enforces this
   precondition; firmware enforces the same rule and rejects with
   "option or extension is unsupported or invalid" when the allow-list
   is missing. Test now mirrors the upstream demo's call sequence.

CoseAlgorithmIdentifier XML docs rewritten so future readers cannot
misapply Esp256 in sign-args contexts (the misleading guidance in the
original docs is what produced bug #1).

Verification on YK 103, FW 5.8.0.beta.0, macOS arm64:
  PreviewSignTests integration suite: 3/3 PASS
    - MakeCredentialWithUnsupportedAlgorithm_Fails
    - MakeCredentialWithPreviewSign_ReturnsGeneratedKey
    - FullCeremony_RegisterDeriveSignVerify_RoundTrip
  Yubico.YubiKey.UnitTests: 3587/3587 PASS (baseline match)
  Yubico.Core.UnitTests:    488/488 PASS, 21 SKIP (baseline match)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ceEnumeration

Environment.SpecialFolder.LocalApplicationData on .NET 8 macOS resolves to
~/Library/Application Support, not the XDG-style ~/.local/share documented
previously. Add a Linux row for completeness and cite SpecialFolder so
future readers can verify the resolution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…elper

The PreviewSignTests static ctor's polling block defeats a YubiKeyDeviceListener
startup race specific to macOS (the IOKit run loop arms on a background thread
and Update() can return before the first arrival callback fires). The same
race will affect any other Fido2/Hid integration test class whose base
constructor enumerates devices, so promote the workaround into
DeviceListenerCacheWarmup.WaitForFirstDevice() in TestUtilities.

PreviewSignTests now calls the helper from its static ctor; behavior is
unchanged. Other test classes can adopt the helper as needed.

Underlying SDK race remains unfixed and tracked separately — a real fix
needs to drive synchronous initial enumeration through MacOSHidDeviceListener
or have YubiKeyDeviceListener.Update() wait for the first callback pass,
both of which are larger than this PR's scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three audit fixes plus a regression test for the new precondition guard.
All four are concrete, well-scoped changes from Plans/codeaudit-previewsign-port.md.

1. Crypto leak (HIGH): ArkgPrimitivesOpenSsl.BlBlindPublicKey now wraps its
   body in try/finally and zeroes the blinding scalar tauBytes via
   CryptographicOperations.ZeroMemory. Brings tauBytes in line with the
   other secret-bearing arrays in the same file (ephSkBytes, kPrime, mk,
   ikmTau, prk, full) which were already zeroed.

2. API footgun (HIGH): GetAssertionParameters.AddPreviewSignByCredentialExtension
   now throws InvalidOperationException with an actionable message when the
   parent's allow-list is null/empty. Without this guard, customers got a
   cryptic firmware "option or extension invalid" error from the YubiKey.
   The required precondition is enforced at the protocol level by the
   firmware (and documented in python-fido2's reference impl); the SDK
   should fail fast and explain how to fix the call site.

3. Documentation lies (LOW): MakeCredentialData.GetPreviewSignGeneratedKey
   and AuthenticatorData.GetPreviewSignSignature each had stale
   <exception cref="NotImplementedException"> tags inherited from a draft
   that were no longer accurate. Deleted.

4. Regression test: PreviewSignExtensionTests now asserts
   AddPreviewSignByCredentialExtension throws InvalidOperationException with
   "AllowCredential" in the message when the allow-list is empty. New helper
   BuildAuthenticatorInfoWithPreviewSign supports the test without hardware.

Verification:
  Yubico.YubiKey.UnitTests:  3588 PASS (3587 baseline + 1 new test)
  Yubico.Core.UnitTests:     488 PASS / 21 SKIP (baseline match)
  Build:                     0 warnings, 0 errors
  3 ARKG NativeShims tests still fail with EntryPointNotFoundException
  for Native_EC_POINT_is_on_curve — expected interim state until the
  Yubico.NativeShims 1.16.1-prerelease.20260428.1 NuGet package lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the private HkdfSha256 method from ArkgPrimitivesOpenSsl that
existed solely as a workaround because HkdfUtilities lived in the
wrong assembly (Yubico.YubiKey, unreachable from Core). Now that
HkdfUtilities is in Yubico.Core, ARKG can call the shared canonical
implementation.

44 lines of duplicated cryptographic code removed. ARKG KAT vectors
verify byte-for-byte equivalence with the previous local copy. The
.ToArray() calls at lines 271 + 291 are forced by downstream code
expecting byte[] (mk fed to HMACSHA256 ctor, shared returned as byte[]).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@DennisDyallo DennisDyallo force-pushed the feature/webauthn-preview-sign branch from dd829ec to bc25944 Compare April 29, 2026 15:03
@github-actions
Copy link
Copy Markdown
Contributor

Code Coverage

Package Line Rate Branch Rate Complexity Health
Yubico.Core 56% 49% 1658
Yubico.YubiKey 50% 46% 7286
Summary 51% (13430 / 26236) 46% (3273 / 7082) 8944

Minimum allowed line rate is 40%

Base automatically changed from feature/nativeshims-tests to develop April 29, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants