first commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# primitives/ristretto255
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `ristretto255` namespace exposes libsodium's Ristretto255 group operations — a prime-order group construction over Curve25519 that eliminates the cofactor pitfalls present in raw Edwards25519 arithmetic. Ristretto255 is used in zero-knowledge proofs, Oblivious Pseudorandom Functions (OPRFs), Password-Authenticated Key Exchanges (PAKEs), and Verifiable Random Functions (VRFs).
|
||||
|
||||
By wrapping libsodium's `crypto_core_ristretto255_*` and `crypto_scalarmult_ristretto255_*` APIs, this module provides the building blocks for advanced cryptographic protocols without requiring callers to reason about low-level curve arithmetic or cofactor clearing.
|
||||
|
||||
## Features
|
||||
|
||||
- Group element addition (`add`) and subtraction (`sub`) via `crypto_core_ristretto255_add/sub`.
|
||||
- Scalar multiplication (`mul`) via `crypto_scalarmult_ristretto255`.
|
||||
- Hash-to-curve via `fromHash` using `crypto_core_ristretto255_from_hash` (64-byte input).
|
||||
- Point serialisation (`toBytes`) and deserialisation with validation (`fromBytes`).
|
||||
- Random group element generation (`random`) via `crypto_core_ristretto255_random`.
|
||||
- All operations are async and call `sodiumReady()` internally.
|
||||
- `fromBytes` throws `RangeError` for invalid or identity points.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { ristretto255 } from "@suaveplan/crypto-sodium/primitives/ristretto255";
|
||||
|
||||
// Generate two random group elements
|
||||
const p = await ristretto255.random();
|
||||
const q = await ristretto255.random();
|
||||
|
||||
// Add them
|
||||
const sum = await ristretto255.add(p, q);
|
||||
|
||||
// Verify commutativity
|
||||
const sumQP = await ristretto255.add(q, p);
|
||||
// sum deepEquals sumQP
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Hash-to-curve for OPRF input blinding:
|
||||
|
||||
```ts
|
||||
import { ristretto255 } from "@suaveplan/crypto-sodium/primitives/ristretto255";
|
||||
|
||||
// Hash the password to a group element (hash-to-curve)
|
||||
// In a real OPRF you'd use a proper H2C function; fromHash works for demos
|
||||
async function hashToPoint(input: Uint8Array): Promise<Uint8Array> {
|
||||
// Pad/hash input to 64 bytes with SHA-512 first in production:
|
||||
const padded = new Uint8Array(64);
|
||||
padded.set(input.subarray(0, Math.min(input.length, 64)));
|
||||
return ristretto255.fromHash(padded);
|
||||
}
|
||||
|
||||
// Scalar multiplication: client blinds the point with a random scalar
|
||||
const blindScalar = new Uint8Array(32);
|
||||
crypto.getRandomValues(blindScalar);
|
||||
const point = await hashToPoint(new TextEncoder().encode("password"));
|
||||
const blinded = await ristretto255.mul(blindScalar, point);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `ristretto255.add(p, q): Promise<Uint8Array>`
|
||||
|
||||
Adds two group elements. Commutative: `add(p, q) === add(q, p)`.
|
||||
|
||||
### `ristretto255.sub(p, q): Promise<Uint8Array>`
|
||||
|
||||
Subtracts `q` from `p`. Inverse of `add`.
|
||||
|
||||
### `ristretto255.mul(scalar, point): Promise<Uint8Array>`
|
||||
|
||||
Scalar multiplication: computes `scalar * point`. Scalar is a 32-byte little-endian integer.
|
||||
|
||||
### `ristretto255.fromHash(hash): Promise<Uint8Array>`
|
||||
|
||||
Maps a 64-byte hash uniformly to a group element via `crypto_core_ristretto255_from_hash`.
|
||||
|
||||
### `ristretto255.toBytes(point): Promise<Uint8Array>`
|
||||
|
||||
Returns a copy of the 32-byte canonical encoding of `point`.
|
||||
|
||||
### `ristretto255.fromBytes(bytes): Promise<Uint8Array>`
|
||||
|
||||
Validates and returns a group element. **Throws `RangeError`** if `bytes` is not a valid, non-identity Ristretto255 point.
|
||||
|
||||
### `ristretto255.random(): Promise<Uint8Array>`
|
||||
|
||||
Generates a uniformly random 32-byte group element.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Ristretto255 represents each group element as a 32-byte canonical encoding. Unlike raw Edwards25519, every 32-byte encoding maps to at most one group element — there are no encoding ambiguities or cofactor issues.
|
||||
|
||||
`fromBytes` calls `crypto_core_ristretto255_is_valid_point` before returning, which rejects the identity element (all-zeros) and any bytes that do not correspond to a valid Ristretto255 point. This prevents callers from accidentally working with invalid inputs in downstream protocols.
|
||||
|
||||
All operations return new `Uint8Array` allocations; the input arrays are not mutated.
|
||||
Reference in New Issue
Block a user