Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View 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.