first commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# Ed25519
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
- [ed25519.select](./ed25519.select/ed25519.select.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_ - [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_ - [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519](./ed25519.md) — <!-- begin:auto-docs --> _(no submodules)_- ed25519.select — <!-- begin:auto-docs --> _(no submo…
|
||||
- [ed25519.select](./ed25519.select/ed25519.select.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519](./ed25519.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.select](./ed25519.select/ed25519.select.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519](./ed25519.md) — <!-- begin:auto-docs --> _(no submodules)_- ed25519.select — <!-- begin:…
|
||||
- [ed25519.select](./ed25519.select/ed25519.select.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519](./ed25519.md) — <!-- begin:auto-docs --> _(no submodules)_- ed25519.select — <!-- begin:auto-docs --> _(no subm…
|
||||
- [ed25519.select](./ed25519.select/ed25519.select.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_ - [ed25519.native](./impl/ed25519.native/ed25519.native.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_ - [ed25519.noble](./impl/ed25519.noble/ed25519.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [ed25519](./ed25519.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
## Purpose
|
||||
|
||||
Ed25519 is the Edwards-curve Digital Signature Algorithm defined in RFC 8032, the recommended signature scheme for new protocols. Compared with ECDSA it has deterministic signatures (no per-signature randomness needed), constant-time verification of well-formed inputs, small 32-byte public keys and 64-byte signatures, and formal soundness properties that make it the baseline choice for TLS, SSH, JWT signing, and peer-to-peer identity. This module exposes the primitive directly — key generation, signing, verification, and public-key derivation — as thin wrappers over `@noble/curves/ed25519`, with a paired `native` stub that throws `PrimitiveNotImplemented`. Selection between the two is controlled by `crypto.config.ts` at build time.
|
||||
|
||||
## Features
|
||||
|
||||
- `getPublicKey(privateKey)` → 32-byte compressed Edwards public key.
|
||||
- `sign(message, privateKey)` → 64-byte deterministic signature.
|
||||
- `verify(signature, message, publicKey)` → `boolean` that never throws for malformed inputs.
|
||||
- `randomPrivateKey()` → 32 random bytes (every 32-byte value is a valid Ed25519 secret; no retry loop needed).
|
||||
- RFC 8032 §7.1 vector 1 is covered in tests.
|
||||
- Native stub throws `PrimitiveNotImplemented('ed25519')` — flip to `'native'` only after per-primitive audit (Phase 3).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import * as ed25519 from "@suaveplan/crypto-curve/ed25519";
|
||||
|
||||
const sk = ed25519.randomPrivateKey();
|
||||
const pk = ed25519.getPublicKey(sk);
|
||||
const msg = new TextEncoder().encode("hello");
|
||||
const sig = ed25519.sign(msg, sk);
|
||||
console.log(ed25519.verify(sig, msg, pk)); // true
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import * as ed25519 from "@suaveplan/crypto-curve/ed25519";
|
||||
import { zeroize } from "@suaveplan/crypto-utils/zeroize";
|
||||
|
||||
function signAndDiscardKey(msg: Uint8Array): { pk: Uint8Array; sig: Uint8Array } {
|
||||
const sk = ed25519.randomPrivateKey();
|
||||
try {
|
||||
const pk = ed25519.getPublicKey(sk);
|
||||
const sig = ed25519.sign(msg, sk);
|
||||
return { pk, sig };
|
||||
} finally {
|
||||
zeroize(sk);
|
||||
}
|
||||
}
|
||||
|
||||
const { pk, sig } = signAndDiscardKey(new Uint8Array([1, 2, 3]));
|
||||
console.log(pk.length, sig.length);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `getPublicKey(privateKey: Uint8Array): Uint8Array` — 32-byte public key. Throws if `privateKey.length !== 32`.
|
||||
- `sign(message: Uint8Array, privateKey: Uint8Array): Uint8Array` — 64-byte signature.
|
||||
- `verify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean` — returns `false` instead of throwing on malformed inputs.
|
||||
- `randomPrivateKey(): Uint8Array` — 32 random bytes via `@suaveplan/crypto-utils/random`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Noble's `verify` throws on some malformed inputs (bad-length public key, non-canonical signature encodings) rather than returning `false`. The `noble` impl wraps the call in a try/catch so every flavour exposes a boolean-only contract — CI's differential fuzz harness depends on that uniformity. Signing is deterministic, so the same `(msg, sk)` pair always produces the same signature: this is a deliberate property that makes replay/caching sound and prevents fault-attack reuse. For signing data larger than a few megabytes, streaming APIs are not offered here on purpose — callers should compose over the hash-then-sign idiom instead.
|
||||
Reference in New Issue
Block a user