This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
securitycritical PriorityID: webauthn-prf-deterministic-salt-derivation

WebAuthn PRF Deterministic Client-Side Salt Derivation and Key Wrapping Standard

Enforces cryptographic invariants when leveraging WebAuthn PRF (Pseudo-Random Function) extension for zero-knowledge client-side encryption key derivation directly from authenticators. Prevents relying party leakage, unkeyed salt reuse, insecure PRF output consumption, and lack of domain separation in HKDF expansions.

Prohibited Anti-Pattern: Exposing Raw PRF Outputs to Servers, Using Static Global Salts Without Domain Separation, or Bypassing HKDF Expansion

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: WebAuthn PRF Deterministic Client-Side Salt Derivation and Key Wrapping Standard - Enforces cryptographic invariants when leveraging WebAuthn PRF (Pseudo-Random Function) extension for zero-knowledge client-side encryption key derivation directly from authenticators. Prevents relying party leakage, unkeyed salt reuse, insecure PRF output consumption, and lack of domain separation in HKDF expansions.
globs: src/**/auth/**/*.{ts,tsx}, src/**/crypto/**/*.{ts,tsx}, src/**/webauthn/**/*.{ts,tsx}, src/**/vault/**/*.{ts,tsx}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/webauthn-prf-deterministic-salt-derivation"
---

# WebAuthn PRF Deterministic Client-Side Salt Derivation and Key Wrapping Standard

> **Severity**: CRITICAL | **Category**: security
> **Anti-Pattern Warning**: Exposing Raw PRF Outputs to Servers, Using Static Global Salts Without Domain Separation, or Bypassing HKDF Expansion

## System Constraints
You are a Principal Cryptographic Architect enforcing absolute security invariants around WebAuthn PRF (hmac-secret / PRF extension) key derivation. Zero-knowledge client-side encryption demands that authenticators derive symmetric keys locally without raw PRF outputs or derived vault keys ever transmitting to or persisting on Relying Party (RP) servers. Mandate strict HKDF expansion with domain-separated info parameters, authenticated symmetric encryption (AES-256-GCM), deterministic per-credential salt storage, and strict verification that the authenticator honored the PRF extension.

## Strict Guidelines
- Engineers MUST ALWAYS treat WebAuthn PRF results (`first` and `second` outputs) as ephemeral high-entropy seed material, feeding them directly into HKDF-Extract and HKDF-Expand (RFC 5869) with cryptographically explicit domain-separation context strings (`info`).
- Engineers MUST NOT transmit the raw PRF output or any intermediate derived keys (`IKM`, `PRK`, or unwrapped Master Key) to the server under any circumstances.
- Engineers MUST ensure PRF input salts are at least 32 bytes of cryptographically secure random bytes (e.g., via `crypto.getRandomValues`) generated during registration and stored openly per-credential, or deterministically derived using an RP-level Master Salt combined with Credential ID via HMAC-SHA-256.
- Engineers MUST ALWAYS verify that `assertion.getClientExtensionResults().prf?.results` exists and contains valid 32-byte ArrayBuffers before attempting downstream decryption, failing closed if the hardware token did not execute the PRF extension.
- Engineers MUST use derived keys solely as Key Encryption Keys (KEK) to unwrap a randomly generated Data Encryption Key (DEK), ensuring key rotation without re-encrypting the entire vault.
- Components NEVER store derived master keys or unwrapped DEKs in persistent browser storage (`localStorage`, `sessionStorage`, or unencrypted `IndexedDB`); keys must exist purely in transient non-exportable memory scopes (e.g., `CryptoKey` non-extractable instances).

## Bad vs Good Implementations
### Insecure Direct Consumption and Server Transmission of PRF Output vs Zero-Knowledge HKDF-Derived KEK

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The bad example uses a static 15-byte string instead of a 32-byte salt, directly leaks the hardware-derived PRF output to the backend server destroying zero-knowledge properties, and attempts to import the raw PRF seed as an AES-GCM key without HKDF expansion or domain separation.
```typescript
export async function decryptVaultInsecure(
  credentialId: string,
  encryptedVault: ArrayBuffer
): Promise<ArrayBuffer> {
  // Flaw 1: Static hardcoded 16-byte salt across all credentials
  const staticSalt = new TextEncoder().encode("static-app-salt");

  const credential = await navigator.credentials.get({
    publicKey: {
      challenge: new Uint8Array(32),
      allowCredentials: [{ id: base64ToBuffer(credentialId), type: "public-key" }],
      extensions: {
        prf: {
          eval: { first: staticSalt }
        }
      }
    }
  }) as PublicKeyCredential;

  const extResults = credential.getClientExtensionResults() as any;
  const rawPrf = extResults.prf.results.first;

  // Flaw 2: Leaking raw PRF output to RP server for sync/logging
  await fetch("/api/auth/verify-prf", {
    method: "POST",
    body: JSON.stringify({ prf: bufferToBase64(rawPrf) })
  });

  // Flaw 3: Direct use of PRF output as AES key without HKDF domain separation
  const key = await crypto.subtle.importKey(
    "raw",
    rawPrf,
    { name: "AES-GCM" },
    true,
    ["decrypt"]
  );

  return crypto.subtle.decrypt(
    { name: "AES-GCM", iv: new Uint8Array(12) },
    key,
    encryptedVault
  );
}
```

#### Production Standard (Correct)
> **Standard**: Validates the PRF extension response from the authenticator, enforces 32-byte salt parameters, imports the PRF output strictly into a non-extractable HKDF primitive, enforces cryptographic domain separation using an explicit context info string, and returns an un-exportable AES-256-GCM Key Encryption Key (KEK) without ever transmitting secrets over the wire.
```typescript
export interface PrfKeyDerivationParams {
  credentialId: Uint8Array;
  storedSalt: Uint8Array; // 32-byte securely generated per-credential salt
  challenge: Uint8Array;
}

export async function deriveVaultKekWithPrf(
  params: PrfKeyDerivationParams
): Promise<CryptoKey> {
  if (params.storedSalt.byteLength < 32) {
    throw new Error("SecurityViolation: Salt must be at least 32 cryptographically secure bytes.");
  }

  const credential = await navigator.credentials.get({
    publicKey: {
      challenge: params.challenge,
      allowCredentials: [{
        id: params.credentialId,
        type: "public-key",
        transports: ["internal", "hybrid", "usb"]
      }],
      userVerification: "required",
      extensions: {
        prf: {
          eval: {
            first: params.storedSalt
          }
        }
      }
    }
  }) as PublicKeyCredential | null;

  if (!credential) {
    throw new Error("AuthenticationFailed: No credential returned.");
  }

  const extensionResults = credential.getClientExtensionResults();
  const prfResults = extensionResults.prf?.results;

  if (!prfResults || !prfResults.first) {
    throw new Error("ExtensionError: Authenticator does not support or refused PRF extension.");
  }

  // Import ephemeral PRF output as raw Key Material for HKDF only (non-extractable)
  const prfKeyMaterial = await crypto.subtle.importKey(
    "raw",
    prfResults.first,
    "HKDF",
    false,
    ["deriveKey"]
  );

  // RFC 5869 HKDF Expansion with rigid domain-separated info context
  const info = new TextEncoder().encode("webauthn-prf:vault-kek:v1:aes-256-gcm");
  const hkdfSalt = new Uint8Array(32); // Standard zero-filled salt for HKDF-Expand over PRF output

  return crypto.subtle.deriveKey(
    {
      name: "HKDF",
      hash: "SHA-256",
      salt: hkdfSalt,
      info: info
    },
    prfKeyMaterial,
    { name: "AES-GCM", length: 256 },
    false, // Non-extractable to prevent key exfiltration
    ["unwrapKey"]
  );
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: WebAuthn PRF Deterministic Client-Side Salt Derivation and Key Wrapping Standard

WebAuthn PRF Deterministic Client-Side Salt Derivation and Key Wrapping Standard defines an industrial-grade behavioral standard for security engineering. Enforces cryptographic invariants when leveraging WebAuthn PRF (Pseudo-Random Function) extension for zero-knowledge client-side encryption key derivation directly from authenticators. Prevents relying party leakage, unkeyed salt reuse, insecure PRF output consumption, and lack of domain separation in HKDF expansions. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Exposing Raw PRF Outputs to Servers, Using Static Global Salts Without Domain Separation, or Bypassing HKDF Expansion Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftExposing Raw PRF Outputs to Servers, Using Static Global Salts Without Domain Separation, or Bypassing HKDF ExpansionCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS treat WebAuthn PRF results (`first` and `second` outputs) as ephemeral high-entropy seed material, feeding them directly into HKDF-Extract and HKDF-Expand (RFC 5869) with cryptographically explicit domain-separation context strings (`info`).

Engineers MUST ALWAYS treat WebAuthn PRF results (`first` and `second` outputs) as ephemeral high-entropy seed material, feeding them directly into HKDF-Extract and HKDF-Expand (RFC 5869) with cryptographically explicit domain-separation context strings (`info`).

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NOT transmit the raw PRF output or any intermediate derived keys (`IKM`, `PRK`, or unwrapped Master Key) to the server under any circumstances.

Engineers MUST NOT transmit the raw PRF output or any intermediate derived keys (`IKM`, `PRK`, or unwrapped Master Key) to the server under any circumstances.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST ensure PRF input salts are at least 32 bytes of cryptographically secure random bytes (e.g., via `crypto.getRandomValues`) generated during registration and stored openly per-credential, or deterministically derived using an RP-level Master Salt combined with Credential ID via HMAC-SHA-256.

Engineers MUST ensure PRF input salts are at least 32 bytes of cryptographically secure random bytes (e.g., via `crypto.getRandomValues`) generated during registration and stored openly per-credential, or deterministically derived using an RP-level Master Salt combined with Credential ID via HMAC-SHA-256.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Engineers MUST ALWAYS verify that `assertion.getClientExtensionResults().prf?.results` exists and contains valid 32-byte ArrayBuffers before attempting downstream decryption, failing closed if the hardware token did not execute the PRF extension.

Engineers MUST ALWAYS verify that `assertion.getClientExtensionResults().prf?.results` exists and contains valid 32-byte ArrayBuffers before attempting downstream decryption, failing closed if the hardware token did not execute the PRF extension.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Engineers MUST use derived keys solely as Key Encryption Keys (KEK) to unwrap a randomly generated Data Encryption Key (DEK), ensuring key rotation without re-encrypting the entire vault.

Engineers MUST use derived keys solely as Key Encryption Keys (KEK) to unwrap a randomly generated Data Encryption Key (DEK), ensuring key rotation without re-encrypting the entire vault.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Components NEVER store derived master keys or unwrapped DEKs in persistent browser storage (`localStorage`, `sessionStorage`, or unencrypted `IndexedDB`)

Components NEVER store derived master keys or unwrapped DEKs in persistent browser storage (`localStorage`, `sessionStorage`, or unencrypted `IndexedDB`); keys must exist purely in transient non-exportable memory scopes (e.g., `CryptoKey` non-extractable instances).

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is WebAuthn PRF Deterministic Client-Side Salt Derivation and Key Wrapping Standard critical for AI coding assistants?

Enforces cryptographic invariants when leveraging WebAuthn PRF (Pseudo-Random Function) extension for zero-knowledge client-side encryption key derivation directly from authenticators. Prevents relying party leakage, unkeyed salt reuse, insecure PRF output consumption, and lack of domain separation in HKDF expansions. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Exposing Raw PRF Outputs to Servers, Using Static Global Salts Without Domain Separation, or Bypassing HKDF Expansion) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

WebAuthn