EC-VRF (Elliptic Curve Verifiable Random Function)
The EC-VRF mode uses the ECVRF-ED25519-SHA512-ELL2 suite defined in RFC 9381 . Unlike Classic RNG, it produces a cryptographic proof with every bet, allowing immediate per-bet verification without waiting for seed rotation.
How It Works
1. Key Generation
The server generates an Ed25519 key pair:
- Private Key — 32 bytes, kept secret on the server
- Public Key — 32 bytes, shown to the player
The public key serves as the commitment — it is mathematically linked to the private key but cannot be used to derive it.
Private Key: (hidden from player)
Public Key: 3a7f1b... (shown to player)2. VRF Input (Alpha)
For each bet, the VRF input (called alpha) is constructed from the player’s client seed and the nonce:
alpha = UTF-8("clientSeed:nonce")For example, with client seed my-seed and nonce 42:
alpha = UTF-8("my-seed:42")3. VRF Prove
The server uses its private key and the alpha to produce two outputs:
- VRF Proof (80 bytes) — A cryptographic proof that the output was generated correctly
- VRF Output (64 bytes) — A SHA-512 hash used as the random value
(proof, output) = ECVRF_prove(privateKey, alpha)The proof consists of three components:
- Gamma (32 bytes) — A curve point derived from the private key and alpha
- c (16 bytes) — A Fiat-Shamir challenge
- s (32 bytes) — A scalar response
The output is computed as:
output = SHA-512(suite_string || 0x03 || point_to_string(cofactor × Gamma) || 0x00)4. Random Number Generation
The 64-byte VRF output is used as the source of randomness:
- Bytes 0–31: First round of random bytes
- Bytes 32–63: Second round of random bytes
- Beyond 64 bytes: HMAC-SHA256 expansion keyed by the VRF output
Round 0: vrfOutput[0..31]
Round 1: vrfOutput[32..63]
Round 2+: HMAC-SHA256(key=vrfOutput, message="clientSeed:nonce:round")The bytes are converted to floats in [0, 1) using the same method as Classic RNG:
float = byte[0] / 256 + byte[1] / 256² + byte[2] / 256³ + byte[3] / 256⁴5. Verification (Immediate, Per-Bet)
After every bet, the player receives the VRF Proof and VRF Output. They can immediately verify:
- Run
ECVRF_verify(publicKey, alpha, proof)→ returns computed output or null - Confirm the computed output matches the VRF Output provided with the bet
- If verification passes, the outcome is mathematically proven to be correct
alpha = UTF-8("clientSeed:nonce")
computedOutput = ECVRF_verify(publicKey, alpha, proof)
// Verification passes if:
// - computedOutput is not null (proof is valid)
// - computedOutput === vrfOutput (output matches)Verification Steps
To verify a bet:
-
Gather the data from the bet receipt:
- VRF Public Key (64 hex chars)
- Client Seed
- Nonce
- VRF Proof (160 hex chars)
- VRF Output (128 hex chars)
-
Build alpha:
UTF-8("clientSeed:nonce") -
Run verification:
ECVRF_verify(publicKey, alpha, proof) -
Compare: The returned output must match the VRF Output from the bet
You can use the verification tool at maktub.bet/ecvrf to verify bets directly in your browser — no data is sent to any server.
Player-Visible Data
| Field | When Visible | Description |
|---|---|---|
| VRF Public Key | Always | Server’s Ed25519 public key (64 hex chars) |
| Client Seed | Always | Player-chosen input |
| Nonce | After bet | Bet counter for the current seed pair |
| VRF Proof | After bet | 80-byte cryptographic proof (160 hex chars) |
| VRF Output | After bet | 64-byte SHA-512 hash (128 hex chars) |
| Next VRF Public Key | Always | Public key for the next seed pair |
Seed Rotation
When the player rotates their seeds:
- The current VRF Public Key remains visible (it was never hidden)
- The Next VRF Public Key becomes the active key
- A new Next VRF Public Key is generated
- The player can set a new client seed
- The nonce resets to 0
Since EC-VRF provides per-bet proofs, seed rotation is not required for verification — it simply refreshes the key pair.
Security Properties
- Uniqueness: For any given public key and alpha, only one valid output exists
- Unpredictable: Without the private key, the output cannot be predicted
- Deterministic: The same private key and alpha always produce the same output and proof
- Verifiable: Anyone with the public key can verify the proof without knowing the private key
- Non-interactive: Verification requires only the public key, alpha, and proof — no server interaction needed
- Per-bet proofs: Every single bet can be independently verified immediately
- Player influence: The client seed ensures the player contributes to the randomness
Technical Details
Curve Parameters
| Parameter | Value |
|---|---|
| Curve | Ed25519 (Twisted Edwards) |
| Hash | SHA-512 |
| Suite | ECVRF-ED25519-SHA512-ELL2 |
| Suite byte | 0x04 |
| Cofactor | 8 |
| Key size | 32 bytes (256 bits) |
| Proof size | 80 bytes (Gamma 32 + c 16 + s 32) |
| Output size | 64 bytes (SHA-512) |
Hash-to-Curve
The implementation uses the try-and-increment method (RFC 9381 Section 5.4.1.1):
- Hash
suite_string || 0x01 || publicKey || alpha || counter || 0x00with SHA-512 - Take the first 32 bytes and attempt to decode as an Ed25519 point
- Multiply by cofactor (8) to ensure the point is in the prime-order subgroup
- Increment counter and retry if decoding fails (up to 256 attempts)
Challenge Generation
The Fiat-Shamir challenge c is computed as:
hash = SHA-512(suite_string || 0x02 || H || Gamma || U || V || 0x00)
c = first 16 bytes of hash (little-endian integer)Verify Algorithm
1. Decode proof: Gamma (32 bytes) + c (16 bytes) + s (32 bytes)
2. Y = decode(publicKey) as Ed25519 point
3. H = hash_to_curve(publicKey, alpha)
4. U = s × B - c × Y
5. V = s × H - c × Gamma
6. c' = challenge_generation(publicKey, H, Gamma, U, V)
7. If c == c': output = SHA-512(0x04 || 0x03 || 8 × Gamma || 0x00)
8. Else: verification failedAdvantages Over Classic RNG
| Aspect | Classic RNG | EC-VRF |
|---|---|---|
| When can players verify? | After seed rotation | Immediately, per bet |
| Proof per bet | None | Yes (VRF Proof) |
| Server secret exposure | Server seed revealed on rotation | Private key never revealed |
| Mathematical guarantee | Hash commitment | Formal VRF security proof |
| Regulatory strength | Industry standard | RFC-standardized |