ElGamal Encryption

Obelysk uses Exponential ElGamal encryption over the STARK curve to hide all token amounts on-chain. This is the foundational cryptographic primitive powering every privacy feature in Track 1 — from confidential transfers to dark pool trading.

STARK
Curve
252-bit
Key Size
Additive
Homomorphic
6
Proofs/Transfer

Why ElGamal?

ElGamal encryption has a critical property that makes it perfect for on-chain privacy: additive homomorphism. The contract can update encrypted balances without ever decrypting them.

Additive Homomorphism

If Enc(a) encrypts amount a and Enc(b) encrypts amount b, then Enc(a) ⊕ Enc(b) = Enc(a + b). The contract can add encrypted amounts together without knowing what they are. This is how balances are updated on-chain.

The Scheme

ElGamal on the STARK curve works as follows:

Key Generation

Private key:  sk ← random scalar in [1, n-1]
Public key:   pk = sk · G

Where G is the STARK curve generator and n is the curve order.

Encryption

To encrypt an amount m with randomness r:

Ciphertext = (L, R) where:
  L = m · G + r · pk     (left component — encodes the message)
  R = r · G              (right component — encodes the randomness)

Decryption

The private key holder computes:

m · G = L - sk · R
      = (m·G + r·pk) - sk·(r·G)
      = m·G + r·sk·G - sk·r·G
      = m·G

Then recovers m via discrete log (using AE hints for O(1) decryption instead of brute force).

AE Hints

Since solving discrete log is expensive, the sender includes an AE hint — an encrypted version of the plaintext amount. The recipient decrypts the hint using a shared secret derived from ECDH, recovering the amount in O(1) time.

STARK Curve Parameters

The STARK curve is an elliptic curve defined over the Stark prime field:

Curve equation:  y² = x³ + α·x + β  (mod p)
  where α = 1

Field prime (p):
  0x800000000000011000000000000000000000000000000000000000000000001

Curve order (n):
  0x800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f

Generator G:
  x = 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca
  y = 0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f

Pedersen Generator H (nothing-up-my-sleeve):
  x = 0x73bd2c9434c955f80b06d2847f8384a226d6cc2557a5735fd9f84d632f576be
  y = 0x1bd58ea52858154de69bf90e446ff200f173d49da444c4f462652ce6b93457e
Field Prime ≠ Curve Order

A critical implementation detail: all scalar operations (Schnorr challenges, blinding factors) must use the curve order n, NOT the field prime p. Using p instead of n breaks Schnorr signature security and can lead to forgery attacks.

Proof System

Every encrypted operation requires zero-knowledge proofs verified on-chain. These prove the operation is valid without revealing the plaintext amounts.

Schnorr Proof of Ownership

Proves knowledge of the private key sk without revealing it:

Prover:
  1. Choose random k
  2. Commitment: A = k · G
  3. Challenge: c = Hash(A || pk || domain)
  4. Response: s = k + c · sk  (mod n)

Verifier:
  Check: s · G == A + c · pk

Chaum-Pedersen Encryption Proof

Proves that a ciphertext (L, R) correctly encrypts a value under a given public key:

Statement: L = m·G + r·pk  AND  R = r·G

Prover (knows m, r):
  1. Random km, kr
  2. AL = km·G + kr·pk,  AR = kr·G
  3. c = Hash(AL, AR, L, R, pk)
  4. sm = km + c·m,  sr = kr + c·r

Verifier:
  Check: sm·G + sr·pk == AL + c·L
  Check: sr·G == AR + c·R

Same-Encryption Proof

Proves that multiple ciphertexts encrypt the same amount under different public keys. This is critical for confidential transfers where sender, receiver, and auditor must all receive the same value:

Statement: (L₁, R₁) under pk₁ and (L₂, R₂) under pk₂
           both encrypt the same message b

Prover:
  Shared response: sb = kb + c·b  (mod n)
  Per-key responses: sr₁ = kr₁ + c·r₁,  sr₂ = kr₂ + c·r₂

Verifier:
  Same sb used in BOTH checks ← proves same message!
3-Party Variant

The SameEncryption3Proof extends this to three parties (sender, receiver, auditor) and is used by PrivacyRouter for compliance-ready transfers. Domain separator: obelysk-same-enc-3-v1.

Range Proofs

Prove that an encrypted amount is within valid bounds (0 to 2^64) without revealing the value. This prevents negative balances and overflow attacks.

Where ElGamal Is Used

ContractHow ElGamal Is Used
ConfidentialTransferAll balances stored as ElGamal ciphertexts. 6 proofs per transfer.
PrivacyRouterMulti-asset encrypted balances with epoch-based settlement.
DarkPoolBalance commitments and amount proofs for sealed-bid auctions.
ShieldedSwapBalance proofs for privacy pool interactions.
StealthRegistryEncrypted amount in payment announcements.

Security Properties

🔒
Semantic Security
Under the Decisional Diffie-Hellman (DDH) assumption on the STARK curve, ciphertexts are indistinguishable from random.
🧮
Homomorphic Updates
Balances can be added/subtracted on-chain without decryption. The contract never sees plaintext amounts.
🔑
No Trusted Setup
All proofs use transparent Fiat-Shamir hashing (Poseidon). No ceremony, no toxic waste, no MPC.
Public Verifiability
Anyone can verify that a proof is valid by checking the Schnorr equations on-chain. No special keys needed.

Next Steps