UNPKG

@noble/curves

Version:

Audited & minimal JS implementation of elliptic curve cryptography

282 lines (271 loc) 14.5 kB
/** * RFC 9497: Oblivious Pseudorandom Functions (OPRFs) Using Prime-Order Groups. * https://www.rfc-editor.org/rfc/rfc9497 * OPRF allows to interactively create an `Output = PRF(Input, serverSecretKey)`: - Server cannot calculate Output by itself: it doesn't know Input - Client cannot calculate Output by itself: it doesn't know server secretKey - An attacker interception the communication can't restore Input/Output/serverSecretKey and can't link Input to some value. ## Issues - Low-entropy inputs (e.g. password '123') enable brute-forced dictionary attacks by the server (solveable by domain separation in POPRF) - High-level protocol needs to be constructed on top, because OPRF is low-level ## Use cases 1. **Password-Authenticated Key Exchange (PAKE):** Enables secure password login (e.g., OPAQUE) without revealing the password to the server. 2. **Private Set Intersection (PSI):** Allows two parties to compute the intersection of their private sets without revealing non-intersecting elements. 3. **Anonymous Credential Systems:** Supports issuance of anonymous, unlinkable credentials (e.g., Privacy Pass) using blind OPRF evaluation. 4. **Private Information Retrieval (PIR):** Helps users query databases without revealing which item they accessed. 5. **Encrypted Search / Secure Indexing:** Enables keyword search over encrypted data while keeping queries private. 6. **Spam Prevention and Rate-Limiting:** Issues anonymous tokens to prevent abuse (e.g., CAPTCHA bypass) without compromising user privacy. ## Modes - OPRF: simple mode, client doesn't need to know server public key - VOPRF: verifable mode, allows client to verify that server used secret key corresponding to known public key - POPRF: partially oblivious mode, VOPRF + domain separation There is also non-interactive mode (Evaluate) that supports creating Output in non-interactive mode with knowledge of secret key. Flow: - (once) Server generates secret and public keys, distributes public keys to clients - deterministically: `deriveKeyPair` or just random: `generateKeyPair` - Client blinds input: `blind(secretInput)` - Server evaluates blinded input: `blindEvaluate` generated by client, sends result to client - Client creates output using result of evaluation via 'finalize' * @module */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ import { randomBytes } from '../utils.ts'; import { type CurvePoint, type CurvePointCons } from './curve.ts'; import { type H2CDSTOpts } from './hash-to-curve.ts'; export type PointBytes = Uint8Array; export type ScalarBytes = Uint8Array; export type Bytes = Uint8Array; export type RNG = typeof randomBytes; export type OPRFOpts<P extends CurvePoint<any, P>> = { name: string; Point: CurvePointCons<P>; hash(msg: Bytes): Bytes; hashToScalar(msg: Uint8Array, options: H2CDSTOpts): bigint; hashToGroup(msg: Uint8Array, options: H2CDSTOpts): P; }; export type OPRFKeys = { secretKey: ScalarBytes; publicKey: PointBytes; }; export type OPRFBlind = { blind: Uint8Array; blinded: Uint8Array; }; export type OPRFBlindEval = { evaluated: PointBytes; proof: Bytes; }; export type OPRFBlindEvalBatch = { evaluated: PointBytes[]; proof: Bytes; }; export type OPRFFinalizeItem = { input: Bytes; blind: ScalarBytes; evaluated: PointBytes; blinded: PointBytes; }; /** * Represents a full OPRF ciphersuite implementation according to RFC 9497. * This object bundles the three protocol variants (OPRF, VOPRF, POPRF) for a specific * prime-order group and hash function combination. * * @see https://www.rfc-editor.org/rfc/rfc9497.html */ export type OPRF = { /** * The unique identifier for the ciphersuite, e.g., "ristretto255-SHA512". * This name is used for domain separation to prevent cross-protocol attacks. */ readonly name: string; /** * The base Oblivious Pseudorandom Function (OPRF) mode (mode 0x00). * This is a two-party protocol between a client and a server to compute F(k, x) * where 'k' is the server's key and 'x' is the client's input. * * The client learns the output F(k, x) but nothing about 'k'. * The server learns nothing about 'x' or F(k, x). * This mode is NOT verifiable; the client cannot prove the server used a specific key. */ readonly oprf: { /** * (Server-side) Generates a new random private/public key pair for the server. * @returns A new key pair. */ generateKeyPair(): OPRFKeys; /** * (Server-side) Deterministically derives a private/public key pair from a seed. * @param seed A 32-byte cryptographically secure random seed. * @param keyInfo An optional byte string for domain separation. * @returns The derived key pair. */ deriveKeyPair(seed: Bytes, keyInfo: Bytes): OPRFKeys; /** * (Client-side) The first step of the protocol. The client blinds its private input. * @param input The client's private input bytes. * @param rng An optional cryptographically secure random number generator. * @returns An object containing the `blind` scalar (which the client MUST keep secret) * and the `blinded` element (which the client sends to the server). */ blind(input: Bytes, rng?: RNG): OPRFBlind; /** * (Server-side) The second step. The server evaluates the client's blinded element * using its secret key. * @param secretKey The server's private key. * @param blinded The blinded group element received from the client. * @returns The evaluated group element, to be sent back to the client. */ blindEvaluate(secretKey: ScalarBytes, blinded: PointBytes): PointBytes; /** * (Client-side) The final step. The client unblinds the server's response to * compute the final OPRF output. * @param input The original private input from the `blind` step. * @param blind The secret scalar from the `blind` step. * @param evaluated The evaluated group element received from the server. * @returns The final OPRF output, `Hash(len(input)||input||len(unblinded)||unblinded||"Finalize")`. */ finalize(input: Bytes, blind: ScalarBytes, evaluated: PointBytes): Bytes; }; /** * The Verifiable Oblivious Pseudorandom Function (VOPRF) mode (mode 0x01). * This mode extends the base OPRF by providing a proof that the server used the * secret key corresponding to its known public key. */ readonly voprf: { /** (Server-side) Generates a key pair for the VOPRF mode. */ generateKeyPair(): OPRFKeys; /** (Server-side) Deterministically derives a key pair for the VOPRF mode. */ deriveKeyPair(seed: Bytes, keyInfo: Bytes): OPRFKeys; /** (Client-side) Blinds the client's private input for the VOPRF protocol. */ blind(input: Bytes, rng?: RNG): OPRFBlind; /** * (Server-side) Evaluates the client's blinded element and generates a DLEQ proof * of correctness. * @param secretKey The server's private key. * @param publicKey The server's public key, used in proof generation. * @param blinded The blinded group element received from the client. * @param rng An optional cryptographically secure random number generator for the proof. * @returns The evaluated element and a proof of correct computation. */ blindEvaluate(secretKey: ScalarBytes, publicKey: PointBytes, blinded: PointBytes, rng?: RNG): OPRFBlindEval; /** * (Server-side) An optimized batch version of `blindEvaluate`. It evaluates multiple * blinded elements and produces a single, constant-size proof for the entire batch, * amortizing the cost of proof generation. * @param secretKey The server's private key. * @param publicKey The server's public key. * @param blinded An array of blinded group elements from one or more clients. * @param rng An optional cryptographically secure random number generator for the proof. * @returns An array of evaluated elements and a single proof for the batch. */ blindEvaluateBatch(secretKey: ScalarBytes, publicKey: PointBytes, blinded: PointBytes[], rng?: RNG): OPRFBlindEvalBatch; /** * (Client-side) The final step. The client verifies the server's proof, and if valid, * unblinds the result to compute the final VOPRF output. * @param input The original private input. * @param blind The secret scalar from the `blind` step. * @param evaluated The evaluated element from the server. * @param blinded The blinded element sent to the server (needed for proof verification). * @param publicKey The server's public key against which the proof is verified. * @param proof The DLEQ proof from the server. * @returns The final VOPRF output. * @throws If the proof verification fails. */ finalize(input: Bytes, blind: ScalarBytes, evaluated: PointBytes, blinded: PointBytes, publicKey: PointBytes, proof: Bytes): Bytes; /** * (Client-side) The batch-aware version of `finalize`. It verifies a single batch proof * against a list of corresponding inputs and outputs. * @param items An array of objects, each containing the parameters for a single finalization. * @param publicKey The server's public key. * @param proof The single DLEQ proof for the entire batch. * @returns An array of final VOPRF outputs, one for each item in the input. * @throws If the proof verification fails. */ finalizeBatch(items: OPRFFinalizeItem[], publicKey: PointBytes, proof: Bytes): Bytes[]; }; /** * A factory for the Partially Oblivious Pseudorandom Function (POPRF) mode (mode 0x02). * This mode extends VOPRF to include a public `info` parameter, known to both client and * server, which is cryptographically bound to the final output. * This is useful for domain separation at the application level. * @param info A public byte string to be mixed into the computation. * @returns An object with the POPRF protocol functions. */ readonly poprf: (info: Bytes) => { /** (Server-side) Generates a key pair for the POPRF mode. */ generateKeyPair(): OPRFKeys; /** (Server-side) Deterministically derives a key pair for the POPRF mode. */ deriveKeyPair(seed: Bytes, keyInfo: Bytes): OPRFKeys; /** * (Client-side) Blinds the client's private input and computes the "tweaked key". * The tweaked key is a public value derived from the server's public key and the public `info`. * @param input The client's private input. * @param publicKey The server's public key. * @param rng An optional cryptographically secure random number generator. * @returns The `blind`, `blinded` element, and the `tweakedKey` which the client uses for verification. */ blind(input: Bytes, publicKey: PointBytes, rng?: RNG): OPRFBlind & { tweakedKey: PointBytes; }; /** * (Server-side) Evaluates the blinded element using a key derived from its secret key and the public `info`. * It generates a DLEQ proof against the tweaked key. * @param secretKey The server's private key. * @param blinded The blinded element from the client. * @param rng An optional RNG for the proof. * @returns The evaluated element and a proof of correct computation. */ blindEvaluate(secretKey: ScalarBytes, blinded: PointBytes, rng?: RNG): OPRFBlindEval; /** * (Server-side) A batch-aware version of `blindEvaluate` for the POPRF mode. * @param secretKey The server's private key. * @param blinded An array of blinded elements. * @param rng An optional RNG for the proof. * @returns An array of evaluated elements and a single proof for the batch. */ blindEvaluateBatch(secretKey: ScalarBytes, blinded: PointBytes[], rng: RNG): OPRFBlindEvalBatch; /** * (Client-side) A batch-aware version of `finalize` for the POPRF mode. * It verifies the proof against the tweaked key. * @param items An array containing the parameters for each finalization. * @param proof The single DLEQ proof for the batch. * @param tweakedKey The tweaked key corresponding to the proof (all items must share the same `info` and `publicKey`). * @returns An array of final POPRF outputs. * @throws If proof verification fails. */ finalizeBatch(items: OPRFFinalizeItem[], proof: Bytes, tweakedKey: PointBytes): Bytes[]; /** * (Client-side) Finalizes the POPRF protocol. It verifies the server's proof against the * `tweakedKey` computed in the `blind` step. The final output is bound to the public `info`. * @param input The original private input. * @param blind The secret scalar. * @param evaluated The evaluated element from the server. * @param blinded The blinded element sent to the server. * @param proof The DLEQ proof from the server. * @param tweakedKey The public tweaked key computed by the client during the `blind` step. * @returns The final POPRF output. * @throws If proof verification fails. */ finalize(input: Bytes, blind: ScalarBytes, evaluated: PointBytes, blinded: PointBytes, proof: Bytes, tweakedKey: PointBytes): Bytes; /** * A non-interactive evaluation function for an entity that knows all inputs. * Computes the final POPRF output directly. Useful for testing or specific applications * where the server needs to compute the output for a known input. * @param secretKey The server's private key. * @param input The client's private input. * @returns The final POPRF output. */ evaluate(secretKey: ScalarBytes, input: Bytes): Bytes; }; }; export declare function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPRF; //# sourceMappingURL=oprf.d.ts.map