libvrf
v1.1.0
Published
Verifiable Random Functions (VRF) for Node.js and browsers - RFC 9381 RSA-FDH/PSS-NOSALT; simplified EC VRF (not RFC-interoperable)
Downloads
227
Maintainers
Readme
libvrf-js
Verifiable Random Functions for JavaScript
A Verifiable Random Function (VRF) is a cryptographic public-key primitive that, from a secret key and a given input, produces a unique pseudorandom output, along with a proof that the output was correctly computed. Only the secret key holder can generate the output–proof pair, but anyone with the corresponding public key can verify the proof.
libvrf-js is a TypeScript/JavaScript implementation of several VRFs for Node.js (v18+) and browsers (EC VRF only).
Features
- Multiple VRF algorithms: RSA-FDH and RSA-PSS-NOSALT (RFC 9381); simplified EC VRF (not RFC-interoperable)
- Node.js & Browsers: EC VRF works in both Node.js and browsers; RSA VRFs work in Node.js only
- Type-safe: Written in TypeScript with full type definitions
- Well-tested: Full matrix over all VRF types; RFC 9381 RSA-FDH vectors from Microsoft libvrf
- Compliance helpers:
isRFC9381Compliant()andgetVRFSuiteInfo()for safe integration - Typed errors:
VRFErrorand*OrThrowAPIs alongside legacy null-return methods
Installation
npm install libvrfRequirements:
- Node.js v18.0.0 or later
- npm v9+ (or compatible package manager)
Dependencies:
node-rsa(^1.1.1) - Used for RSA-based VRF operations only. EC VRF uses Node.js built-in crypto.
Browser/CDN Usage
✅ Browser Support: EC VRF now works in browsers using WebCrypto API! RSA-based VRFs still require Node.js.
Current Status:
- ✅ EC VRF: Works in browsers using async APIs and WebCrypto
- ❌ RSA VRFs: Do not work in browsers (requires
node-rsaand Node.js crypto) - ✅ Node.js: All VRF types work correctly
The library is available via CDN:
jsDelivr (latest version):
<script src="https://cdn.jsdelivr.net/npm/libvrf/dist/browser/libvrf.min.js"></script>unpkg (latest version):
<script src="https://unpkg.com/libvrf/dist/browser/libvrf.min.js"></script>Usage in browser:
<script src="https://unpkg.com/libvrf/dist/browser/libvrf.min.js"></script>
<script>
// After loading the script, use the global 'libvrf' object
// Note: Browser APIs are async
(async () => {
// Create a secret key (async)
const secretKey = await libvrf.VRF.createAsync(libvrf.VRFType.EC_VRF_P256_SHA256_TAI);
// Get the public key (async)
const publicKey = await secretKey.getPublicKeyAsync();
// Generate a VRF proof (async)
const input = new TextEncoder().encode('hello');
const proof = await secretKey.getVRFProofAsync(input);
// Verify the proof (async)
const [success, vrfValue] = await publicKey.verifyVRFProofAsync(input, proof);
console.log('Verification:', success);
console.log('VRF Value:', Array.from(vrfValue).map(b => b.toString(16).padStart(2, '0')).join(''));
})();
</script>Important Notes:
- EC VRF only: Only
EC_VRF_P256_SHA256_TAIworks in browsers - Async APIs: Browser code must use
createAsync(),getPublicKeyAsync(),getVRFProofAsync(), andverifyVRFProofAsync() - Node.js compatibility: Node.js can also use the async APIs, or continue using the sync APIs
Supported VRF Types
RSA-based VRFs
RSA-FDH (Full Domain Hash)
RSA_FDH_VRF_RSA2048_SHA256- RSA-FDH with 2048-bit key and SHA-256RSA_FDH_VRF_RSA3072_SHA256- RSA-FDH with 3072-bit key and SHA-256RSA_FDH_VRF_RSA4096_SHA384- RSA-FDH with 4096-bit key and SHA-384RSA_FDH_VRF_RSA4096_SHA512- RSA-FDH with 4096-bit key and SHA-512
RSA-PSS-NOSALT
RSA_PSS_NOSALT_VRF_RSA2048_SHA256- RSA-PSS (no salt) with 2048-bit key and SHA-256RSA_PSS_NOSALT_VRF_RSA3072_SHA256- RSA-PSS (no salt) with 3072-bit key and SHA-256RSA_PSS_NOSALT_VRF_RSA4096_SHA384- RSA-PSS (no salt) with 4096-bit key and SHA-384RSA_PSS_NOSALT_VRF_RSA4096_SHA512- RSA-PSS (no salt) with 4096-bit key and SHA-512
Elliptic Curve VRFs
EC_VRF_P256_SHA256_TAI- Simplified EC VRF on P-256 / SHA-256 (not RFC 9381 wire-compatible)
Quick Start
Basic Usage
import { VRF, VRFType } from 'libvrf';
// 1. Choose a VRF type and generate a key pair
const type = VRFType.RSA_FDH_VRF_RSA2048_SHA256;
const secretKey = VRF.create(type)!;
// 2. Get the public key
const publicKey = secretKey.getPublicKey()!;
// 3. Generate a VRF proof for some input
const input = new TextEncoder().encode('hello world');
const proof = secretKey.getVRFProof(input)!;
// 4. Verify the proof and get the VRF value
const [success, vrfValue] = publicKey.verifyVRFProof(input, proof);
console.log('Proof verified:', success);
console.log('VRF Value:', Buffer.from(vrfValue).toString('hex'));Serialization
import { VRF, VRFType } from 'libvrf';
const type = VRFType.EC_VRF_P256_SHA256_TAI;
const secretKey = VRF.create(type)!;
const publicKey = secretKey.getPublicKey()!;
// Serialize public key (DER-encoded SPKI)
const publicKeyBytes = publicKey.toBytes();
console.log('Public key size:', publicKeyBytes.length, 'bytes');
// Deserialize public key
const loadedPublicKey = VRF.publicKeyFromBytes(type, publicKeyBytes)!;
// Serialize proof
const input = new Uint8Array([1, 2, 3, 4, 5]);
const proof = secretKey.getVRFProof(input)!;
const proofBytes = proof.toBytes();
// Deserialize proof
const loadedProof = VRF.proofFromBytes(type, proofBytes)!;
// Verify with loaded key and proof
const [success, vrfValue] = loadedPublicKey.verifyVRFProof(input, loadedProof);
console.log('Verification:', success);
console.log('VRF Value:', Buffer.from(vrfValue).toString('hex'));Using Different VRF Types
import { VRF, VRFType } from 'libvrf';
// EC VRF (fastest, smallest keys)
const ecKey = VRF.create(VRFType.EC_VRF_P256_SHA256_TAI)!;
// RSA-FDH VRF (widely compatible)
const rsaFdhKey = VRF.create(VRFType.RSA_FDH_VRF_RSA2048_SHA256)!;
// RSA-PSS VRF (based on RSA-PSS signatures)
const rsaPssKey = VRF.create(VRFType.RSA_PSS_NOSALT_VRF_RSA2048_SHA256)!;API Reference
VRF
The main class providing static methods for VRF operations.
VRF.create(type: VRFType): SecretKey | null
Creates a new VRF secret key for the specified VRF type. Returns null on failure.
Prefer VRF.createOrThrow() when you need explicit VRFError handling.
VRF.createOrThrow(type: VRFType): SecretKey
Same as create, but throws VRFError with a VRFErrorCode instead of returning null.
isRFC9381Compliant(type: VRFType): boolean
Returns true for RSA-FDH and RSA-PSS-NOSALT suites. EC VRF in this port returns false.
getVRFSuiteInfo(type: VRFType): VRFSuiteInfo | null
Returns browser support and interoperability metadata for the suite.
VRF.proofFromBytes(type: VRFType, data: Uint8Array): Proof | null
Deserializes a VRF proof from bytes.
VRF.publicKeyFromBytes(type: VRFType, data: Uint8Array): PublicKey | null
Deserializes a VRF public key from bytes.
VRF.secretKeyToBytes(secretKey: SecretKey): Uint8Array | null
Serializes a secret key (RSA: PKCS#8 DER; EC: raw private scalar). Never log or commit this output.
VRF.secretKeyFromBytes(type: VRFType, data: Uint8Array): SecretKey | null
Restores a secret key from bytes. EC import uses the Node.js sync path; in browsers use VRF.createAsync() and initializeAsync() instead.
SecretKey
Represents a VRF secret key.
Methods
getVRFProof(input: Uint8Array): Proof | null- Generates a VRF proof (Node.js sync)getVRFProofAsync(input)/getPublicKeyAsync()- Use in browsers; sync methods throw or returnnullwhen not initializedgetPublicKey(): PublicKey | null- Returns the corresponding public keyclone(): SecretKey- Creates a deep copyisInitialized(): boolean- Checks if properly initializedgetType(): VRFType- Returns the VRF type
PublicKey
Represents a VRF public key.
Methods
verifyVRFProof(input: Uint8Array, proof: Proof): [boolean, Uint8Array]- Verifies a prooftoBytes(): Uint8Array- Serializes the public keyfromBytes(type: VRFType, data: Uint8Array): boolean- Deserializes from bytesclone(): PublicKey- Creates a deep copyisInitialized(): boolean- Checks if properly initializedgetType(): VRFType- Returns the VRF type
Proof
Represents a VRF proof.
Methods
getVRFValue(): Uint8Array- Extracts the VRF valuetoBytes(): Uint8Array- Serializes the prooffromBytes(type: VRFType, data: Uint8Array): boolean- Deserializes from bytesclone(): Proof- Creates a deep copyisInitialized(): boolean- Checks if properly initializedgetType(): VRFType- Returns the VRF type
Security Considerations
Important Security Notes:
EC VRF RFC 9381 Compliance: The EC VRF implementation (
EC_VRF_P256_SHA256_TAI) uses a simplified deterministic construction and is NOT RFC 9381 compliant. EC VRF proofs are NOT interoperable with RFC 9381 compliant implementations (including the C++ libvrf).Browser Support: Only EC VRF works in browsers. RSA-based VRFs require Node.js. Browser code must use async APIs (
createAsync,getPublicKeyAsync,getVRFProofAsync,verifyVRFProofAsync).Key Generation Trust: RSA-based VRFs are not secure unless the key generation process is trusted. For more details, see RFC 9381.
Cryptographic Primitives: This library uses Node.js's built-in
cryptomodule and browser's WebCrypto API for cryptographic operations.Production Use: This is a JavaScript port with simplified EC VRF implementation. For production systems requiring RFC 9381 compliance or interoperability, use the original C++ libvrf.
VRF Value: The VRF value is deterministic for a given key and input. The same key and input will always produce the same VRF value.
Limitations
This JavaScript implementation has several important limitations compared to the original C++ libvrf:
EC VRF RFC 9381 Compliance
- NOT RFC 9381 Compliant: The EC VRF implementation (
EC_VRF_P256_SHA256_TAI) uses a simplified deterministic construction and does not implement the full RFC 9381 specification. - NOT Interoperable: EC VRF proofs generated by this library cannot be verified by RFC 9381 compliant implementations (including the C++ libvrf), and vice versa.
- Different Proof Format: The proof structure and VRF output values differ from RFC 9381 compliant implementations.
- Self-Consistent Only: Proofs generated by this library can only be verified by this library.
Browser Support
- EC VRF Browser Support: EC VRF (
EC_VRF_P256_SHA256_TAI) works in browsers using WebCrypto API with async methods (createAsync(),getPublicKeyAsync(),getVRFProofAsync(),verifyVRFProofAsync()) - RSA VRFs: RSA-based VRFs require
node-rsaand Node.js crypto APIs, which are not available in browsers - Node.js Compatibility: Node.js supports both sync and async APIs for EC VRF - use sync methods for backward compatibility or async methods for consistency with browser code
Test Coverage
- No Standard Test Vectors: This implementation does not include RFC 9381 test vectors and has not been tested for compatibility with the C++ libvrf test vectors.
- RSA VRF Compatibility: RSA-FDH and RSA-PSS-NOSALT VRFs are likely compatible with the C++ implementation but have not been tested against C++ test vectors.
Interoperability
- EC VRF: Not compatible with other RFC 9381 implementations or the C++ libvrf.
- RSA VRFs: Compatibility with other implementations is untested and may vary.
Recommendations
- For RFC 9381 Compliance: Use the original C++ libvrf or another RFC 9381 compliant implementation.
- For Interoperability: Do not use this library's EC VRF in systems that need to interoperate with other VRF implementations.
- For Self-Contained Systems: This library works well for systems where all components use
libvrf-jsexclusively.
Browser Support
This library works in modern browsers that support:
- WebCrypto API
- ES2020 features
- Uint8Array
Tested browsers:
- Chrome/Edge 80+
- Firefox 75+
- Safari 14+
Building
# Install dependencies
npm install
# Build for Node.js and browser
npm run build
# Build only for Node.js
npm run build:node
# Build only for browser
npm run build:browser
# Fast tests (2048 RSA + EC; default in CI)
npm test
# Slow RSA 3072/4096 matrix
npm run test:full
# Coverage (same fast set as npm test)
npm run test:coverage
# Browser bundle smoke test
npm run test:browser-smoke
# Prove/verify timing
npm run benchmark
# Lint code
npm run lintSupported Node.js: LTS releases 18.x, 20.x, 22.x, 24.x (see CI matrix). Dependabot opens weekly npm update PRs.
Examples
See the examples directory for more usage examples:
Contributing
Contributions are welcome! Please open an issue or submit a pull request on GitHub.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
This is a JavaScript/TypeScript port of the official libvrf C++ library.
The original C++ implementation by Microsoft Corporation provides a robust, production-ready VRF library with comprehensive test coverage and full RFC 9381 compliance. This JavaScript port brings similar functionality to Node.js and browser environments while maintaining API compatibility with the original design.
Note: The EC VRF implementation in this JavaScript port uses a simplified deterministic construction and is NOT RFC 9381 compliant. For RFC 9381 compliance and interoperability, use the original C++ implementation.
References
- RFC 9381 - Verifiable Random Functions (VRFs)
- libvrf - Official C++ implementation
- Microsoft libvrf GitHub Repository
Related Projects
- Original C++ Library: https://github.com/Microsoft/libvrf
- This JavaScript/TypeScript port aims to provide the same API and functionality for web and Node.js applications
Support
For issues, questions, or contributions related to:
- This JavaScript port: Please open issues in this repository
- The original C++ library: Visit https://github.com/Microsoft/libvrf
