The demo below simulates the four core IBE operations in a simplified, illustrative model (real pairings use 256-bit fields; here we use tiny numbers so you can follow along).
<!-- Identity-Based Encryption — simplified educational demo -->
<div class="panel">
<h3>Step 1 — PKG Setup</h3>
<div class="field-row">
<label>Master secret <em>(s)</em></label>
<input id="masterSecret" type="number" min="2" max="20" value="7" />
<button id="btnSetup" type="button">Run Setup</button>
</div>
<div id="setupOut" class="output hidden"></div>
</div>
<div class="panel">
<h3>Step 2 — Key Extraction</h3>
<div class="field-row">
<label>Identity (recipient)</label>
<input id="identity" type="text" value="alice@example.com" placeholder="e.g. alice@example.com" />
<button id="btnExtract" type="button">Extract Key</button>
</div>
<div id="extractOut" class="output hidden"></div>
</div>
<div class="panel">
<h3>Step 3 — Encrypt to identity</h3>
<div class="field-row">
<label>Message (1–22)</label>
<input id="message" type="number" min="1" max="22" value="10" placeholder="1 – 22" />
<button id="btnEncrypt" type="button">Encrypt</button>
</div>
<div id="encryptOut" class="output hidden"></div>
</div>
<div class="panel">
<h3>Step 4 — Decrypt with private key</h3>
<button id="btnDecrypt" type="button">Decrypt</button>
<div id="decryptOut" class="output hidden"></div>
</div>
/* IBE demo styles */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: .9rem; }
h3 { margin: 0 0 .45rem; font-size: .93rem; color: #1d3557; }
.panel { background: #f4f7fa; border: 1px solid #cdd9e3; border-radius: 10px;
padding: .7rem .9rem; margin-bottom: .6rem; }
.field-row { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
label { font-weight: 600; min-width: 7.5rem; }
input[type=text], input[type=number] { flex: 1; min-width: 0; padding: .32rem .5rem;
border: 1px solid #adb1b8; border-radius: 6px; font-size: .88rem; }
button { font: 600 .84rem system-ui, sans-serif; padding: .35rem .78rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 7px; cursor: pointer; white-space: nowrap; }
.output { margin-top: .5rem; background: #fff; border: 1px solid #cdd9e3;
border-radius: 7px; padding: .45rem .7rem; font-size: .82rem;
line-height: 1.65; font-family: ui-monospace, monospace; }
.output.ok { border-left: 3px solid #0a7d33; }
.output.bad { border-left: 3px solid #c92f3c; color: #c92f3c; }
.hidden { display: none; }
.kv { display: grid; grid-template-columns: auto 1fr; gap: .12rem .55rem; }
.kv b { color: #1d3557; white-space: nowrap; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// IBE toy demo: Setup -> Extract -> Encrypt -> Decrypt
// Uses a tiny prime p=23 and discrete-log pairing stand-in so the math stays visible.
// Tiny prime — real IBE uses 256-bit fields
const P = 23;
// Generator of the multiplicative group mod p
const G = 5;
// Hash any identity string to an integer in [1, p-1]
function hashToInt(str) {
let h = 0;
for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) % (P - 1) + 1;
return h;
}
// Fast modular exponentiation
function modpow(base, exp, mod) {
let result = 1; base = base % mod;
while (exp > 0) {
if (exp % 2 === 1) result = result * base % mod;
exp = Math.floor(exp / 2); base = base * base % mod;
}
return result;
}
// Toy bilinear pairing: e(a, b) = g^(a*b) mod p
// In real IBE this is the Weil or Tate pairing on an elliptic curve
function pairing(a, b) {
return modpow(G, a * b % (P - 1), P);
}
// Modular multiplicative inverse by brute force (fine for p=23)
function modinv(a, mod) {
for (let i = 1; i < mod; i++) if (a * i % mod === 1) return i;
return 1;
}
// Shared state across the four IBE steps
let s = 0, hID = 0, dIDexp = 0, storedR = 0, storedV = 0;
function show(id, html, cls) {
const el = document.getElementById(id);
el.innerHTML = html;
el.className = 'output ' + (cls || '');
}
// Step 1 — PKG Setup: choose master secret s, publish g^s
document.getElementById('btnSetup').addEventListener('click', function () {
const v = parseInt(document.getElementById('masterSecret').value, 10);
if (isNaN(v) || v < 2 || v > 20) { show('setupOut', 'Master secret must be between 2 and 20.', 'bad'); return; }
s = v;
hID = 0; dIDexp = 0; storedR = 0; storedV = 0;
['extractOut','encryptOut','decryptOut'].forEach(function(id){ document.getElementById(id).className='output hidden'; });
const Ppub = modpow(G, s, P);
show('setupOut',
fmt('<div class="kv"><b>p</b><span>%P% (toy field size)</span><b>g</b><span>%G% (group generator)</span><b>s</b><span>%s% <em>kept secret by PKG</em></span><b>P<sub>pub</sub> = g<sup>s</sup></b><span>%Ppub%</span></div><br><em>Public parameters (p, g, P_pub) are broadcast to everyone.</em>', { P: P, G: G, s: s, Ppub: Ppub }), 'ok');
});
// Step 2 — Key Extraction: PKG derives private key for identity
document.getElementById('btnExtract').addEventListener('click', function () {
if (!s) { show('extractOut', 'Run PKG Setup first.', 'bad'); return; }
const id = document.getElementById('identity').value.trim();
if (!id) { show('extractOut', 'Please enter an identity string.', 'bad'); return; }
hID = hashToInt(id);
// Private key exponent: dID_exp = s * H(id) mod (p-1)
dIDexp = s * hID % (P - 1);
const dIDpub = modpow(G, dIDexp, P);
storedR = 0; storedV = 0;
document.getElementById('encryptOut').className = 'output hidden';
document.getElementById('decryptOut').className = 'output hidden';
show('extractOut',
fmt('<div class="kv"><b>Identity</b><span>%id%</span><b>H(id)</b><span>%hID%</span><b>Key exponent</b><span>s · H(id) mod (p-1) = %dIDexp%</span><b>Private key d_ID</b><span>g<sup>%dIDexp%</sup> mod p = %dIDpub%</span></div><br><em>The PKG issues d_ID only to the authenticated identity holder.</em>', { id: id, hID: hID, dIDexp: dIDexp, dIDpub: dIDpub }), 'ok');
});
// Step 3 — Encrypt using identity as public key
document.getElementById('btnEncrypt').addEventListener('click', function () {
if (!dIDexp) { show('encryptOut', 'Run Key Extraction first.', 'bad'); return; }
const m = parseInt(document.getElementById('message').value, 10);
if (isNaN(m) || m < 1 || m > P - 1) { show('encryptOut', fmt('Message must be a number between 1 and %v%', { v: P - 1 }), 'bad'); return; }
// Pick a fresh ephemeral r for this encryption
storedR = 2 + Math.floor(Math.random() * (P - 3));
// Encrypt mask = pairing(H(id)*s, r) = g^(H(id)*s*r); sender uses s as 'Ppub' in toy
const maskExp = hID * s % (P - 1) * storedR % (P - 1);
const mask = modpow(G, maskExp, P);
const U = modpow(G, storedR, P);
storedV = m * mask % P;
show('encryptOut',
fmt('<div class="kv"><b>m</b><span>%m%</span><b>r</b><span>%storedR% <em>chosen fresh each time</em></span><b>Pairing mask</b><span>g<sup>H(id)·s·r</sup> mod p = %mask%</span><b>U = g<sup>r</sup></b><span>%U%</span><b>V = m·mask</b><span>%storedV%</span></div><br><em>Ciphertext (U, V) is sent to the recipient.</em>', { m: m, storedR: storedR, mask: mask, U: U, storedV: storedV }), 'ok');
});
// Step 4 — Decrypt using private key
document.getElementById('btnDecrypt').addEventListener('click', function () {
if (!storedR || !storedV) { show('decryptOut', 'Encrypt a message first.', 'bad'); return; }
// Decryption: recover mask = pairing(dID_exp, r) = g^(s*H(id)*r), then m = V / mask
// This equals the encryption mask because s*H(id)*r == H(id)*s*r
const maskDec = pairing(dIDexp, storedR);
const recovered = storedV * modinv(maskDec, P) % P;
show('decryptOut',
fmt('<div class="kv"><b>V received</b><span>%storedV%</span><b>Decryption mask</b><span>g<sup>d<sub>ID</sub>·r</sup> mod p = %maskDec%</span><b>Recovered m</b><span>%recovered%</span></div><br><strong>Decrypted: %recovered% ✓</strong>', { storedV: storedV, maskDec: maskDec, recovered: recovered }), 'ok');
});
<!-- Identity-Based Encryption — simplified educational demo -->
<div class="panel">
<h3>Step 1 — PKG Setup</h3>
<div class="field-row">
<label>Master secret <em>(s)</em></label>
<input id="masterSecret" type="number" min="2" max="20" value="7" />
<button id="btnSetup" type="button">Run Setup</button>
</div>
<div id="setupOut" class="output hidden"></div>
</div>
<div class="panel">
<h3>Step 2 — Key Extraction</h3>
<div class="field-row">
<label>Identity (recipient)</label>
<input id="identity" type="text" value="alice@example.com" placeholder="e.g. alice@example.com" />
<button id="btnExtract" type="button">Extract Key</button>
</div>
<div id="extractOut" class="output hidden"></div>
</div>
<div class="panel">
<h3>Step 3 — Encrypt to identity</h3>
<div class="field-row">
<label>Message (1–22)</label>
<input id="message" type="number" min="1" max="22" value="10" placeholder="1 – 22" />
<button id="btnEncrypt" type="button">Encrypt</button>
</div>
<div id="encryptOut" class="output hidden"></div>
</div>
<div class="panel">
<h3>Step 4 — Decrypt with private key</h3>
<button id="btnDecrypt" type="button">Decrypt</button>
<div id="decryptOut" class="output hidden"></div>
</div>
/* IBE demo styles */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: .9rem; }
h3 { margin: 0 0 .45rem; font-size: .93rem; color: #1d3557; }
.panel { background: #f4f7fa; border: 1px solid #cdd9e3; border-radius: 10px;
padding: .7rem .9rem; margin-bottom: .6rem; }
.field-row { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
label { font-weight: 600; min-width: 7.5rem; }
input[type=text], input[type=number] { flex: 1; min-width: 0; padding: .32rem .5rem;
border: 1px solid #adb1b8; border-radius: 6px; font-size: .88rem; }
button { font: 600 .84rem system-ui, sans-serif; padding: .35rem .78rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 7px; cursor: pointer; white-space: nowrap; }
.output { margin-top: .5rem; background: #fff; border: 1px solid #cdd9e3;
border-radius: 7px; padding: .45rem .7rem; font-size: .82rem;
line-height: 1.65; font-family: ui-monospace, monospace; }
.output.ok { border-left: 3px solid #0a7d33; }
.output.bad { border-left: 3px solid #c92f3c; color: #c92f3c; }
.hidden { display: none; }
.kv { display: grid; grid-template-columns: auto 1fr; gap: .12rem .55rem; }
.kv b { color: #1d3557; white-space: nowrap; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// IBE toy demo: Setup -> Extract -> Encrypt -> Decrypt
// Uses a tiny prime p=23 and discrete-log pairing stand-in so the math stays visible.
// Tiny prime — real IBE uses 256-bit fields
const P = 23;
// Generator of the multiplicative group mod p
const G = 5;
// Hash any identity string to an integer in [1, p-1]
function hashToInt(str) {
let h = 0;
for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) % (P - 1) + 1;
return h;
}
// Fast modular exponentiation
function modpow(base, exp, mod) {
let result = 1; base = base % mod;
while (exp > 0) {
if (exp % 2 === 1) result = result * base % mod;
exp = Math.floor(exp / 2); base = base * base % mod;
}
return result;
}
// Toy bilinear pairing: e(a, b) = g^(a*b) mod p
// In real IBE this is the Weil or Tate pairing on an elliptic curve
function pairing(a, b) {
return modpow(G, a * b % (P - 1), P);
}
// Modular multiplicative inverse by brute force (fine for p=23)
function modinv(a, mod) {
for (let i = 1; i < mod; i++) if (a * i % mod === 1) return i;
return 1;
}
// Shared state across the four IBE steps
let s = 0, hID = 0, dIDexp = 0, storedR = 0, storedV = 0;
function show(id, html, cls) {
const el = document.getElementById(id);
el.innerHTML = html;
el.className = 'output ' + (cls || '');
}
// Step 1 — PKG Setup: choose master secret s, publish g^s
document.getElementById('btnSetup').addEventListener('click', function () {
const v = parseInt(document.getElementById('masterSecret').value, 10);
if (isNaN(v) || v < 2 || v > 20) { show('setupOut', 'Master secret must be between 2 and 20.', 'bad'); return; }
s = v;
hID = 0; dIDexp = 0; storedR = 0; storedV = 0;
['extractOut','encryptOut','decryptOut'].forEach(function(id){ document.getElementById(id).className='output hidden'; });
const Ppub = modpow(G, s, P);
show('setupOut',
fmt('<div class="kv"><b>p</b><span>%P% (toy field size)</span><b>g</b><span>%G% (group generator)</span><b>s</b><span>%s% <em>kept secret by PKG</em></span><b>P<sub>pub</sub> = g<sup>s</sup></b><span>%Ppub%</span></div><br><em>Public parameters (p, g, P_pub) are broadcast to everyone.</em>', { P: P, G: G, s: s, Ppub: Ppub }), 'ok');
});
// Step 2 — Key Extraction: PKG derives private key for identity
document.getElementById('btnExtract').addEventListener('click', function () {
if (!s) { show('extractOut', 'Run PKG Setup first.', 'bad'); return; }
const id = document.getElementById('identity').value.trim();
if (!id) { show('extractOut', 'Please enter an identity string.', 'bad'); return; }
hID = hashToInt(id);
// Private key exponent: dID_exp = s * H(id) mod (p-1)
dIDexp = s * hID % (P - 1);
const dIDpub = modpow(G, dIDexp, P);
storedR = 0; storedV = 0;
document.getElementById('encryptOut').className = 'output hidden';
document.getElementById('decryptOut').className = 'output hidden';
show('extractOut',
fmt('<div class="kv"><b>Identity</b><span>%id%</span><b>H(id)</b><span>%hID%</span><b>Key exponent</b><span>s · H(id) mod (p-1) = %dIDexp%</span><b>Private key d_ID</b><span>g<sup>%dIDexp%</sup> mod p = %dIDpub%</span></div><br><em>The PKG issues d_ID only to the authenticated identity holder.</em>', { id: id, hID: hID, dIDexp: dIDexp, dIDpub: dIDpub }), 'ok');
});
// Step 3 — Encrypt using identity as public key
document.getElementById('btnEncrypt').addEventListener('click', function () {
if (!dIDexp) { show('encryptOut', 'Run Key Extraction first.', 'bad'); return; }
const m = parseInt(document.getElementById('message').value, 10);
if (isNaN(m) || m < 1 || m > P - 1) { show('encryptOut', fmt('Message must be a number between 1 and %v%', { v: P - 1 }), 'bad'); return; }
// Pick a fresh ephemeral r for this encryption
storedR = 2 + Math.floor(Math.random() * (P - 3));
// Encrypt mask = pairing(H(id)*s, r) = g^(H(id)*s*r); sender uses s as 'Ppub' in toy
const maskExp = hID * s % (P - 1) * storedR % (P - 1);
const mask = modpow(G, maskExp, P);
const U = modpow(G, storedR, P);
storedV = m * mask % P;
show('encryptOut',
fmt('<div class="kv"><b>m</b><span>%m%</span><b>r</b><span>%storedR% <em>chosen fresh each time</em></span><b>Pairing mask</b><span>g<sup>H(id)·s·r</sup> mod p = %mask%</span><b>U = g<sup>r</sup></b><span>%U%</span><b>V = m·mask</b><span>%storedV%</span></div><br><em>Ciphertext (U, V) is sent to the recipient.</em>', { m: m, storedR: storedR, mask: mask, U: U, storedV: storedV }), 'ok');
});
// Step 4 — Decrypt using private key
document.getElementById('btnDecrypt').addEventListener('click', function () {
if (!storedR || !storedV) { show('decryptOut', 'Encrypt a message first.', 'bad'); return; }
// Decryption: recover mask = pairing(dID_exp, r) = g^(s*H(id)*r), then m = V / mask
// This equals the encryption mask because s*H(id)*r == H(id)*s*r
const maskDec = pairing(dIDexp, storedR);
const recovered = storedV * modinv(maskDec, P) % P;
show('decryptOut',
fmt('<div class="kv"><b>V received</b><span>%storedV%</span><b>Decryption mask</b><span>g<sup>d<sub>ID</sub>·r</sup> mod p = %maskDec%</span><b>Recovered m</b><span>%recovered%</span></div><br><strong>Decrypted: %recovered% ✓</strong>', { storedV: storedV, maskDec: maskDec, recovered: recovered }), 'ok');
});
Comments
Loading comments...