Introduction

Every computation consumes power. A transistor switching from 0 to 1 draws a brief current spike that is absent when it stays at 0. For decades this was considered an inevitable physical fact with no security consequence. In 1999 Paul Kocher, Joshua Jaffe, and Benjamin Jun showed it was a catastrophe.

Their attack — Differential Power Analysis (DPA) — treats a cryptographic chip as an unwitting radio transmitter. By recording the chip's power draw while it encrypts many different messages, and then statistically correlating those traces with guesses about the secret key, an attacker can recover the key one small piece at a time. No invasive probing, no reverse-engineering of circuits — just a oscilloscope and a laptop.

The insight is elegant: if your guess for one byte of the key is correct, the group of traces where the intermediate value has a 1-bit at some position will, on average, show a measurable power difference from the group where it has a 0-bit. A wrong guess scrambles the grouping and the difference vanishes into noise. The correct guess stands out like a spike above a flat baseline.

DPA works against virtually every symmetric cipher that has ever been put in silicon — including AES and DES — unless deliberate countermeasures are added. It remains one of the most practically dangerous attacks in all of cryptography.

Try It: Recover a Key Bit

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">&#9632; true key (0xAB)</span>
  <span class="leg-other">&#9632; 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;
});
Result
Console

The chart shows the correlation coefficient between the predicted power model and the actual traces, for every key guess from 0 to 255. The true key produces the highest spike. A wrong guess sees only noise. This is DPA in its purest form: statistics doing what math alone cannot.

The Real Complexity

Why does the attack work at all? The physics and the statistics each play a role.

The physical model. CMOS circuits consume power proportional to the number of bits that flip. The simplest useful approximation is the Hamming-weight model: the power consumed when writing a byte bb is proportional to HW(b)\text{HW}(b), the number of 1-bits in bb. Real chips have more complex leakage, but this model is surprisingly effective.

The intermediate value. For a key-byte guess kk and plaintext pp, the attacker predicts the power of the first AES SubBytes output: v=SubBytes(p⊕k)v = \text{SubBytes}(p \oplus k). If the guess is right, HW(v)\text{HW}(v) correlates with the actual measured power. If wrong, it does not.

The correlation. Let TiT_i be the power trace for plaintext pip_i, and let hi(k)=HW(SubBytes(pi⊕k))h_i(k) = \text{HW}(\text{SubBytes}(p_i \oplus k)). The attacker computes the Pearson correlation coefficient

r(k)=∑i(hi(k)−hˉ)(Ti−Tˉ)∑i(hi(k)−hˉ)2⋅∑i(Ti−Tˉ)2r(k) = \frac{\sum_i (h_i(k) - \bar{h})(T_i - \bar{T})}{\sqrt{\sum_i(h_i(k)-\bar{h})^2 \cdot \sum_i(T_i-\bar{T})^2}}

for each candidate k∈{0,…,255}k \in \{0, \ldots, 255\}. The true key produces ∣r∣≫0|r| \gg 0; all others hover near zero.

Scaling. The signal-to-noise ratio grows as N\sqrt{N} with the number of traces NN. Even a single-trace signal invisible in noise becomes unmistakable after a few hundred traces. This is why the attack is practically unstoppable against unprotected hardware: you can always collect more traces.

Countermeasures. The standard defense is masking: each intermediate value vv is replaced by v⊕rv \oplus r for a fresh random mask rr that changes every encryption, so the Hamming weight seen by the attacker encodes rr rather than kk. Higher-order DPA can attack masked implementations by combining two or more intermediate values, which is why real secure chips use multiple independent masks (see randomized algorithms for related probabilistic reasoning).

Where It Matters

DPA is not a theoretical curiosity — it has broken real products and driven an entire engineering discipline:

  • Smart cards and EMV. The attack was first demonstrated against commercially deployed smart cards in 1999. Banks worldwide eventually had to upgrade payment-card hardware to include masking countermeasures.
  • Hardware security modules (HSMs). HSMs protect cryptographic keys in data centers. Every serious HSM now undergoes side-channel testing as part of certification (FIPS 140-3, Common Criteria).
  • IoT and embedded systems. Microcontrollers in industrial sensors, automotive ECUs, and consumer wearables all run cryptographic routines on hardware with minimal shielding — a natural target for DPA.
  • Certification standards. The EMVCO and Common Criteria evaluation frameworks now explicitly require resistance to power analysis. Passing a certified DPA test is mandatory for payment-grade chips.
  • Academic impact. DPA triggered a new field — physical cryptanalysis — producing hundreds of attack variants (electromagnetic, acoustic, fault injection) and an equal number of countermeasures. It also demonstrated that a discrete log or factoring-hard cipher offers no protection if the implementation leaks.

Conclusion

Differential Power Analysis exposed a gap that pure cryptography had ignored for decades: a cipher can be mathematically unbreakable and physically trivial to break. The AES key schedule can resist 21282^{128} brute-force guesses, yet a few hundred power traces and a spreadsheet can recover it in minutes from an unprotected chip.

The lesson is not that cryptography is broken — it is that security has physical layers. A correct implementation must treat its power consumption, electromagnetic emissions, and timing as part of the attack surface. Masking randomizes the leakage; hiding adds noise and balances the circuit; dual-rail logic equalizes power draw regardless of data.

DPA remains an active research frontier. Higher-order attacks, machine-learning-assisted profiling, and electromagnetic variants keep pushing the boundary of what can be extracted without cracking the math. If you want to understand why secure chips cost more and take longer to certify than ordinary ones, DPA is the answer.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/differential-power-analysis/Content licensed under CC BY-NC 4.0.