{"_id":"@claucondor/elgamal","name":"@claucondor/elgamal","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@claucondor/elgamal","version":"0.1.0","description":"Homomorphic ElGamal encryption on BabyJubjub — additive privacy primitives for Flow EVM","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"}},"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts --clean","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint":"eslint src tests scripts","precompute-bsgs":"npx tsx scripts/precompute-bsgs.ts","precompute-bsgs:48":"npx tsx scripts/precompute-bsgs.ts --bits=48","generate-setup":"npx tsx scripts/generate-setup.ts","deploy":"npx tsx scripts/deploy.ts"},"dependencies":{"circomlibjs":"^0.1.7","snarkjs":"^0.7.6","ethers":"^6.13.4"},"devDependencies":{"@types/node":"^22.0.0","tsup":"^8.0.0","typescript":"^5.5.0","vitest":"^2.0.0","@vitest/coverage-v8":"^2.0.0","tsx":"^4.0.0"},"engines":{"node":">=18.0.0"},"keywords":["elgamal","babyjubjub","homomorphic","zk","groth16","privacy","flow","evm","circom"],"license":"MIT","author":{"name":"oydual3","email":"claucondor@gmail.com"},"repository":{"type":"git","url":"git+https://github.com/openjanus/primitives.git","directory":"packages/elgamal"},"_id":"@claucondor/elgamal@0.1.0","gitHead":"489e3291628e46b377d782ab115dd1b5749250d3","bugs":{"url":"https://github.com/openjanus/primitives/issues"},"homepage":"https://github.com/openjanus/primitives#readme","_nodeVersion":"22.20.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-JxpDd1snP5bJY4B0fFQe8xa0F8t/5Q8AGsJkXcw7C+QlvGHLCfkFtjOTf1UCGQfigl3zoojjUnwC6GiII3M8eg==","shasum":"7ba0e56248980f033eb35d5e6e18bf51405e6cff","tarball":"https://registry.npmjs.org/@claucondor/elgamal/-/elgamal-0.1.0.tgz","fileCount":14,"unpackedSize":104516,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCQKzmCc2xdsEuEjNT9r3BYpbLoCQ1tDdUfkI+C1lCoQAIhAK1psPHKYSIOKDc66dCKp2gN0vHMqPeqjEEJWnqXdzJf"}]},"_npmUser":{"name":"oydual31","email":"claucondor@gmail.com"},"directories":{},"maintainers":[{"name":"oydual31","email":"claucondor@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/elgamal_0.1.0_1781551665602_0.9425848322854167"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-15T19:27:45.498Z","0.1.0":"2026-06-15T19:27:45.756Z","modified":"2026-06-15T19:27:45.963Z"},"maintainers":[{"name":"oydual31","email":"claucondor@gmail.com"}],"description":"Homomorphic ElGamal encryption on BabyJubjub — additive privacy primitives for Flow EVM","homepage":"https://github.com/openjanus/primitives#readme","keywords":["elgamal","babyjubjub","homomorphic","zk","groth16","privacy","flow","evm","circom"],"repository":{"type":"git","url":"git+https://github.com/openjanus/primitives.git","directory":"packages/elgamal"},"author":{"name":"oydual3","email":"claucondor@gmail.com"},"bugs":{"url":"https://github.com/openjanus/primitives/issues"},"license":"MIT","readme":"# @claucondor/elgamal\n\n**Homomorphic ElGamal encryption on BabyJubjub** — additive privacy primitives for Flow EVM. Part of the openjanus/primitives monorepo.\n\n## What it is\n\nExponential ElGamal encryption over the BabyJubjub elliptic curve, with additive homomorphism and Groth16 zero-knowledge proofs. A recipient can register a public key on-chain; senders can encrypt individual values to that key; the on-chain accumulator homomorphically adds all contributions; the recipient decrypts the total using only their private key — without learning any individual sender amount.\n\nThe \"recipient knows total only\" privacy property was validated end-to-end on Flow EVM testnet (Phase 1, 2026-05-25): 3 senders submitted E(10), E(25), E(7) — Bob decrypted the accumulated ciphertext to 42 without access to per-sender randomness.\n\n## When to use\n\nUse this package when you need:\n- **Additive privacy**: multiple parties contribute encrypted amounts; the recipient learns only the total\n- **On-chain accumulation**: homomorphic addition without decrypting in the contract\n- **ZK-verified decryption**: recipient proves correct opening without revealing private key\n- **Flow EVM integration**: works with the deployed BabyJub.sol precompile\n\nDo NOT use for:\n- General-purpose public-key encryption (use ElGamal on secp256k1 or NaCl box instead)\n- Amounts requiring more than 2^32 range (needs BSGS precompute with `--bits=48`)\n- Post-quantum security (based on DDH — see Trust Assumptions)\n\n## Quick Install and Usage\n\n```bash\nnpm install @claucondor/elgamal\n```\n\n```typescript\nimport { encrypt, decrypt, add, deriveFromFlowKey, warmupDecrypt, randomScalar } from \"@claucondor/elgamal\";\n\n// One-time startup: build BSGS table (covers [0, 2^32))\nawait warmupDecrypt();\n\n// Keypair derivation from a Flow signing key\nconst bob = await deriveFromFlowKey(myFlowSigningKeyBuffer);\n\n// Alice encrypts 42 to Bob's pubkey\nconst r = await randomScalar();\nconst ct = await encrypt(42n, r, bob.pubkey);\n\n// Multiple senders: homomorphic accumulation\nconst ct_alice = await encrypt(10n, await randomScalar(), bob.pubkey);\nconst ct_carol = await encrypt(25n, await randomScalar(), bob.pubkey);\nconst ct_dave  = await encrypt(7n,  await randomScalar(), bob.pubkey);\nconst accumulated = await add(await add(ct_alice, ct_carol), ct_dave);\n\n// Bob decrypts with his private key — sees 42, not 10+25+7\nconst total = await decrypt(accumulated, bob.privkey);\nconsole.log(total); // 42n\n```\n\n## Cryptographic Guarantees\n\n| Property | Status | Notes |\n|----------|--------|-------|\n| IND-CPA | YES | Under DDH assumption on BabyJubjub |\n| Additive homomorphism | YES | `add(E(a), E(b)) = E(a+b)` |\n| Sender privacy | YES | Accumulated ciphertext is indistinguishable from fresh `E(sum)` |\n| ZK-verified decryption | YES | Groth16 circuit proves correct opening |\n| Post-quantum | NO | Based on elliptic curve DL — broken by Shor's algorithm |\n\n## Trust Assumptions\n\n1. **DDH hardness** on BabyJubjub: no efficient algorithm exists to distinguish `g^a, g^b, g^(ab)` from random.\n2. **Trusted setup** for Groth16 circuits: `encrypt_consistency.zkey` and `decrypt_open.zkey`. For production, use the Hermez ceremony output + Flow block hash beacon (see `circuits/README.md`). For development, the lab setup (single-contributor pot14) is testnet-grade only.\n3. **BSGS range**: values must be in `[0, 2^32)` with default settings, or `[0, 2^48)` with the precomputed disk table.\n\n## Performance\n\n| Operation | Time | Notes |\n|-----------|------|-------|\n| Encrypt | ~5ms | Point scalar multiplications |\n| BSGS warmup (2^32) | ~500ms | Builds 65536-entry table, once per process |\n| BSGS warmup (2^48) | ~30s | 16.7M entries — use disk cache |\n| Decrypt | <5ms | After warmup |\n| Prove (decrypt_open) | ~1.4s | snarkjs Groth16, WASM witness |\n| Verify off-chain | ~50ms | snarkjs |\n| Verify on-chain | ~45k gas | EVM Groth16 pairing check |\n| Accumulate on-chain (Flow EVM) | ~110k gas | Two BabyJub point additions |\n| Register pubkey (Flow EVM) | ~100k gas | One curve check + store |\n\nGas figures from Phase 1 testnet results (2026-05-25).\n\n## Deployed Contracts — v0.2.0 (Flow EVM Testnet, ceremony-backed)\n\nTrusted setup: Hermez pot14 (200+ contributors) + Flow VRF beacon\n(testnet block 323555648). E2E: 27/27 PASS (2026-05-26).\n\n| Contract | Address |\n|----------|---------|\n| BabyJub.sol | `0x27139AFda7425f51F68D32e0A38b7D43BcB0f870` |\n| JanusToken (ElGamal accumulator) | `0xb12E600fFcde967210cFD81CF9f32bBB6e68a499` |\n| EncryptConsistencyVerifier | `0x0C1e731036f4632CF9620bf6C6BB8204eD3a3B1e` |\n| DecryptOpenVerifier | `0x1c248dA94aab9f4A03005E7944a8b745a6236Dbc` |\n\n> **DEPRECATED — v0.1.0 addresses (single-contributor lab setup — DO NOT USE):**\n> EncryptConsistencyVerifier `0x6F8Cc93dd6aA7B3ED0a3DaA75271815558ad9b5C`,\n> DecryptOpenVerifier `0x3bB139B5404fD6b152813bC3532367AAa096638b`\n\n## BSGS Precompute (Production 2^48)\n\n```bash\n# Build and cache a 2^32 table (~1MB, <1s)\nnpm run precompute-bsgs\n\n# Build a 2^48 table (~600MB, ~30min) — full production spec\nnpm run precompute-bsgs:48\n\n# Use disk-cached table at runtime\nimport { getBabyJub, initFromDisk, decrypt } from \"@claucondor/elgamal\";\nconst babyjub = await getBabyJub();\ninitFromDisk(babyjub, \"./precompute/bsgs_table_48.bin\", 48);\nconst value = await decrypt(ct, privkey, 48);\n```\n\n## Open Issues for Phase 3 (Contracts)\n\n1. **Per-user COA pattern**: Phase 1 used a single shared COA as the on-chain recipient. Phase 3 must give each user their own Cadence Owned Account (COA) so `msg.sender` maps correctly per user.\n2. **Pubkey rotation**: The accumulator currently prevents re-registration (`hasPubkey` guard). Phase 3 needs a rotation mechanism (timelock + ZK proof of old key ownership).\n3. **Nonce/replay protection**: The `accumulate()` function accepts any ciphertext from any caller. Phase 3 must add nonce or commitment-reveal to prevent replay attacks.\n4. **ZK-gated accumulate**: Phase 1 deferred `encrypt_consistency` proof verification on `accumulate()`. Phase 3 should add `verifyProof` call in `accumulate()` to enforce well-formed inputs.\n5. **Production trusted setup**: Deploy new verifier contracts after running Hermez + beacon ceremony (see `circuits/README.md`).\n\nSee [ARCHITECTURE.md](../../ARCHITECTURE.md) for deeper cryptographic design notes.\n","readmeFilename":"README.md","_rev":"1-26ee60f0d23616fff55ba226da4a507d"}