Side-Channel Attacks

A smart card computes an RSA signature correctly every time. Yet an attacker with an oscilloscope recovers the private key. This happens without ever seeing a wrong output. The algorithm is mathematically perfect. The implementation betrays itself through how long it takes, how much power it draws, or which cache lines it touches. This is the central lesson of side-channel attacks: correctness of a cryptographic primitive says nothing about the secrecy of the physical process that computes it. Side channels matter because real computers are not abstract Turing machines. They are electrical circuits with caches, branch predictors, shared buses, and power supplies. All of these respond to secret-dependent computation in measurable ways. Cloud providers run mutually distrusting tenants on the same physical core. Browsers execute attacker-controlled JavaScript next to sensitive DOM data. Embedded devices sit in an attacker's own hand. In every one of these settings, an adversary who cannot break Cryptographic Hash Functions or factor RSA moduli can often still extract the key. They do this by watching the machine think. This article builds a systematic taxonomy of side channels — timing, cache/microarchitecture, and power/EM. It explains the common mathematical machinery behind exploiting them: statistical correlation between a leakage model and a measured signal. We derive why naive implementations leak. We walk through the classic timing attack on modular exponentiation and a cache-timing attack on table-based AES. We give the standard defenses (constant-time coding, blinding, masking) with honest discussion of their costs. The same reasoning that lets an attacker infer a bit of a key from a timing difference also underlies traffic analysis and fingerprinting attacks on anonymity systems. This foreshadows the broader theme of Privacy and Anonymity: any observable channel correlated with a secret is a potential leak. This holds whether the secret is a cryptographic key or a user's identity.

Key takeaways
  • Side channels leak secrets through physical or microarchitectural observables — time, power, EM, cache state — not through breaking the math of a cipher.
  • Timing attacks exploit data-dependent branches or variable-time instructions (e.g., early-exit modular exponentiation, non-constant-time comparisons).
  • Cache attacks (Flush+Reload, Prime+Probe, Evict+Time) exploit shared cache hierarchies to infer which memory lines — and hence which secret-dependent table entries — a victim accessed.
  • Power/EM attacks use statistical techniques like Differential Power Analysis (DPA) and Correlation Power Analysis (CPA) to recover keys from thousands of traces.
  • The universal defense principle is constant-time, data-independent execution: no branches, memory accesses, or instruction timings may depend on secret data.
  • Countermeasures (blinding, masking, constant-time libraries) impose measurable performance costs and must be verified, not assumed.
The core insight

A side channel turns any measurable difference correlated with secret data — time, power, cache state — into an oracle that can be statistically inverted to recover the secret.

The idea

A side channel is an unintended information leak from the physical implementation of a system, as opposed to a leak through its designed input/output interface. Formally, if a system computes y=f(x,k)y = f(x, k) where kk is secret, a side channel is an additional observable L=g(x,k,implementation details)L = g(x, k, \text{implementation details}) that an adversary can measure and that is statistically dependent on kk even when yy alone reveals nothing about kk. Side channels are classified by the physical quantity observed. Timing channels exploit variation in execution time caused by data-dependent branches, variable-latency instructions (integer division, some multiplications), or cache/TLB behavior. Cache and microarchitectural channels exploit shared hardware resources — L1/L2/L3 caches, translation lookaside buffers, branch predictors, execution ports — whose state after a victim's execution reveals which addresses or code paths the victim touched. Power and electromagnetic (EM) channels exploit the fact that CMOS circuits draw current proportional to the Hamming weight or Hamming distance of the bits being processed, so an oscilloscope trace of supply current (or a nearby EM probe) statistically correlates with intermediate values of a computation. The common mathematical structure across all these channels is a leakage function L(t)=h(v(k,x))+noiseL(t) = h(v(k, x)) + \text{noise}, where v(k,x)v(k,x) is some intermediate value computed during execution (e.g., an S-box output byte, or "cache line ℓ\ell was accessed"), and hh is a deterministic model of how that value manifests physically (Hamming weight model, Hamming distance model, or a binary hit/miss indicator). An attacker who can guess candidate sub-keys, predict vv for each candidate, and correlate the predicted leakage against the measured leakage across many traces can distinguish the correct sub-key hypothesis from wrong ones — this is the essence of Differential Power Analysis and its refinements, and an analogous hypothesis-testing logic underlies cache-timing key recovery.

How it works

We present the mechanism in three layers: the general attack pipeline, a concrete timing attack (RSA/modular exponentiation), and a concrete cache attack (Flush+Reload on AES T-tables). All three instantiate the same generic template.

Generic side-channel attack pipeline ``` ALGORITHM GenericSideChannelAttack Input: victim runs f(x, k) for unknown secret k, attacker can trigger many runs leakage_model(hypothesis_bits, x) -- predicts an observable given a guess measure() -- returns a real physical/timing trace 1. traces <- [] 2. predictions <- [] 3. for i = 1 to N: # collect N traces 4. x_i <- choose or observe input (known plaintext / triggerable input) 5. trigger victim to compute f(x_i, k) 6. trace_i <- measure() # timing, power waveform, cache state 7. traces.append(trace_i) 8. for each candidate sub-key guess g in key-space slice: 9. for i = 1 to N: 10. predictions[i] <- leakage_model(g, x_i) 11. score[g] <- correlation(predictions, traces) # e.g. Pearson r, or

# simple mean-difference test 12. best_guess <- argmax_g score[g] 13. output best_guess ```

Why this works, mechanically: if gg equals the true sub-key, the predicted leakage predictions\text{predictions} is a genuine (noisy) function of the same intermediate value that physically drives trace\text{trace}, so the correlation is large in magnitude. If gg is wrong, the prediction is uncorrelated with the true intermediate value, so the correlation concentrates near zero as NN grows (by the law of large numbers). The attacker never needs to see the plaintext-key relationship directly — only that some function of the guess correlates with some physical measurement.

Case 1 — Timing attack on modular exponentiation. The textbook square-and-multiply algorithm for RSA/DH computes c=mk  nc = m^k \bmod n bit-by-bit: ``` ALGORITHM SquareAndMultiply_UNSAFE(m, k, n) # k = k_{t-1} ... k_1 k_0 in binary 1. r <- 1 2. for i = t-1 downto 0: 3. r <- (r r) mod n 4. if k_i == 1: 5. r <- (r m) mod n # extra multiply, extra TIME, only when bit=1 6. return r ``` Line 5 executes conditionally on the secret bit kik_i. If the modular multiplication's latency is even slightly data-dependent (true of schoolbook long multiplication and of early-exit "optimized" modmul routines), an attacker who can measure total exponentiation time for many known messages mm can, bit by bit from the most significant bit down, guess kik_i, predict the timing contribution of the corresponding multiply, and correlate that prediction against the observed total time — recovering the exponent one bit at a time (Kocher's original 1996 attack).

Case 2 — Flush+Reload cache attack on table-based AES. Software AES implementations historically used precomputed T-tables so that each round reduces to XORs and table lookups: `state[i] = Ttable[state[i] XOR roundkey[i]]`. The table is shared in memory (and in cache) between attacker and victim on the same machine via shared libraries or page deduplication. ``` ALGORITHM FlushReload_AES(shared_Ttable_pages) 1. for each round of victim invocation: 2. for each address a in shared_Ttable_pages: 3. clflush(a) # evict a from ALL cache levels 4. wait(victim_executes_one_AES_round) # trigger/synchronize 5. for each address a in shared_Ttable_pages: 6. t0 <- rdtsc() 7. read(a) 8. t1 <- rdtsc() 9. if (t1 - t0) < THRESHOLD: 10. mark a as "accessed by victim" # cache HIT -> victim touched this line 11. infer which T-table index (hence which key byte XOR plaintext byte) was used 12. combine with known plaintext bytes to solve for key byte ``` Because a cache hit (data already resident) takes on the order of a few nanoseconds while a cache miss (fetch from DRAM) takes on the order of 100+ nanoseconds, the timing threshold cleanly separates "victim accessed this line" from "victim did not," giving the attacker a near-noiseless oracle for which 16-byte-aligned chunk of the T-table (and hence which value of `plaintext_byte XOR key_byte`) was used — enough, across many encryptions, to recover the AES key byte by byte.

Figure
Side-channel attack surface: shared hardware between mutually distrusting processes
Victim process AES encrypt(plaintext, key) accesses Ttable[p XOR k] Attacker process flush + reload probe measures access latency Shared L1/L2/L3 cache Ttable pages (shared library / dedup) DRAM (slow: ~100ns+ miss) loads T-table line reload probe miss fetch hit: fast (~ns)

Complexity & correctness

Timing attack complexity. For an nn-bit RSA exponent recovered bit-by-bit, each bit requires collecting timing samples over O(1/ε2)O(1/\varepsilon^2) traces, where ε\varepsilon is the timing difference (in units of the measurement's standard deviation) caused by the conditional multiply — this follows directly from the standard sample-size bound for detecting a mean shift ε\varepsilon with fixed confidence via a tt-test or correlation test (variance of the sample mean shrinks as 1/N1/N, so N=Θ(σ2/ε2)N = \Theta(\sigma^2/\varepsilon^2) traces suffice to separate the two hypotheses). Total work is O(n/ε2)O(n / \varepsilon^2) traces plus O(n)O(n) sequential bit-recovery steps, since each bit's correct value is needed to correctly predict the timing contribution of subsequent bits (errors propagate, so most practical attacks include an error-correction/backtracking step).

Cache attack complexity. Flush+Reload against T-table AES needs, in the noiseless case, only O(1)O(1) encryption observed per key byte in principle (16 possible table-region hits directly reveal p⊕kp \oplus k for a known plaintext byte pp), but in practice noise — OS scheduling jitter, prefetching, other cache traffic — forces averaging over tens to a few hundred encryptions per byte, giving overall complexity O(256⋅c)O(256 \cdot c) trace-probes for a full AES-128 key (1616 bytes ×16\times 16 candidate nibble/byte values, times a small constant cc of repeated trials for noise averaging) — this is why practical implementations report full-key recovery in seconds to minutes of monitored encryptions, not brute-force 21282^{128}.

Power analysis (DPA/CPA) complexity. Differential Power Analysis partitions NN traces into two sets by a predicted single bit of an intermediate value and computes the difference of means; Correlation Power Analysis computes Pearson correlation between a full Hamming-weight prediction and the traces. Both require N=Θ(1/ρ2)N = \Theta(1/\rho^2) traces to reliably detect a true correlation of magnitude ρ\rho against Gaussian noise, by the same variance-of-the-sample-correlation argument — smaller ρ\rho (well-shielded, low-leakage hardware) directly forces a quadratically larger number of traces, which is precisely why hiding/masking countermeasures work: they drive ρ→0\rho \to 0.

Correctness argument (why the statistical distinguisher actually recovers the key). The key theoretical fact is a hypothesis-testing separation: under the correct key hypothesis, the predicted leakage Y=h(v(k∗,x))Y = h(v(k^*, x)) and the measured trace T=h(v(k∗,x))+ηT = h(v(k^*,x)) + \eta share the deterministic component h(v(k∗,x))h(v(k^*,x)), so Cov(Y,T)=Var(h(v(k∗,x)))>0\text{Cov}(Y, T) = \text{Var}(h(v(k^*,x))) > 0. Under any wrong hypothesis g≠k∗g \ne k^*, if the leakage function h∘vh \circ v behaves close to a pseudorandom function of the guess (true for AES S-boxes, false for linear operations — a caveat attackers must handle), v(g,x)v(g,x) is statistically independent of the true intermediate value, so Cov(Yg,T)→0\text{Cov}(Y_g, T) \to 0 as N→∞N \to \infty by the law of large numbers. The correct guess is therefore, with high probability for large enough NN, the arg-max of the empirical correlation — this is exactly a multiple-hypothesis mean-comparison test, and its sample complexity is governed by the signal-to-noise ratio (SNR) of the channel, matching the Θ(1/ρ2)\Theta(1/\rho^2) bound above.

Honest tradeoffs. These attacks are not cryptanalysis of AES or RSA — the algorithms remain unbroken as functions. The attack is entirely against the implementation's physical realization, and its cost is dominated by (a) how large the leakage signal ρ\rho is relative to noise, and (b) how many traces/queries the attacker can obtain, which is itself bounded by rate limits, physical access, or co-location constraints in real deployments. Constant-time defenses eliminate ρ\rho at the cost of always executing the "slow path" (e.g., always doing the multiply in square-and-multiply, or using bitsliced/AES-NI hardware instructions instead of table lookups), typically costing a modest constant-factor slowdown, not an asymptotic one.

Worked example

We trace a simplified Flush+Reload recovery of one byte of an AES-128 key using a 4-entry toy T-table (real AES has 256-entry tables per T-table; we shrink it for legibility) — the recovered relation generalizes identically. Suppose the first-round operation is `idx = plaintext_byte XOR key_byte`, and the implementation looks up `Ttable[idx]`. The attacker knows `plaintext_byte = 0x35` for a given encryption and wants `key_byte`.

StepActionObservation
1Attacker flushes all 4 cache lines holding `Ttable[0..3]`cache empty for T-table
2Attacker triggers victim to encrypt with `plaintext_byte = 0x35`victim computes `idx = 0x35 XOR key_byte` and reads `Ttable[idx]`
3Attacker reloads `Ttable[0]`: 132 cyclesMISS (slow)
4Attacker reloads `Ttable[1]`: 41 cyclesHIT (fast)
5Attacker reloads `Ttable[2]`: 129 cyclesMISS
6Attacker reloads `Ttable[3]`: 136 cyclesMISS
7Conclude victim accessed `Ttable[1]`, so `idx = 1`—
8Solve: `key_byte = plaintext_byte XOR idx = 0x35 XOR 0x01 = 0x34`recovered key byte
9Repeat steps 1–8 with a different known plaintext byte to confirm (reduce noise-induced false hit)confirms `0x34` across trials

Repeating this procedure independently for each of the 16 key bytes of AES-128 recovers the entire key with total work linear in the key length (16 bytes), each requiring only a handful of flush–trigger–reload trials to beat measurement noise — a dramatically smaller search than the 21282^{128} brute force the cipher nominally requires, precisely because the cache leaked the table index directly instead of leaving the attacker to guess it.

Common pitfalls & traps

PitfallWhy it happensConsequence
Branching on secret bits (`if (secret_bit) ...`)Natural way to write control flowBranch predictor / timing reveals the bit directly
Table lookups indexed by secret dataFast, idiomatic (S-boxes, T-tables)Cache-line access pattern leaks the index (Flush+Reload, Prime+Probe)
Non-constant-time comparison (`memcmp` early-exit on MAC check)Naive equality checkTiming reveals number of matching prefix bytes, enabling forgery
Assuming "no measurable difference on my machine" is proof of safetyTesting on one CPU/OS with limited resolutionDifferent microarchitecture, or attacker with better instrumentation, reveals the leak
Masking/blinding implemented but re-combined too early in the pipelineCountermeasure only "shields" part of the computationHigher-order or combined-trace attacks still succeed
Treating side-channel resistance as a one-time patchCompiler optimizations can reintroduce data-dependent branches or timingRe-introduced leak after a "harmless" refactor or compiler upgrade

Pioneers & history

Origins · 1 minPaul C. Kocher and how this was discovered
PC
Paul C. Kocher
1973– · Independent cryptographer, founder of Cryptography Research Inc.

Kocher's 1996 paper on timing attacks against implementations of Diffie–Hellman, RSA, and DSS was the first rigorous demonstration that an algorithm's running time alone could leak a private key, launching side-channel analysis as a formal subfield of cryptographic engineering. He went on to co-develop Differential Power Analysis with Jaffe and Jun in 1998–1999, and later co-discovered the Spectre class of microarchitectural attacks in 2018, connecting classical side-channel theory to modern speculative-execution hardware.

History
1996
— Paul Kocher publishes the first practical timing attack on RSA/DH/DSS implementations.
1999
— Kocher, Jaffe, and Jun introduce Differential Power Analysis (DPA) against smart cards.
2005
— Osvik, Shamir, and Tromer demonstrate practical cache-timing attacks recovering AES keys via Prime+Probe and Evict+Time.
2010
— Yarom and Falkner formalize the Flush+Reload technique against shared-library cryptographic code.
2018
— Kocher et al. and Horn et al. independently disclose Spectre and Meltdown, extending side-channel leakage to speculative execution across security domains.

Where this matters

Constant-time cryptographic libraries (BoringSSL, libsodium, HACL*, the Go crypto standard library) are engineered specifically to defeat timing and cache side channels, using techniques such as branch-free conditional selection and bitsliced or hardware-accelerated (AES-NI) implementations that avoid secret-indexed table lookups entirely. Smart cards and hardware security modules undergo formal side-channel evaluation (Common Criteria, FIPS 140-3 physical security testing) using power/EM trace analysis before certification for payment and identity applications. Cloud hypervisors and browser JavaScript engines add mitigations — cache partitioning, reduced timer resolution, site isolation — directly in response to cache-timing and speculative-execution side channels demonstrated against shared multi-tenant infrastructure. The same statistical logic of correlating an observable with a hypothesis reappears in traffic-analysis attacks on encrypted network flows and anonymity networks, linking this material forward to Privacy and Anonymity, and it also depends conceptually on the memory layout assumptions covered in Memory-Safety Vulnerabilities, since many cache attacks require precise control over shared memory pages.

Ask the tutor: How would you design and test a constant-time verification routine to eliminate a MAC-comparison timing side channel?

Can side-channel attacks ever be fully eliminated?

Not in an absolute sense — any physical implementation of a computation leaks some signal, since real hardware always has data-dependent power draw, timing, and electromagnetic emission at a fine enough resolution. What defenders can achieve is driving the exploitable signal-to-noise ratio so low that the number of traces required for statistically reliable key recovery becomes impractically large (e.g., requiring more traces than the key's lifetime permits, or more than an attacker can physically collect). This is why side-channel security is evaluated quantitatively — in terms of the number of traces needed for a successful attack — rather than treated as a binary yes/no property the way algorithmic security often is.

Why can't we just rely on the cryptographic algorithm being mathematically secure?

Mathematical security proofs for algorithms like AES or RSA model computation as an idealized black box that takes an input and returns an output, with no other observable behavior. Real hardware violates this model constantly: execution time, cache state, power draw, and EM emissions are all physically necessary byproducts of computation that the abstract security proof simply does not account for. Side-channel security is therefore an implementation-level property, proven (or broken) separately from the algorithm's cryptanalytic security, and both must hold for a real system to be considered secure.

What is the difference between DPA and CPA?

Differential Power Analysis (DPA) partitions collected traces into two groups based on a single predicted bit of an intermediate value (e.g., the least significant bit of an S-box output) and looks for a significant difference of means between the groups at some time sample. Correlation Power Analysis (CPA) instead predicts a finer-grained leakage value — typically the full Hamming weight of an intermediate byte — and computes the Pearson correlation coefficient between predictions and traces across all time samples. CPA generally needs fewer traces because it uses more information per prediction, but both rely on exactly the same underlying statistical separation between correct and incorrect key hypotheses.

Are side-channel attacks only a concern for embedded devices and smart cards?

No — while classical power-analysis side channels were first demonstrated against smart cards, modern side-channel research targets shared cloud infrastructure, web browsers, and even remote network services. Cache-timing attacks work between mutually distrusting virtual machines on the same physical server; Spectre-class attacks exploit speculative execution inside ordinary consumer CPUs; and remote timing attacks over a network can, with enough averaging to overcome network jitter, still recover secrets from web servers. Any shared or physically accessible computing resource is a candidate side-channel surface.

How does constant-time programming actually prevent these attacks?

Constant-time code guarantees that the sequence of instructions executed, and the sequence of memory addresses accessed, are both independent of secret data — only the values being processed may depend on the secret. This is achieved by replacing secret-dependent branches with arithmetic/bitwise select operations (e.g., computing both branches and selecting via a mask) and replacing secret-indexed table lookups with either bitsliced implementations or hardware instructions (like AES-NI) that compute the S-box functionally rather than via memory lookup. Because timing and cache-access patterns no longer depend on the secret, the correlation an attacker relies on collapses to zero, defeating both timing and cache-based recovery.

What role does noise play in defending against these attacks, and is adding noise a good defense?

Noise increases the number of traces an attacker needs, since sample complexity scales as Θ(1/ρ2)\Theta(1/\rho^2) where ρ\rho is signal-to-noise ratio, so doubling the noise standard deviation roughly quadruples the required trace count. However, noise alone is a weak, non-structural defense: attackers can average across many traces to suppress random noise, and averaging is cheap if the attacker can trigger many victim operations. Noise is best used as a supplementary hardening layer on top of structural countermeasures like constant-time coding and masking, not as a substitute for them.

Why did AES table-lookup implementations become a major target, and how did the field respond?

T-table implementations were the standard "fast" software AES design for over a decade, trading a small memory footprint for speed by precomputing combined SubBytes/MixColumns operations into lookup tables indexed by secret-dependent bytes. Once Osvik–Shamir–Tromer and later Bernstein demonstrated practical cache-timing key recovery against exactly this design, the field shifted decisively toward bitsliced software AES (no data-dependent memory access at all) and, once available, hardware AES-NI instructions that compute the S-box inside fixed-latency silicon rather than via cacheable memory, eliminating the cache side channel structurally rather than through obfuscation.

Before this