Caloogy Article
中文

LLL Lattice Reduction: How the Knapsack Cryptosystem Fell

Date
Sep 29, 2025
Series
Cryptography
Reading
6 min
Topics
LatticeLLLKnapsackMerkle–Hellman

The one-sentence version

A lattice can have many bases: some with long, slanted vectors, others with short, nearly perpendicular ones. The LLL algorithm (1982) turns a "bad basis" into a "fairly good basis" in polynomial time, and so finds quite short vectors. The secret of many cryptosystems happens to be a short vector in some lattice. This is how the 1978 Merkle–Hellman knapsack cryptosystem was broken. Today LLL remains a core tool for attacking ECDSA nonce leaks and evaluating post-quantum lattice parameters.

This article has three layers:

  1. The intuition: lattices, good and bad bases, the knapsack problem
  2. Real-world use: the rise and fall of knapsack cryptography, recovering private keys from side-channel leaks, choosing post-quantum parameters
  3. The mathematics: Gaussian reduction in two dimensions, the idea of LLL, the knapsack cryptosystem and its lattice attack, with worked examples

Layer 1: The Intuition

1.1 Lattices, good bases and bad bases

Take two vectors b1,b2b_1, b_2 in the plane. All their integer combinations a1b1+a2b2a_1 b_1 + a_2 b_2 form a regularly arranged set of points called a lattice.

The same array of points can be generated by different bases. Take the integer lattice Z2\mathbb{Z}^2 (all points with integer coordinates):

Someone holding the bad basis has a hard time seeing that "the shortest vector in this lattice has length just 1". This is the basic idea of lattice cryptography: the public key gives a bad basis, and the private key is a good one (see "Post-Quantum Cryptography").

1.2 The knapsack problem

You are given a pile of items of different weights, {82,123,287,83,248,373,10,471}\{82, 123, 287, 83, 248, 373, 10, 471\}. Which ones add up to exactly 881881?

This is the subset sum problem. In general it is NP-complete, and with many items you are left with something close to brute force. In 1978 Merkle and Hellman built one of the earliest public-key cryptosystems on it: which items you pick are the plaintext bits, and the total weight is the ciphertext.

1.3 Why it fell

To let the legitimate recipient decrypt, Merkle–Hellman hid a "trapdoor" structure in the item weights. In 1982 Shamir used this structure to break it; soon after, people found a more general method: write the knapsack problem as a lattice, where the secret bit string corresponds to an unusually short vector in the lattice, and run LLL to get it straight out.


Layer 2: Real-World Use

The rise and fall of knapsack cryptography

Year Event
1978 The Merkle–Hellman knapsack cryptosystem is proposed, almost at the same time as RSA
1982 Shamir gives a polynomial-time attack
1982 Lenstra, Lenstra and Lovász publish the LLL algorithm (originally for factoring polynomials with rational coefficients)
1985 onward Lagarias, Odlyzko and others use lattice reduction to attack "low-density" knapsacks; nearly all later improved knapsack variants are wiped out

What LLL is used for today


Layer 3: The Mathematics

3.1 The definition of a lattice

Given linearly independent vectors b1,…,bn∈Rmb_1, \dots, b_n \in \mathbb{R}^m, the lattice is

L={∑iaibi:ai∈Z}.L = \Big\{\sum_i a_i b_i : a_i \in \mathbb{Z}\Big\}.

Two bases BB, B′B' generate the same lattice if and only if B′=UBB' = UB, where UU is a unimodular matrix (an integer matrix with determinant ±1\pm 1), that is, U∈GLn(Z)U \in GL_n(\mathbb{Z}). So "changing basis" means multiplying by an element of the group GLn(Z)GL_n(\mathbb{Z}).

The determinant of a lattice (the volume of the fundamental parallelepiped), ∣det⁡B∣|\det B|, doesn't change with the basis; it is an invariant of the lattice.

3.2 Two dimensions: Gaussian reduction, the vector version of the Euclidean algorithm

In two dimensions there is an exact algorithm (Lagrange–Gauss): repeatedly use the short vector to "whittle down" the long one, until no more can be removed. This is exactly the same idea as "subtract the smaller from the larger" when computing a greatest common divisor.

A worked example: reducing the bad basis (5,8),(8,13)(5, 8), (8, 13)

Each step subtracts one vector from another, a unimodular transformation that leaves the generated lattice unchanged:

Step Operation Basis
0 (5,8), (8,13)(5, 8),\ (8, 13)
1 (8,13)−(5,8)(8, 13) - (5, 8) (5,8), (3,5)(5, 8),\ (3, 5)
2 (5,8)−(3,5)(5, 8) - (3, 5) (2,3), (3,5)(2, 3),\ (3, 5)
3 (3,5)−(2,3)(3, 5) - (2, 3) (2,3), (1,2)(2, 3),\ (1, 2)
4 (2,3)−(1,2)(2, 3) - (1, 2) (1,1), (1,2)(1, 1),\ (1, 2)
5 (1,2)−(1,1)(1, 2) - (1, 1) (1,1), (0,1)(1, 1),\ (0, 1)
6 (1,1)−(0,1)(1, 1) - (0, 1) (1,0), (0,1)(1, 0),\ (0, 1)

We end with the standard basis, confirming this lattice is Z2\mathbb{Z}^2. Note that the coordinates are all Fibonacci numbers, exactly matching the Euclidean algorithm computing gcd⁡(8,13)\gcd(8, 13); Fibonacci numbers happen to be the "worst case" of the Euclidean algorithm. (Checked with Python. Real Gaussian reduction subtracts the best integer multiple at each step, using fewer steps with the same result.)

3.3 The idea of LLL

In high dimensions there is no algorithm both exact and efficient (the shortest vector problem, SVP, is NP-hard), so LLL settles for an approximation:

  1. Size reduction: for each bib_i, subtract integer multiples of the earlier vectors, making its components along the Gram–Schmidt directions as small as possible (a generalization of two-dimensional Gaussian reduction);
  2. The Lovász condition: if two adjacent vectors are in a "bad" order (the Gram–Schmidt length of the later one is much shorter than the earlier one's), swap them and return to step 1.

It can be proved that this ends in polynomial time, and the first output vector satisfies

∥b1∥≤2(n−1)/2⋅λ1(L),\|b_1\| \le 2^{(n-1)/2} \cdot \lambda_1(L),

where λ1\lambda_1 is the length of the true shortest vector. In the worst case this approximation factor is exponential, but real performance is far better than theory, often finding the shortest vector outright in a few dozen dimensions. The stronger BKZ algorithm builds on LLL by solving blocks exactly, trading time for quality.

3.4 Merkle–Hellman and its lattice attack

Private key: a superincreasing sequence w=(2,3,7,14,30,57,120,251)w = (2, 3, 7, 14, 30, 57, 120, 251) (each term exceeds the sum of all earlier ones), a modulus q=491>∑wiq = 491 > \sum w_i and a multiplier r=41r = 41 with gcd⁡(r,q)=1\gcd(r, q) = 1.

Public key: bi=rwi mod qb_i = r w_i \bmod q, that is, b=(82,123,287,83,248,373,10,471)b = (82, 123, 287, 83, 248, 373, 10, 471). It looks like a general knapsack, with the superincreasing structure "hidden" by the modular multiplication.

Encryption and decryption

Encrypt the plaintext bits 0110000101100001 (the ASCII code of the letter a): take items 2, 3 and 8, C=123+287+471=881.C = 123 + 287 + 471 = 881.

Decrypt: r−1 mod 491=12r^{-1} \bmod 491 = 12 and C′=881⋅12 mod 491=261C' = 881 \cdot 12 \bmod 491 = 261. Then go greedily through the superincreasing sequence:

  • 261≥251261 \ge 251: take item 8, leaving 1010
  • 120,57,30,14120, 57, 30, 14 all exceed 1010: skip
  • 10≥710 \ge 7: take item 3, leaving 33
  • 3≥33 \ge 3: take item 2, leaving 00

This gives the bits 0110000101100001, recovering a. (Checked with Python.)

The lattice attack: build the lattice whose basis is the following rows (the last column holds the weights):

(10⋯0b101⋯0b2⋮⋱⋮00⋯1bn00⋯0−C)\begin{pmatrix} 1 & 0 & \cdots & 0 & b_1 \\ 0 & 1 & \cdots & 0 & b_2 \\ \vdots & & \ddots & & \vdots \\ 0 & 0 & \cdots & 1 & b_n \\ 0 & 0 & \cdots & 0 & -C \end{pmatrix}

Taking the plaintext bits xix_i as coefficients, adding the first nn rows and then the last row gives

(x1,…,xn, ∑xibi−C)=(x1,…,xn, 0).(x_1, \dots, x_n,\ \textstyle\sum x_i b_i - C) = (x_1, \dots, x_n,\ 0).

This is an extremely short vector whose components are only 0 and 1, with length at most n\sqrt{n}, while other vectors in the lattice are usually much longer. When the knapsack's "density" is low, LLL finds it directly, and the bit string is the plaintext.


Summary

Layer Key points
Intuition The same lattice has good and bad bases; short vectors hide behind bad bases
Algebra Changing basis == multiplying by a unimodular matrix (GLn(Z)GL_n(\mathbb{Z})); two-dimensional reduction is the Euclidean algorithm
LLL Finds a "short enough" vector in polynomial time, with approximation factor 2(n−1)/22^{(n-1)/2}
Impact Crushed knapsack cryptography; attacks ECDSA nonce leaks; sets post-quantum parameters