Below, Alice and Bob agree on a shared secret using only public messages. They both start from agreed public numbers: a base g and a prime modulus p. Each secretly picks a private number, then they swap mixtures — never the secrets themselves. Pick the private numbers and step through it.
<p class="hint">Alice and Bob share a public base <b>g</b> and prime modulus <b>p</b>. Each secretly picks a private number, they swap public mixtures, and both arrive at the <b>same</b> shared secret — while Eve, who sees everything on the wire, cannot.</p>
<div class="params">
<label>g = <input id="g" type="number" min="2" max="20" value="5"></label>
<label>p = <input id="p" type="number" min="3" max="9973" value="23"></label>
</div>
<div class="lanes">
<div class="lane alice">
<h3>Alice</h3>
<label class="priv">private a = <input id="a" type="number" min="1" max="9972" value="6"></label>
<div class="calc" id="calcA">A = g<sup>a</sup> mod p = ?</div>
<div class="secret" id="secA">secret = B<sup>a</sup> mod p = ?</div>
</div>
<div class="lane eve">
<h3>Eve (eavesdropper)</h3>
<div class="wire" id="wireA">A = ?</div>
<div class="wire" id="wireB">B = ?</div>
<div class="evenote" id="eveNote">sees g, p, A, B — must solve the discrete log to learn the secret.</div>
</div>
<div class="lane bob">
<h3>Bob</h3>
<label class="priv">private b = <input id="b" type="number" min="1" max="9972" value="15"></label>
<div class="calc" id="calcB">B = g<sup>b</sup> mod p = ?</div>
<div class="secret" id="secB">secret = A<sup>b</sup> mod p = ?</div>
</div>
</div>
<div class="status" id="status">Pick numbers, then run the exchange.</div>
<div class="btns">
<button id="run" type="button">Run the exchange</button>
<button id="crack" type="button" class="ghost">Eve: brute-force the secret</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.params { display: flex; gap: 1rem; margin: .3rem 0 .8rem; flex-wrap: wrap; }
label { font-size: .9rem; font-weight: 600; }
input { width: 64px; font: 600 14px ui-monospace, monospace; padding: .25rem .4rem;
border: 1px solid #adb1b8; border-radius: 6px; }
.lanes { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: .6rem; }
.lane { border: 1px solid #cdd9e3; border-radius: 10px; padding: .6rem; background: #f4f8fb; }
.lane h3 { margin: 0 0 .5rem; font-size: 1rem; }
.alice h3 { color: #1d6fb8; }
.bob h3 { color: #0a7d33; }
.eve { background: #fbf0f0; border-color: #e3cdcd; }
.eve h3 { color: #c92f3c; }
.priv { display: block; margin-bottom: .5rem; }
.calc, .secret, .wire { font: 600 13px ui-monospace, monospace; margin: .35rem 0;
background: #fff; border: 1px solid #dde6ee; border-radius: 6px; padding: .35rem .45rem; }
.secret { background: #e9f7ee; border-color: #b8e3c8; }
.secret.match { background: #d4f0de; }
.wire { background: #fff; border-color: #ecd6d6; }
.evenote { font-size: .78rem; color: #944; margin-top: .4rem; line-height: 1.35; }
.status { font-size: 1rem; font-weight: 700; margin: .7rem 0 .4rem; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #c92f3c; border-color: #c92f3c; }
@media (max-width: 560px) { .lanes { grid-template-columns: 1fr; } }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Diffie-Hellman key exchange. Public: base g and prime modulus p.
// Alice's private a -> A = g^a mod p ; Bob's private b -> B = g^b mod p.
// Shared secret: Alice computes B^a mod p, Bob computes A^b mod p.
// Both equal g^(ab) mod p. Eve sees g, p, A, B but must solve the
// DISCRETE LOGARITHM (find a from A = g^a mod p) to recover the secret —
// easy to exponentiate, hard to invert.
function modpow(base, exp, mod) {
base %= mod; let r = 1n;
let b = BigInt(base), e = BigInt(exp), m = BigInt(mod);
while (e > 0n) { if (e & 1n) r = (r * b) % m; b = (b * b) % m; e >>= 1n; }
return Number(r);
}
const $ = id => document.getElementById(id);
function setStatus(t, cls){ const s = $('status'); s.textContent = t; s.className = 'status ' + cls; }
function run() {
const g = +$('g').value, p = +$('p').value, a = +$('a').value, b = +$('b').value;
const A = modpow(g, a, p); // Alice's public value
const B = modpow(g, b, p); // Bob's public value
const sAlice = modpow(B, a, p); // Alice's computed secret = B^a mod p
const sBob = modpow(A, b, p); // Bob's computed secret = A^b mod p
$('calcA').innerHTML = 'A = g<sup>a</sup> mod p = ' + A;
$('calcB').innerHTML = 'B = g<sup>b</sup> mod p = ' + B;
$('wireA').textContent = 'A = ' + A;
$('wireB').textContent = 'B = ' + B;
$('secA').innerHTML = 'secret = B<sup>a</sup> mod p = ' + sAlice;
$('secB').innerHTML = 'secret = A<sup>b</sup> mod p = ' + sBob;
const ok = sAlice === sBob;
$('secA').classList.toggle('match', ok);
$('secB').classList.toggle('match', ok);
if (ok) setStatus(fmt('Both sides agree on the shared secret %sAlice% — Eve never saw it.', { sAlice: sAlice }), 'ok');
else setStatus('Secrets differ — check that p is prime and inputs are valid.', 'bad');
console.log(fmt('A=%A% B=%B% Alice secret=%sAlice% Bob secret=%sBob%', { A: A, B: B, sAlice: sAlice, sBob: sBob }));
}
// Eve's only route: try every private exponent until g^x mod p matches A,
// then use it. This brute force is trivial for tiny p but explodes with size.
function crack() {
const g = +$('g').value, p = +$('p').value;
const A = modpow(g, +$('a').value, p);
const b = +$('b').value, B = modpow(g, b, p);
let steps = 0, found = -1;
for (let x = 1; x < p; x++) { steps++; if (modpow(g, x, p) === A) { found = x; break; } }
if (found >= 0) {
const secret = modpow(B, found, p);
setStatus(fmt('Eve cracked it in %steps% tries: a = %found%, secret = %secret%. With a 2048-bit p this loop would outlast the universe.', { steps: steps, found: found, secret: secret }), 'bad');
} else setStatus('Eve found no exponent (bad parameters).', 'bad');
}
$('run').addEventListener('click', run);
$('crack').addEventListener('click', crack);
run();
<p class="hint">Alice and Bob share a public base <b>g</b> and prime modulus <b>p</b>. Each secretly picks a private number, they swap public mixtures, and both arrive at the <b>same</b> shared secret — while Eve, who sees everything on the wire, cannot.</p>
<div class="params">
<label>g = <input id="g" type="number" min="2" max="20" value="5"></label>
<label>p = <input id="p" type="number" min="3" max="9973" value="23"></label>
</div>
<div class="lanes">
<div class="lane alice">
<h3>Alice</h3>
<label class="priv">private a = <input id="a" type="number" min="1" max="9972" value="6"></label>
<div class="calc" id="calcA">A = g<sup>a</sup> mod p = ?</div>
<div class="secret" id="secA">secret = B<sup>a</sup> mod p = ?</div>
</div>
<div class="lane eve">
<h3>Eve (eavesdropper)</h3>
<div class="wire" id="wireA">A = ?</div>
<div class="wire" id="wireB">B = ?</div>
<div class="evenote" id="eveNote">sees g, p, A, B — must solve the discrete log to learn the secret.</div>
</div>
<div class="lane bob">
<h3>Bob</h3>
<label class="priv">private b = <input id="b" type="number" min="1" max="9972" value="15"></label>
<div class="calc" id="calcB">B = g<sup>b</sup> mod p = ?</div>
<div class="secret" id="secB">secret = A<sup>b</sup> mod p = ?</div>
</div>
</div>
<div class="status" id="status">Pick numbers, then run the exchange.</div>
<div class="btns">
<button id="run" type="button">Run the exchange</button>
<button id="crack" type="button" class="ghost">Eve: brute-force the secret</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.params { display: flex; gap: 1rem; margin: .3rem 0 .8rem; flex-wrap: wrap; }
label { font-size: .9rem; font-weight: 600; }
input { width: 64px; font: 600 14px ui-monospace, monospace; padding: .25rem .4rem;
border: 1px solid #adb1b8; border-radius: 6px; }
.lanes { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: .6rem; }
.lane { border: 1px solid #cdd9e3; border-radius: 10px; padding: .6rem; background: #f4f8fb; }
.lane h3 { margin: 0 0 .5rem; font-size: 1rem; }
.alice h3 { color: #1d6fb8; }
.bob h3 { color: #0a7d33; }
.eve { background: #fbf0f0; border-color: #e3cdcd; }
.eve h3 { color: #c92f3c; }
.priv { display: block; margin-bottom: .5rem; }
.calc, .secret, .wire { font: 600 13px ui-monospace, monospace; margin: .35rem 0;
background: #fff; border: 1px solid #dde6ee; border-radius: 6px; padding: .35rem .45rem; }
.secret { background: #e9f7ee; border-color: #b8e3c8; }
.secret.match { background: #d4f0de; }
.wire { background: #fff; border-color: #ecd6d6; }
.evenote { font-size: .78rem; color: #944; margin-top: .4rem; line-height: 1.35; }
.status { font-size: 1rem; font-weight: 700; margin: .7rem 0 .4rem; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #c92f3c; border-color: #c92f3c; }
@media (max-width: 560px) { .lanes { grid-template-columns: 1fr; } }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Diffie-Hellman key exchange. Public: base g and prime modulus p.
// Alice's private a -> A = g^a mod p ; Bob's private b -> B = g^b mod p.
// Shared secret: Alice computes B^a mod p, Bob computes A^b mod p.
// Both equal g^(ab) mod p. Eve sees g, p, A, B but must solve the
// DISCRETE LOGARITHM (find a from A = g^a mod p) to recover the secret —
// easy to exponentiate, hard to invert.
function modpow(base, exp, mod) {
base %= mod; let r = 1n;
let b = BigInt(base), e = BigInt(exp), m = BigInt(mod);
while (e > 0n) { if (e & 1n) r = (r * b) % m; b = (b * b) % m; e >>= 1n; }
return Number(r);
}
const $ = id => document.getElementById(id);
function setStatus(t, cls){ const s = $('status'); s.textContent = t; s.className = 'status ' + cls; }
function run() {
const g = +$('g').value, p = +$('p').value, a = +$('a').value, b = +$('b').value;
const A = modpow(g, a, p); // Alice's public value
const B = modpow(g, b, p); // Bob's public value
const sAlice = modpow(B, a, p); // Alice's computed secret = B^a mod p
const sBob = modpow(A, b, p); // Bob's computed secret = A^b mod p
$('calcA').innerHTML = 'A = g<sup>a</sup> mod p = ' + A;
$('calcB').innerHTML = 'B = g<sup>b</sup> mod p = ' + B;
$('wireA').textContent = 'A = ' + A;
$('wireB').textContent = 'B = ' + B;
$('secA').innerHTML = 'secret = B<sup>a</sup> mod p = ' + sAlice;
$('secB').innerHTML = 'secret = A<sup>b</sup> mod p = ' + sBob;
const ok = sAlice === sBob;
$('secA').classList.toggle('match', ok);
$('secB').classList.toggle('match', ok);
if (ok) setStatus(fmt('Both sides agree on the shared secret %sAlice% — Eve never saw it.', { sAlice: sAlice }), 'ok');
else setStatus('Secrets differ — check that p is prime and inputs are valid.', 'bad');
console.log(fmt('A=%A% B=%B% Alice secret=%sAlice% Bob secret=%sBob%', { A: A, B: B, sAlice: sAlice, sBob: sBob }));
}
// Eve's only route: try every private exponent until g^x mod p matches A,
// then use it. This brute force is trivial for tiny p but explodes with size.
function crack() {
const g = +$('g').value, p = +$('p').value;
const A = modpow(g, +$('a').value, p);
const b = +$('b').value, B = modpow(g, b, p);
let steps = 0, found = -1;
for (let x = 1; x < p; x++) { steps++; if (modpow(g, x, p) === A) { found = x; break; } }
if (found >= 0) {
const secret = modpow(B, found, p);
setStatus(fmt('Eve cracked it in %steps% tries: a = %found%, secret = %secret%. With a 2048-bit p this loop would outlast the universe.', { steps: steps, found: found, secret: secret }), 'bad');
} else setStatus('Eve found no exponent (bad parameters).', 'bad');
}
$('run').addEventListener('click', run);
$('crack').addEventListener('click', crack);
run();
Watch the asymmetry. Alice sends A = gᵃ mod p and Bob sends B = gᵇ mod p in the clear. Alice computes Bᵃ mod p, Bob computes Aᵇ mod p, and both land on the same value gᵃᵇ mod p — their shared secret. Eve sees g, p, A and B, but to find the secret she would have to recover a private exponent from gᵃ mod p. That is the discrete logarithm: easy to raise a number to a power, fiendishly hard to climb back down.
Comments
Loading comments...