# non-extractable-key _(no submodules)_ ## Purpose Provides `nonExtractableKey()`, a helper that generates a `CryptoKey` with `extractable: false`. Key material generated this way never leaves the cryptographic engine — it cannot be read by JavaScript code via `crypto.subtle.exportKey`. The key handle can be used for cryptographic operations but its raw bytes are permanently inaccessible, providing a hardware-security-module-like boundary in software. This is the correct approach whenever keys must remain hardware-bound or compliance requirements prohibit key export. Examples include session-scoped encryption keys, FIDO2-style signing keys, and keys derived from hardware-backed key material in PWA contexts. The non-extractability also means that if an adversary gains script execution access, they cannot exfiltrate the key bytes — they can only use the key handle for operations it was created for. ## Features - Generates a `CryptoKey` with `extractable: false` for any `crypto.subtle`-supported algorithm - Supports symmetric algorithms: AES-GCM, AES-CBC, AES-KW, HMAC - Supports asymmetric algorithms: ECDSA (P-256, P-384), ECDH (P-256, P-384), Ed25519, X25519 - For asymmetric algorithms, returns only the private key (the non-extractable half) - `assertWebCryptoAvailable()` guard before any `crypto.subtle` call - Typed `BrowserCryptoError` on generation failure ## Usage: Basic Example ```ts import { nonExtractableKey } from "@suaveplan/crypto-browser/non-extractable-key/non-extractable-key"; // Generate a non-extractable AES-256-GCM key for session encryption const sessionKey = await nonExtractableKey( { name: "AES-GCM", length: 256 }, ["encrypt", "decrypt"] ); // sessionKey.extractable === false // crypto.subtle.exportKey("raw", sessionKey) will throw const nonce = crypto.getRandomValues(new Uint8Array(12)); const ciphertext = await crypto.subtle.encrypt( { name: "AES-GCM", iv: nonce }, sessionKey, new TextEncoder().encode("private data") ); ``` ## Usage: Advanced Example ```ts import { nonExtractableKey } from "@suaveplan/crypto-browser/non-extractable-key/non-extractable-key"; // Hardware-bound ECDSA signing key — private key material never leaves the engine async function createBoundSigningKey(): Promise { return nonExtractableKey( { name: "ECDSA", namedCurve: "P-256" }, ["sign"] ); } // Use the key for signing without any risk of key extraction async function signChallenge( key: CryptoKey, challenge: Uint8Array ): Promise { const sig = await crypto.subtle.sign( { name: "ECDSA", hash: "SHA-256" }, key, challenge ); return new Uint8Array(sig); } // Store the key in IndexedDB for persistence (CryptoKey is structured-cloneable) // async function storeKey(db: IDBDatabase, key: CryptoKey): Promise { ... } ``` ## API Reference ### `nonExtractableKey(algorithm, usages): Promise` Generates a non-extractable `CryptoKey`. | Parameter | Type | Description | |-----------|------|-------------| | `algorithm` | `AlgorithmIdentifier \| RsaHashedKeyGenParams \| EcKeyGenParams \| HmacKeyGenParams \| AesKeyGenParams` | Key generation algorithm descriptor | | `usages` | `KeyUsage[]` | Intended key operations, e.g. `["encrypt", "decrypt"]` | **Returns:** `Promise` with `extractable === false`. - For symmetric algorithms: returns the `CryptoKey` directly. - For asymmetric algorithms: returns the `privateKey` from the generated `CryptoKeyPair`. **Throws:** `BrowserCryptoError` — `BROWSER_CRYPTO.KEY.GENERATION_FAILED` or `BROWSER_CRYPTO.NOT_AVAILABLE`. ## Implementation Notes The implementation calls `crypto.subtle.generateKey` with `extractable: false`. The Web Crypto API guarantees that once a key is created non-extractable, no subsequent API call can extract the raw bytes — `exportKey` will throw `InvalidAccessError` unconditionally. For asymmetric algorithms, `generateKey` returns a `CryptoKeyPair`. The function detects this via `"privateKey" in result` and returns only the private key. The public key is discarded; if callers need the public key they should use the specific engine modules (`ecdsaP384.generateKeypair()`, etc.) which export both. Non-extractable keys are structured-cloneable and can be persisted to `IndexedDB`, shared with `BroadcastChannel`, or transferred to `Worker` contexts via `postMessage` — without ever exposing the raw key bytes. This makes them suitable for long-lived application keys that survive page reloads.