# handle/registry ## Purpose Process-wide registry for RSA key-handle providers. Every HSM, KMS, OS keychain, or PKCS#11 bridge registers a provider once at startup; the registry resolves opaque `RsaKeyHandle` values to the matching provider at call time and dispatches `resolve` / `sign` / `decrypt` operations through it. Decouples consumer code from any specific hardware backend. ## Features - Single in-process registry keyed by provider name - Idempotent re-registration of the same provider value - Conflicting registration of a different provider under the same name rejects with `INVALID_KEY` - `signWithHandle` / `decryptWithHandle` mirror the `RsaCryptoEngine` surface so handle consumers and engine consumers share the same scheme-options shape - `unregisterKeyHandleProvider` exposed for test cleanup ## Usage: Basic Example ```ts import { registerKeyHandleProvider, signWithHandle, } from "@suaveplan/crypto-rsa/handle"; registerKeyHandleProvider(myHsmProvider); const sig = await signWithHandle( { provider: "my-hsm", id: "key-1" }, message, { scheme: "pkcs1v15", hash: "SHA-256" } ); ``` ## Usage: Advanced Example For test setups that need a clean registry per test: ```ts import { registerKeyHandleProvider, unregisterKeyHandleProvider, SoftwareRsaKeyHandleProvider, } from "@suaveplan/crypto-rsa/handle"; beforeEach(() => { registerKeyHandleProvider(new SoftwareRsaKeyHandleProvider()); }); afterEach(() => { unregisterKeyHandleProvider("software"); }); ``` ## API Reference ### `registerKeyHandleProvider(provider)` - `provider: RsaKeyHandleProvider` - **throws** `CryptoRsaError(CRYPTO_RSA.INVALID_KEY)` on conflicting registration of a different provider under the same name ### `unregisterKeyHandleProvider(name)` - `name: string` ### `resolveKeyHandle(handle): Promise` - **throws** `CryptoRsaError(CRYPTO_RSA.INVALID_KEY)` when no provider is registered under the handle's `provider` name ### `signWithHandle(handle, message, options): Promise` ### `decryptWithHandle(handle, ciphertext, options): Promise` ## Implementation Notes - The registry is a module-level `Map`. The chosen scope is process-lifetime; if cross-process or per-vault isolation is required, callers must wrap their own dispatch layer. - Idempotent registration of the same provider value (object identity comparison) is a no-op so that import-once-then-register patterns work without per-import side effects. - Conflicting registration (two different provider objects with the same name) raises a typed error instead of silently winning — this is to detect copy-paste mistakes between adapters during integration. - Handle resolution failures throw `INVALID_KEY` because from the caller's perspective the supplied handle was not valid (the underlying cause — provider unregistered, id unknown — is captured in the error context).