Initial Commit
This commit is contained in:
138
packages/security-stack/crypto/crypto-browser/src/rsa/rsa.md
Normal file
138
packages/security-stack/crypto/crypto-browser/src/rsa/rsa.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# rsa
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Provides a high-level `rsa` namespace object wrapping RSA cryptographic operations from `crypto.subtle`. Supports three algorithm variants — `RSA-PSS`, `RSA-OAEP`, and `RSASSA-PKCS1-v1_5` — for signing/verification and asymmetric encryption/decryption. Keys are represented as standard DER byte arrays (SPKI for public keys, PKCS8 for private keys), which are universally portable across platforms and easy to store and transmit.
|
||||
|
||||
RSA is provided alongside the elliptic-curve engines for interoperability with legacy systems, PKI infrastructure, and protocols that require RSA (e.g., certain TLS cipher suites, JWT RSA algorithms, legacy JOSE). For new designs, Ed25519 and ECDH-X25519 are preferred.
|
||||
|
||||
## Features
|
||||
|
||||
- `generateKeypair(algorithm, modulusLength?)` — generates an RSA keypair; default 2048-bit modulus, configurable up to 4096 bits
|
||||
- `sign(algorithm, privateKey, message)` — RSA-PSS or RSASSA-PKCS1-v1_5 signature generation
|
||||
- `verify(algorithm, publicKey, signature, message)` — RSA signature verification
|
||||
- `encrypt(publicKey, plaintext)` — RSA-OAEP encryption (public-key encryption)
|
||||
- `decrypt(privateKey, ciphertext)` — RSA-OAEP decryption
|
||||
- SHA-256 hash used for all operations
|
||||
- RSA-PSS uses 32-byte salt length
|
||||
- All methods guard with `assertWebCryptoAvailable()`
|
||||
- Typed `BrowserCryptoError` for all failure paths
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { rsa } from "@suaveplan/crypto-browser/rsa/rsa";
|
||||
|
||||
// Generate and use RSA-PSS for signing
|
||||
const { publicKey, privateKey } = await rsa.generateKeypair("RSA-PSS");
|
||||
const message = new TextEncoder().encode("document to sign");
|
||||
|
||||
const signature = await rsa.sign("RSA-PSS", privateKey, message);
|
||||
const valid = await rsa.verify("RSA-PSS", publicKey, signature, message);
|
||||
console.log(valid); // true
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { rsa, type RsaAlgorithm } from "@suaveplan/crypto-browser/rsa/rsa";
|
||||
import { BrowserCryptoError } from "@suaveplan/crypto-browser/errors";
|
||||
|
||||
// RSA-OAEP for encrypting a small payload (e.g., a symmetric key)
|
||||
async function wrapAesKey(
|
||||
recipientPublicKey: Uint8Array,
|
||||
aesKey: Uint8Array // must be <= ~190 bytes for RSA-2048
|
||||
): Promise<Uint8Array> {
|
||||
try {
|
||||
return await rsa.encrypt(recipientPublicKey, aesKey);
|
||||
} catch (err) {
|
||||
if (err instanceof BrowserCryptoError) {
|
||||
console.error(`Crypto failure: ${err.code}`);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function unwrapAesKey(
|
||||
recipientPrivateKey: Uint8Array,
|
||||
wrappedKey: Uint8Array
|
||||
): Promise<Uint8Array> {
|
||||
return await rsa.decrypt(recipientPrivateKey, wrappedKey);
|
||||
}
|
||||
|
||||
// 4096-bit keypair for higher-security contexts
|
||||
const { publicKey, privateKey } = await rsa.generateKeypair("RSA-OAEP", 4096);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `rsa.generateKeypair(algorithm, modulusLength?): Promise<{ publicKey, privateKey }>`
|
||||
|
||||
Generates an RSA keypair.
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `algorithm` | `RsaAlgorithm` | — | `"RSA-PSS"` \| `"RSA-OAEP"` \| `"RSASSA-PKCS1-v1_5"` |
|
||||
| `modulusLength` | `number` | `2048` | Key bit length (2048 or 4096 recommended) |
|
||||
|
||||
**Returns:** `Promise<{ publicKey: Uint8Array; privateKey: Uint8Array }>` — SPKI/PKCS8 DER bytes.
|
||||
|
||||
**Throws:** `BrowserCryptoError` — `BROWSER_CRYPTO.KEY.GENERATION_FAILED`.
|
||||
|
||||
---
|
||||
|
||||
### `rsa.sign(algorithm, privateKey, message): Promise<Uint8Array>`
|
||||
|
||||
Signs a message. Use with `"RSA-PSS"` or `"RSASSA-PKCS1-v1_5"`.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algorithm` | `RsaAlgorithm` | Must match keypair generation algorithm |
|
||||
| `privateKey` | `Uint8Array` | PKCS8 DER private key |
|
||||
| `message` | `Uint8Array` | Message to sign |
|
||||
|
||||
**Returns:** `Promise<Uint8Array>` — signature bytes.
|
||||
|
||||
**Throws:** `BrowserCryptoError` — `BROWSER_CRYPTO.OPERATION.FAILED`.
|
||||
|
||||
---
|
||||
|
||||
### `rsa.verify(algorithm, publicKey, signature, message): Promise<boolean>`
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algorithm` | `RsaAlgorithm` | Must match signing algorithm |
|
||||
| `publicKey` | `Uint8Array` | SPKI DER public key |
|
||||
| `signature` | `Uint8Array` | Signature bytes |
|
||||
| `message` | `Uint8Array` | Original message |
|
||||
|
||||
**Returns:** `Promise<boolean>`.
|
||||
|
||||
---
|
||||
|
||||
### `rsa.encrypt(publicKey, plaintext): Promise<Uint8Array>`
|
||||
|
||||
RSA-OAEP encryption. For RSA-2048, plaintext must be ≤ 190 bytes (2048-bit modulus minus OAEP overhead).
|
||||
|
||||
---
|
||||
|
||||
### `rsa.decrypt(privateKey, ciphertext): Promise<Uint8Array>`
|
||||
|
||||
RSA-OAEP decryption.
|
||||
|
||||
---
|
||||
|
||||
### `type RsaAlgorithm`
|
||||
|
||||
`"RSA-PSS" | "RSA-OAEP" | "RSASSA-PKCS1-v1_5"`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
All RSA operations use SHA-256 as the hash algorithm, which is the most widely supported and appropriate choice for 2048- and 4096-bit RSA. RSA-PSS uses a 32-byte (256-bit) salt, matching the hash output length, which is the PKCS#1v2.2 recommendation.
|
||||
|
||||
The `encrypt()` and `decrypt()` methods are hardcoded to RSA-OAEP — only RSA-OAEP provides semantic security for asymmetric encryption. `RSA-PSS` and `RSASSA-PKCS1-v1_5` are signing-only algorithms and cannot be used with `encrypt`/`decrypt`.
|
||||
|
||||
Key generation for RSA-2048 typically takes 50–200 ms in `crypto.subtle`; RSA-4096 can take 500–2000 ms. This is significantly slower than elliptic-curve key generation (< 5 ms). All operations use `await` and do not block the main thread.
|
||||
Reference in New Issue
Block a user