Below is a simplified DPA experiment. A secret 8-bit key byte is hidden in the chip. The chip encrypts 200 random plaintexts and each encryption leaks a noisy power trace. Your job: run the correlation attack and watch it pick the correct key guess out of 256 candidates.
<!-- DPA demo — collect noisy power traces and recover the secret key byte via correlation -->
<p class="hint">A secret 8-bit key is hidden inside the chip. Slide the controls to choose how many power traces to collect and how much noise the chip adds. Press <b>Run Attack</b> — the chart plots the Pearson correlation for all 256 key guesses. The true key lights up in red.</p>
<div class="controls">
<label>Traces: <input id="nTraces" type="range" min="10" max="500" value="50" step="10"> <span id="nVal">50</span></label>
<label>Noise level: <input id="noiseLvl" type="range" min="0" max="5" value="2" step="1"> <span id="noiseVal">2</span></label>
</div>
<div class="btns">
<button id="btnRun" type="button">Run Attack</button>
<button id="btnReset" type="button" class="ghost">Reset</button>
</div>
<div class="status" id="status"></div>
<div class="chart-wrap">
<canvas id="chart" width="560" height="200"></canvas>
<div class="axis-label">Key guess (0–255)</div>
</div>
<div class="legend">
<span class="leg-true">■ true key (0xAB)</span>
<span class="leg-other">■ other guesses</span>
</div>
/* Layout and color for the DPA correlation demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
.controls { display: flex; flex-direction: column; gap: .35rem; margin-bottom: .5rem; font-size: .88rem; }
.controls label { display: flex; align-items: center; gap: .4rem; }
.controls input[type=range] { flex: 1; max-width: 200px; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .5rem; }
button { font: 600 14px system-ui, sans-serif; padding: .4rem .85rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.status { font-size: .95rem; font-weight: 600; min-height: 1.4em; margin-bottom: .3rem; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.chart-wrap { position: relative; width: 100%; overflow-x: auto; }
canvas { display: block; width: 100%; height: auto; border: 1px solid #dde3ea; border-radius: 8px; }
.axis-label { font-size: .8rem; color: #666; text-align: center; margin-top: .2rem; }
.legend { font-size: .82rem; margin-top: .3rem; display: flex; gap: 1rem; }
.leg-true { color: #e63946; }
.leg-other { color: #457b9d; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* Differential Power Analysis demo — Hamming-weight model + Pearson correlation */
/* AES S-box lookup table (256 bytes) */
const SBOX = [
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xe6,0x48,0x27,0x9b,0xf2,0x15,0x89,0x09,0x2f,0x36,0x1a,0x22,0x03,0xd5,0x6e,
0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb
];
/* Hamming weight = number of set bits in a byte */
function hw(b) {
let c = 0;
for (let x = b & 0xff; x; x &= x - 1) c++;
return c;
}
/* Simple linear-congruential RNG for reproducible traces */
let seed = 42;
function rand() { seed = (seed * 1664525 + 1013904223) & 0xffffffff; return (seed >>> 0) / 0xffffffff; }
function randn() {
/* Box-Muller transform produces normally distributed noise */
const u = rand() || 1e-10, v = rand();
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
}
/* The hidden key byte the attacker wants to recover */
const SECRET_KEY = 0xAB;
let traces = [], plaintexts = [];
/* Simulate N power traces; each = HW(SBOX[plaintext XOR key]) + Gaussian noise */
function collectTraces(n, noiseLevel) {
seed = 12345;
traces = []; plaintexts = [];
const noiseScale = [0, 0.4, 0.8, 1.5, 2.5, 4.0][noiseLevel];
for (let i = 0; i < n; i++) {
const p = Math.floor(rand() * 256);
const v = SBOX[(p ^ SECRET_KEY) & 0xff];
/* power model — true leakage is Hamming weight of the S-box output plus noise */
const power = hw(v) + noiseScale * randn();
plaintexts.push(p);
traces.push(power);
}
}
/* Pearson correlation between predicted power values and measured traces */
function pearson(xs, ys) {
const n = xs.length;
if (n === 0) return 0;
let mx = 0, my = 0;
for (let i = 0; i < n; i++) { mx += xs[i]; my += ys[i]; }
mx /= n; my /= n;
let num = 0, dx2 = 0, dy2 = 0;
for (let i = 0; i < n; i++) {
const dx = xs[i] - mx, dy = ys[i] - my;
num += dx * dy; dx2 += dx * dx; dy2 += dy * dy;
}
return (dx2 && dy2) ? num / Math.sqrt(dx2 * dy2) : 0;
}
/* For each key guess k, compute Pearson correlation between predicted and actual traces */
function runAttack() {
const corrs = new Float64Array(256);
for (let k = 0; k < 256; k++) {
const preds = plaintexts.map(p => hw(SBOX[(p ^ k) & 0xff]));
corrs[k] = pearson(preds, traces);
}
return corrs;
}
/* Draw correlation bar chart on the canvas */
function drawChart(corrs) {
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
ctx.clearRect(0, 0, W, H);
/* light background */
ctx.fillStyle = '#f8fafc';
ctx.fillRect(0, 0, W, H);
/* zero-correlation baseline */
ctx.strokeStyle = '#cdd3da';
ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(0, H / 2); ctx.lineTo(W, H / 2); ctx.stroke();
/* one bar per key guess, red for the true key */
const barW = W / 256;
const scale = H / 2 * 0.9;
for (let k = 0; k < 256; k++) {
const r = corrs[k];
const x = k * barW;
const h = Math.abs(r) * scale;
const y = r >= 0 ? H / 2 - h : H / 2;
ctx.fillStyle = k === SECRET_KEY ? '#e63946' : '#457b9d';
ctx.fillRect(x, y, Math.max(barW - 0.5, 1), h);
}
}
function run() {
const n = parseInt(document.getElementById('nTraces').value, 10);
const nl = parseInt(document.getElementById('noiseLvl').value, 10);
collectTraces(n, nl);
const corrs = runAttack();
drawChart(corrs);
/* find the key guess with the highest absolute correlation */
let best = 0, bestK = 0;
for (let k = 0; k < 256; k++) {
if (Math.abs(corrs[k]) > best) { best = Math.abs(corrs[k]); bestK = k; }
}
const st = document.getElementById('status');
if (bestK === SECRET_KEY) {
st.textContent = i18nFmt('Correct key 0x%k% found! Peak correlation: %r%', { k: '0x' + SECRET_KEY.toString(16).toUpperCase(), r: best.toFixed(3) });
st.className = 'status ok';
} else {
st.textContent = i18nFmt('Best guess was 0x%k% (r = %r%) — need more traces or less noise.', { k: '0x' + bestK.toString(16).toUpperCase(), r: best.toFixed(3) });
st.className = 'status bad';
}
}
function reset() {
traces = []; plaintexts = [];
const ctx = document.getElementById('chart').getContext('2d');
ctx.clearRect(0, 0, 560, 200);
document.getElementById('status').textContent = '';
document.getElementById('status').className = 'status';
}
document.getElementById('btnRun').addEventListener('click', run);
document.getElementById('btnReset').addEventListener('click', reset);
document.getElementById('nTraces').addEventListener('input', function () {
document.getElementById('nVal').textContent = this.value;
});
document.getElementById('noiseLvl').addEventListener('input', function () {
document.getElementById('noiseVal').textContent = this.value;
});
<!-- DPA demo — collect noisy power traces and recover the secret key byte via correlation -->
<p class="hint">A secret 8-bit key is hidden inside the chip. Slide the controls to choose how many power traces to collect and how much noise the chip adds. Press <b>Run Attack</b> — the chart plots the Pearson correlation for all 256 key guesses. The true key lights up in red.</p>
<div class="controls">
<label>Traces: <input id="nTraces" type="range" min="10" max="500" value="50" step="10"> <span id="nVal">50</span></label>
<label>Noise level: <input id="noiseLvl" type="range" min="0" max="5" value="2" step="1"> <span id="noiseVal">2</span></label>
</div>
<div class="btns">
<button id="btnRun" type="button">Run Attack</button>
<button id="btnReset" type="button" class="ghost">Reset</button>
</div>
<div class="status" id="status"></div>
<div class="chart-wrap">
<canvas id="chart" width="560" height="200"></canvas>
<div class="axis-label">Key guess (0–255)</div>
</div>
<div class="legend">
<span class="leg-true">■ true key (0xAB)</span>
<span class="leg-other">■ other guesses</span>
</div>
/* Layout and color for the DPA correlation demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
.controls { display: flex; flex-direction: column; gap: .35rem; margin-bottom: .5rem; font-size: .88rem; }
.controls label { display: flex; align-items: center; gap: .4rem; }
.controls input[type=range] { flex: 1; max-width: 200px; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .5rem; }
button { font: 600 14px system-ui, sans-serif; padding: .4rem .85rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.status { font-size: .95rem; font-weight: 600; min-height: 1.4em; margin-bottom: .3rem; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.chart-wrap { position: relative; width: 100%; overflow-x: auto; }
canvas { display: block; width: 100%; height: auto; border: 1px solid #dde3ea; border-radius: 8px; }
.axis-label { font-size: .8rem; color: #666; text-align: center; margin-top: .2rem; }
.legend { font-size: .82rem; margin-top: .3rem; display: flex; gap: 1rem; }
.leg-true { color: #e63946; }
.leg-other { color: #457b9d; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* Differential Power Analysis demo — Hamming-weight model + Pearson correlation */
/* AES S-box lookup table (256 bytes) */
const SBOX = [
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xe6,0x48,0x27,0x9b,0xf2,0x15,0x89,0x09,0x2f,0x36,0x1a,0x22,0x03,0xd5,0x6e,
0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb
];
/* Hamming weight = number of set bits in a byte */
function hw(b) {
let c = 0;
for (let x = b & 0xff; x; x &= x - 1) c++;
return c;
}
/* Simple linear-congruential RNG for reproducible traces */
let seed = 42;
function rand() { seed = (seed * 1664525 + 1013904223) & 0xffffffff; return (seed >>> 0) / 0xffffffff; }
function randn() {
/* Box-Muller transform produces normally distributed noise */
const u = rand() || 1e-10, v = rand();
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
}
/* The hidden key byte the attacker wants to recover */
const SECRET_KEY = 0xAB;
let traces = [], plaintexts = [];
/* Simulate N power traces; each = HW(SBOX[plaintext XOR key]) + Gaussian noise */
function collectTraces(n, noiseLevel) {
seed = 12345;
traces = []; plaintexts = [];
const noiseScale = [0, 0.4, 0.8, 1.5, 2.5, 4.0][noiseLevel];
for (let i = 0; i < n; i++) {
const p = Math.floor(rand() * 256);
const v = SBOX[(p ^ SECRET_KEY) & 0xff];
/* power model — true leakage is Hamming weight of the S-box output plus noise */
const power = hw(v) + noiseScale * randn();
plaintexts.push(p);
traces.push(power);
}
}
/* Pearson correlation between predicted power values and measured traces */
function pearson(xs, ys) {
const n = xs.length;
if (n === 0) return 0;
let mx = 0, my = 0;
for (let i = 0; i < n; i++) { mx += xs[i]; my += ys[i]; }
mx /= n; my /= n;
let num = 0, dx2 = 0, dy2 = 0;
for (let i = 0; i < n; i++) {
const dx = xs[i] - mx, dy = ys[i] - my;
num += dx * dy; dx2 += dx * dx; dy2 += dy * dy;
}
return (dx2 && dy2) ? num / Math.sqrt(dx2 * dy2) : 0;
}
/* For each key guess k, compute Pearson correlation between predicted and actual traces */
function runAttack() {
const corrs = new Float64Array(256);
for (let k = 0; k < 256; k++) {
const preds = plaintexts.map(p => hw(SBOX[(p ^ k) & 0xff]));
corrs[k] = pearson(preds, traces);
}
return corrs;
}
/* Draw correlation bar chart on the canvas */
function drawChart(corrs) {
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
ctx.clearRect(0, 0, W, H);
/* light background */
ctx.fillStyle = '#f8fafc';
ctx.fillRect(0, 0, W, H);
/* zero-correlation baseline */
ctx.strokeStyle = '#cdd3da';
ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(0, H / 2); ctx.lineTo(W, H / 2); ctx.stroke();
/* one bar per key guess, red for the true key */
const barW = W / 256;
const scale = H / 2 * 0.9;
for (let k = 0; k < 256; k++) {
const r = corrs[k];
const x = k * barW;
const h = Math.abs(r) * scale;
const y = r >= 0 ? H / 2 - h : H / 2;
ctx.fillStyle = k === SECRET_KEY ? '#e63946' : '#457b9d';
ctx.fillRect(x, y, Math.max(barW - 0.5, 1), h);
}
}
function run() {
const n = parseInt(document.getElementById('nTraces').value, 10);
const nl = parseInt(document.getElementById('noiseLvl').value, 10);
collectTraces(n, nl);
const corrs = runAttack();
drawChart(corrs);
/* find the key guess with the highest absolute correlation */
let best = 0, bestK = 0;
for (let k = 0; k < 256; k++) {
if (Math.abs(corrs[k]) > best) { best = Math.abs(corrs[k]); bestK = k; }
}
const st = document.getElementById('status');
if (bestK === SECRET_KEY) {
st.textContent = i18nFmt('Correct key 0x%k% found! Peak correlation: %r%', { k: '0x' + SECRET_KEY.toString(16).toUpperCase(), r: best.toFixed(3) });
st.className = 'status ok';
} else {
st.textContent = i18nFmt('Best guess was 0x%k% (r = %r%) — need more traces or less noise.', { k: '0x' + bestK.toString(16).toUpperCase(), r: best.toFixed(3) });
st.className = 'status bad';
}
}
function reset() {
traces = []; plaintexts = [];
const ctx = document.getElementById('chart').getContext('2d');
ctx.clearRect(0, 0, 560, 200);
document.getElementById('status').textContent = '';
document.getElementById('status').className = 'status';
}
document.getElementById('btnRun').addEventListener('click', run);
document.getElementById('btnReset').addEventListener('click', reset);
document.getElementById('nTraces').addEventListener('input', function () {
document.getElementById('nVal').textContent = this.value;
});
document.getElementById('noiseLvl').addEventListener('input', function () {
document.getElementById('noiseVal').textContent = this.value;
});
Comments
Loading comments...