first commit
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# ecdsa-p384
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Provides ECDSA (Elliptic Curve Digital Signature Algorithm) operations using the NIST P-384 curve and SHA-384 hash, backed by `crypto.subtle`. P-384 offers 192 bits of security — stronger than P-256's 128 bits — and is required by many compliance frameworks (NIST SP 800-57, CNSA Suite 2.0, NSA Suite B). This module is intended for contexts where P-384 is mandated by policy or interoperability requirements.
|
||||
|
||||
The public key format is the 97-byte uncompressed point representation (0x04 prefix followed by 48-byte X and Y coordinates). The private key is PKCS8 DER. Both formats are standard and directly importable by other Web Crypto implementations.
|
||||
|
||||
## Features
|
||||
|
||||
- `generateKeypair()` — generates a P-384 ECDSA keypair using `crypto.subtle.generateKey`
|
||||
- `sign(privateKey, message)` — signs with ECDSA-P384 / SHA-384
|
||||
- `verify(publicKey, signature, message)` — verifies an ECDSA-P384 signature
|
||||
- Public key: 97-byte uncompressed point (raw format)
|
||||
- Private key: PKCS8 DER bytes
|
||||
- Signature format: IEEE P1363 (R || S) as returned by `crypto.subtle`
|
||||
- `assertWebCryptoAvailable()` guard on every call
|
||||
- Typed `BrowserCryptoError` for all failure paths
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { ecdsaP384 } from "@suaveplan/crypto-browser/ecdsa-p384/ecdsa-p384";
|
||||
|
||||
const kp = await ecdsaP384.generateKeypair();
|
||||
// kp.publicKey is 97 bytes (uncompressed P-384 point)
|
||||
// kp.privateKey is PKCS8 DER bytes
|
||||
|
||||
const message = new TextEncoder().encode("document to sign");
|
||||
const signature = await ecdsaP384.sign(kp.privateKey, message);
|
||||
const valid = await ecdsaP384.verify(kp.publicKey, signature, message);
|
||||
console.log(valid); // true
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { ecdsaP384 } from "@suaveplan/crypto-browser/ecdsa-p384/ecdsa-p384";
|
||||
import { BrowserCryptoError } from "@suaveplan/crypto-browser/errors";
|
||||
|
||||
// Issue a signed certificate-like structure
|
||||
async function issueAttestation(
|
||||
issuerPrivateKey: Uint8Array,
|
||||
payload: Uint8Array
|
||||
): Promise<{ signature: Uint8Array; algorithm: string }> {
|
||||
try {
|
||||
const signature = await ecdsaP384.sign(issuerPrivateKey, payload);
|
||||
return { signature, algorithm: "ECDSA-P384-SHA384" };
|
||||
} catch (err) {
|
||||
if (err instanceof BrowserCryptoError) {
|
||||
throw new Error(`Signing failed: ${err.code}`);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the attestation with a stored public key
|
||||
async function verifyAttestation(
|
||||
publicKey: Uint8Array,
|
||||
payload: Uint8Array,
|
||||
signature: Uint8Array
|
||||
): Promise<boolean> {
|
||||
return ecdsaP384.verify(publicKey, signature, payload);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `ecdsaP384.generateKeypair(): Promise<{ publicKey: Uint8Array; privateKey: Uint8Array }>`
|
||||
|
||||
Generates a P-384 ECDSA keypair.
|
||||
|
||||
**Returns:** `Promise<{ publicKey: Uint8Array; privateKey: Uint8Array }>` — 97-byte uncompressed public key and PKCS8 DER private key.
|
||||
|
||||
**Throws:** `BrowserCryptoError` — `BROWSER_CRYPTO.KEY.GENERATION_FAILED` or `BROWSER_CRYPTO.NOT_AVAILABLE`.
|
||||
|
||||
---
|
||||
|
||||
### `ecdsaP384.sign(privateKey, message): Promise<Uint8Array>`
|
||||
|
||||
Signs `message` using ECDSA-P384 with SHA-384.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `privateKey` | `Uint8Array` | PKCS8 DER private key bytes |
|
||||
| `message` | `Uint8Array` | Bytes to sign |
|
||||
|
||||
**Returns:** `Promise<Uint8Array>` — IEEE P1363 signature (96 bytes for P-384).
|
||||
|
||||
**Throws:** `BrowserCryptoError` — `BROWSER_CRYPTO.OPERATION.FAILED` or `BROWSER_CRYPTO.NOT_AVAILABLE`.
|
||||
|
||||
---
|
||||
|
||||
### `ecdsaP384.verify(publicKey, signature, message): Promise<boolean>`
|
||||
|
||||
Verifies an ECDSA-P384 / SHA-384 signature.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `publicKey` | `Uint8Array` | 97-byte uncompressed P-384 public key |
|
||||
| `signature` | `Uint8Array` | IEEE P1363 signature bytes |
|
||||
| `message` | `Uint8Array` | Original message bytes |
|
||||
|
||||
**Returns:** `Promise<boolean>` — `true` if signature is valid.
|
||||
|
||||
**Throws:** `BrowserCryptoError` — `BROWSER_CRYPTO.OPERATION.FAILED` or `BROWSER_CRYPTO.NOT_AVAILABLE`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
SHA-384 is paired with P-384 because the security level of the hash should match that of the curve. Using SHA-256 with P-384 would create a mismatch: the 192-bit curve security would be limited to the 128-bit collision resistance of SHA-256.
|
||||
|
||||
The signature output format from `crypto.subtle.sign` for ECDSA is IEEE P1363 (raw R || S concatenation), not DER/ASN.1. This differs from OpenSSL's default DER encoding. When interoperating with systems that expect DER signatures, a conversion layer is needed.
|
||||
|
||||
P-384 key generation is typically 2–5× slower than P-256 due to the larger field size. For high-throughput signing workloads where P-256 security is sufficient, consider using `webCryptoEcdsaP256Engine` instead.
|
||||
Reference in New Issue
Block a user