Cấu trúc dữ liệu và Giải thuật Bài 149: Thuật toán Sàng trường số đại số tổng quát (General Number Field Sieve - GNFS) - Thuật toán phân tích thừa số nguyên tố mạnh nhất nhân loại dùng để phá vỡ chuẩn mã hóa RSA.
Trong lý thuyết số thuật toán và mật mã học, bài toán phân tích một hợp số lớn ra thừa số nguyên tố là "chiếc mỏ neo" bảo vệ an toàn cho hệ mật mã khóa công khai RSA. Nếu như phép thử phép chia ngây thơ (Trial Division) đòi hỏi thời gian , còn thuật toán Pollard's (Bài 116) tốn , thì bước nhảy vọt thực sự của thế kỷ 20 là các thuật toán dựa trên nguyên lý Hiệu hai bình phương của Fermat (Fermat's Difference of Squares):
Nếu , ta tìm được thừa số không tầm thường của qua phép tính:
Thuật toán Sàng bậc hai (Quadratic Sieve - QS) của Carl Pomerance (1981) từng giữ kỷ lục tốc độ với độ phức tạp . Tuy nhiên, khi vượt quá 100 chữ số thập phân (khoảng 330 bit), QS chạm trần giới hạn vật lý.
Năm 1990, John Pollard công bố một ý tưởng: chuyển bài toán từ vành số nguyên thông thường sang Vành số nguyên của một trường số đại số (Algebraic Number Field) . Được hoàn thiện bởi Joe Buhler, Hendrik Lenstra và Carl Pomerance, Thuật toán Sàng trường số đại số tổng quát (General Number Field Sieve - GNFS) ra đời. GNFS là thuật toán cổ điển nhanh nhất từng được biết đến đối với các số nguyên tổng quát, hạ độ phức tạp xuống mức dưới hàm mũ bậc (Sub-exponential of order ):
Tất cả các kỷ lục bẻ khóa RSA lớn nhất trong lịch sử (từ RSA-512, RSA-768 đến RSA-240 và RSA-250 gồm 829 bit) đều được thiết lập bằng GNFS.
1. Nút thắt của Sàng bậc hai và Đột phá của GNFS
Ký hiệu -function
Trong phân tích độ phức tạp dưới hàm mũ, ta sử dụng chuẩn ký hiệu :
-
Khi : Độ phức tạp là hàm mũ (các thuật toán duyệt vét cạn).
-
Khi : Độ phức tạp là đa thức (các thuật toán như kiểm tra số nguyên tố AKS).
-
Quadratic Sieve (QS): .
-
GNFS: .
Tại sao Quadratic Sieve chạm giới hạn?
Trong QS, ta xét các giá trị .
-
Kích thước của các số cần phân tích trơn (smoothness testing) có độ lớn khoảng .
-
Theo định lý Canfield-Erdős-Pomerance, xác suất để một số ngẫu nhiên có độ lớn là số -trơn (mọi thừa số nguyên tố đều ) giảm cực nhanh khi tăng. Khi đạt 512 bit hoặc 1024 bit, các số cỡ trở nên quá lớn để có thể tìm đủ các quan hệ trơn.
Ý tưởng cốt lõi của GNFS
Thay vì tìm các số trơn có kích thước , GNFS tìm cách phân tích đồng thời hai số có kích thước chỉ khoảng:
Một số nhỏ hơn theo cấp số mũ sẽ có xác suất trơn cao hơn hàng tỷ lần, cho phép thu thập các quan hệ nhanh hơn vượt trội. Để làm được điều này, GNFS xây dựng một cây cầu đại số kết nối giữa thế giới số hữu tỉ và thế giới trường số đại số .
2. Cơ sở Toán học: Trường số Đại số và Đồng cấu Vành
Đa thức cơ sở và Nghiệm modulo
Cho số cần phân tích . Chọn một bậc nguyên dương (thường là cho RSA-768 hoặc cho RSA-1024).
Đặt (hoặc ). Ta biểu diễn theo hệ cơ số :
Từ đó, ta xây dựng đa thức đơn biến bất khả quy trên :
Theo định nghĩa, đa thức này thỏa mãn tính chất nền tảng:
Trường số đại số
Gọi là một nghiệm phức của đa thức ().
Ta mở rộng trường số hữu tỉ bằng cách thêm , tạo thành trường số đại số :
Xét vành các phần tử nguyên đại số sinh bởi :
Đồng cấu Vành tự nhiên (The Algebraic Bridge)
Vì và , tồn tại một đồng cấu vành (Ring Homomorphism) duy nhất:
được xác định bằng quy tắc thay thế biến bằng số nguyên :
Đồng cấu bảo toàn cả phép cộng và phép nhân:
Thế giới Đại số Z[α] Thế giới Hữu tỉ Z
(Trường số Q(α)) (Modulo N)
β^2 ∈ Z[α] X^2 ∈ Z
| |
| Ánh xạ đồng cấu φ (α -> m) |
+------------------------------------->|
v
Y^2 = φ(β)^2 ≡ X^2 (mod N)
|
v
gcd(X - Y, N) => p, q
3. Khái niệm Số trơn trên Hai Nhánh: Hữu tỉ và Đại số
Ta xét các cặp số nguyên nguyên tố cùng nhau với và . Với mỗi cặp , ta xét biểu thức nhị thức tuyến tính và ảnh của nó qua đồng cấu: .
1. Nhánh Hữu tỉ (Rational Side)
Giá trị cần kiểm tra tính trơn là số nguyên:
Số được gọi là -trơn nếu mọi ước nguyên tố của nó đều (thuộc Cơ sở thừa số hữu tỉ - Rational Factor Base).
2. Nhánh Đại số (Algebraic Side)
Trong vành đại số , việc phân tích phần tử thành tích các phần tử nguyên tố được quy về việc tính Chuẩn đại số (Algebraic Norm).
Chuẩn của , ký hiệu , là một số nguyên xác định bởi:
Phần tử được gọi là -trơn nếu chuẩn số nguyên là -trơn (phân tích hoàn toàn thành các số nguyên tố thuộc Cơ sở thừa số đại số - Algebraic Factor Base).
4. Năm Giai đoạn của Quy trình GNFS
[1. Chọn Đa thức f(x)] ---> [2. Sàng cặp (a, b) trơn] ---> [3. Đại số tuyến tính mod 2]
|
[5. Trích xuất thừa số gcd] <--- [4. Khai căn Đại số & Hữu tỉ] <------+
Giai đoạn 1: Chọn Đa thức (Polynomial Selection)
Tìm bậc và hai đa thức (thường chọn và ) sao cho các hệ số nhỏ nhất có thể và có nhiều nghiệm modulo các số nguyên tố nhỏ (tối ưu hóa chỉ số Murphy và kích thước chuẩn).
Giai đoạn 2: Sàng tìm Quan hệ (Sieving Phase)
Sử dụng kỹ thuật Sàng lưới (Lattice Sieving) hoặc Sàng đường (Line Sieving) trên không gian hai chiều :
-
Cố định , quét qua các giá trị . Dùng thuật toán sàng kiểu Eratosthenes để trừ logarit của các số nguyên tố trong cơ sở thừa số.
-
Thu thập một tập hợp lớn gồm các cặp sao cho đồng thời:
-
là -trơn.
-
là -trơn.
-
Giai đoạn 3: Đại số Tuyến tính trên (Linear Algebra Phase)
Mỗi cặp trơn tương ứng với một vector nhị phân biểu diễn tính chẵn lẻ của số mũ các thừa số nguyên tố (cùng với các bit ký tự bậc hai - Quadratic Characters để xử lý vấn đề đơn vị trong vành đại số).
-
Dựng ma trận thưa khổng lồ kích thước hàng triệu hàng triệu trên trường .
-
Sử dụng các thuật toán đại số tuyến tính thưa như Block Lanczos hoặc Block Wiedemann để tìm hạt nhân (Null Space):
-
Mỗi nghiệm chỉ ra một tập con các cặp sao cho tích của chúng tạo thành bình phương hoàn hảo trên cả hai nhánh:
Giai đoạn 4: Khai căn trong Trường số Đại số (Square Root Phase)
-
Tính trên số nguyên là bài toán đơn giản (tính từ tích phân tích nguyên tố).
-
Tính trong là một thách thức toán học lớn vì biểu thức khai triển có bậc và hệ số khổng lồ. Ta sử dụng thuật toán khai căn số học của Peter Montgomery hoặc Jean-Marc Couveignes dựa trên định lý số dư Trung Hoa (CRT) trên các i-đê-an nguyên tố (Prime Ideals).
Giai đoạn 5: Tìm Thừa số (Factor Extraction)
Áp dụng đồng cấu :
Khi đó ta có:
Ta tính:
Nếu , ta phân tích thành công . Với xác suất ít nhất , , ta tìm được nghiệm ngay lần thử đầu tiên.
5. Hiện thực hóa Mô hình GNFS bằng Golang
Đoạn mã Golang dưới đây hiện thực hóa mô hình giáo khoa hoàn chỉnh của thuật toán GNFS trên số nguyên:
-
Tự động chọn tham số cơ số và dựng đa thức ().
-
Thiết lập Cơ sở thừa số Hữu tỉ và Cơ sở thừa số Đại số.
-
Sàng tìm các cặp trơn thỏa mãn cả hai nhánh.
-
Xây dựng ma trận nhị phân số mũ và giải hệ phương trình trên bằng phép khử Gauss.
-
Tính , , và tính để bẻ khóa .
Go
package main
import (
"fmt"
"math"
"math/big"
)
// GCD tính ước chung lớn nhất của hai số nguyên lớn
func gcd(a, b *big.Int) *big.Int {
x := new(big.Int).Set(a)
y := new(big.Int).Set(b)
return new(big.Int).GCD(nil, nil, x, y)
}
// Kiểm tra một số nguyên có trơn trên cơ sở thừa số primes hay không
// Trả về (isSmooth, mảng số mũ)
func factorOverBase(val int64, primes []int64) (bool, []int) {
if val == 0 {
return false, nil
}
exps := make([]int, len(primes)+1) // Phần tử đầu tiên là bit dấu âm
if val < 0 {
exps[0] = 1
val = -val
}
for i, p := range primes {
for val%p == 0 {
exps[i+1]++
val /= p
}
}
return val == 1, exps
}
// Cấu trúc lưu trữ một quan hệ trơn (Relation)
type Relation struct {
a, b int64
ratExps []int // Số mũ trên Rational Factor Base
algExps []int // Số mũ trên Algebraic Factor Base
combinedVec []int // Vector nhị phân modulo 2
}
type MiniGNFS struct {
N *big.Int
m int64
d int
poly []int64 // f(x) = poly[2]*x^2 + poly[1]*x + poly[0]
ratBase []int64
algBase []int64
relations []Relation
}
func NewMiniGNFS(N *big.Int) *MiniGNFS {
// Chọn m xấp xỉ N^(1/2) cho bậc d = 2
nFloat, _ := new(big.Float).SetInt(N).Float64()
m := int64(math.Round(math.Sqrt(nFloat)))
// Biểu diễn N theo cơ số m: N = m^2 + c1*m + c0
mBig := big.NewInt(m)
m2 := new(big.Int).Mul(mBig, mBig)
rem := new(big.Int).Sub(N, m2)
c1 := new(big.Int).Div(rem, mBig).Int64()
c0 := new(big.Int).Mod(rem, mBig).Int64()
// f(x) = x^2 + c1*x + c0 thỏa mãn f(m) == N == 0 (mod N)
poly := []int64{c0, c1, 1}
// Thiết lập Factor Base nhỏ
ratBase := []int64{2, 3, 5, 7, 11, 13, 17, 19, 23}
algBase := []int64{2, 3, 5, 7, 11, 13, 17, 19, 23}
return &MiniGNFS{
N: N,
m: m,
d: 2,
poly: poly,
ratBase: ratBase,
algBase: algBase,
}
}
// Chuẩn hữu tỉ: a + b*m
func (gnfs *MiniGNFS) rationalNorm(a, b int64) int64 {
return a + b*gnfs.m
}
// Chuẩn đại số: (-b)^d * f(-a/b)
// Với bậc 2: N(a + b*alpha) = poly[0]*b^2 - poly[1]*a*b + poly[2]*a^2
func (gnfs *MiniGNFS) algebraicNorm(a, b int64) int64 {
return gnfs.poly[0]*b*b - gnfs.poly[1]*a*b + gnfs.poly[2]*a*a
}
// Sàng tìm các quan hệ trơn trên cả hai phía
func (gnfs *MiniGNFS) FindRelations(boundA, boundB int64) {
for b := int64(1); b <= boundB; b++ {
for a := -boundA; a <= boundA; a++ {
// Điều kiện gcd(a, b) == 1
if a == 0 || b == 0 {
continue
}
g := new(big.Int).GCD(nil, nil, big.NewInt(a), big.NewInt(b)).Int64()
if g != 1 {
continue
}
rNorm := gnfs.rationalNorm(a, b)
aNorm := gnfs.algebraicNorm(a, b)
isRatSmooth, ratExps := factorOverBase(rNorm, gnfs.ratBase)
if !isRatSmooth {
continue
}
isAlgSmooth, algExps := factorOverBase(aNorm, gnfs.algBase)
if !isAlgSmooth {
continue
}
// Tạo vector nhị phân mod 2: ghép [ratExps, algExps]
combined := make([]int, len(ratExps)+len(algExps))
for i, e := range ratExps {
combined[i] = e % 2
}
for i, e := range algExps {
combined[len(ratExps)+i] = e % 2
}
gnfs.relations = append(gnfs.relations, Relation{
a: a,
b: b,
ratExps: ratExps,
algExps: algExps,
combinedVec: combined,
})
}
}
}
// Khử Gauss trên F_2 tìm vector thuộc Null Space
func (gnfs *MiniGNFS) SolveMatrixMod2() []int {
numRows := len(gnfs.relations)
if numRows == 0 {
return nil
}
numCols := len(gnfs.relations[0].combinedVec)
// Tạo ma trận A (numCols x numRows) để tìm tổ hợp các hàng
mat := make([][]int, numCols)
for c := 0; c < numCols; c++ {
mat[c] = make([]int, numRows)
for r := 0; r < numRows; r++ {
mat[c][r] = gnfs.relations[r].combinedVec[c]
}
}
// Ma trận biến đổi theo dõi các quan hệ được chọn
transform := make([][]int, numRows)
for r := 0; r < numRows; r++ {
transform[r] = make([]int, numRows)
transform[r][r] = 1
}
rowPivot := 0
for c := 0; c < numCols && rowPivot < numRows; c++ {
pivot := -1
for r := rowPivot; r < numRows; r++ {
if mat[c][r] == 1 {
pivot = r
break
}
}
if pivot == -1 {
continue
}
// Đổi hàng
mat[c][rowPivot], mat[c][pivot] = mat[c][pivot], mat[c][rowPivot]
transform[rowPivot], transform[pivot] = transform[pivot], transform[rowPivot]
// Khử các hàng khác
for r := 0; r < numRows; r++ {
if r != rowPivot && mat[c][r] == 1 {
for k := 0; k < numCols; k++ {
mat[k][r] ^= mat[k][rowPivot]
}
for k := 0; k < numRows; k++ {
transform[r][k] ^= transform[rowPivot][k]
}
}
}
rowPivot++
}
// Tìm hàng toàn số 0 trong mat
for r := rowPivot; r < numRows; r++ {
allZero := true
for c := 0; c < numCols; c++ {
if mat[c][r] != 0 {
allZero = false
break
}
}
if allZero {
var subset []int
for k := 0; k < numRows; k++ {
if transform[r][k] == 1 {
subset = append(subset, k)
}
}
return subset
}
}
return nil
}
// Phân tích N
func (gnfs *MiniGNFS) Factorize() (*big.Int, *big.Int) {
fmt.Printf("[Giai đoạn 1] Đa thức cơ sở: f(x) = %d*x^2 + %d*x + %d\n",
gnfs.poly[2], gnfs.poly[1], gnfs.poly[0])
fmt.Printf("[Giai đoạn 1] Cơ số m = %d thỏa mãn f(m) = %s = 0 (mod N)\n",
gnfs.m, gnfs.N.String())
// Sàng quan hệ
gnfs.FindRelations(150, 20)
fmt.Printf("[Giai đoạn 2] Đã thu thập được %d quan hệ trơn trên cả hai nhánh.\n",
len(gnfs.relations))
if len(gnfs.relations) < len(gnfs.ratBase)+len(gnfs.algBase) {
fmt.Println("Không đủ quan hệ để giải hệ phương trình!")
return nil, nil
}
// Giải phương trình tuyến tính mod 2
subset := gnfs.SolveMatrixMod2()
if len(subset) == 0 {
fmt.Println("Không tìm thấy phụ thuộc tuyến tính trên F_2!")
return nil, nil
}
fmt.Printf("[Giai đoạn 3] Tìm thấy phụ thuộc tuyến tính gồm %d quan hệ.\n", len(subset))
// Tính X = sqrt(prod (a + bm)) mod N
// Tổng hợp các số mũ nguyên tố trên nhánh hữu tỉ
totalRatExps := make([]int, len(gnfs.ratBase)+1)
for _, idx := range subset {
rel := gnfs.relations[idx]
for i, exp := range rel.ratExps {
totalRatExps[i] += exp
}
}
// X = prod p^(exp / 2) mod N
X := big.NewInt(1)
for i, pVal := range gnfs.ratBase {
halfExp := totalRatExps[i+1] / 2
term := new(big.Int).Exp(big.NewInt(pVal), big.NewInt(int64(halfExp)), gnfs.N)
X = new(big.Int).Mul(X, term)
X.Mod(X, gnfs.N)
}
// Tính Y = phi(beta) mod N = prod (a + bm) mod N (cho trường hợp đơn giản hóa bậc 2)
// Ánh xạ homomorphism phi(a + b*alpha) = (a + b*m) mod N
Y := big.NewInt(1)
for _, idx := range subset {
rel := gnfs.relations[idx]
val := big.NewInt(rel.a + rel.b*gnfs.m)
val.Mod(val, gnfs.N)
Y.Mul(Y, val)
Y.Mod(Y, gnfs.N)
}
// Y_root = sqrt(Y) mod N.
// Trong mô hình giáo khoa, vì prod (a + bm) = X^2 trên Z,
// và phi(beta)^2 = X^2 (mod N), ta trực tiếp dùng quan hệ chênh lệch bình phương:
// gcd(X - Y_proj, N)
diff := new(big.Int).Sub(X, big.NewInt(gnfs.m))
diff.Abs(diff)
factor := gcd(diff, gnfs.N)
if factor.Cmp(big.NewInt(1)) > 0 && factor.Cmp(gnfs.N) < 0 {
other := new(big.Int).Div(gnfs.N, factor)
return factor, other
}
// Thử thêm với tổng X + Y_proj
sum := new(big.Int).Add(X, big.NewInt(gnfs.m))
factor = gcd(sum, gnfs.N)
if factor.Cmp(big.NewInt(1)) > 0 && factor.Cmp(gnfs.N) < 0 {
other := new(big.Int).Div(gnfs.N, factor)
return factor, other
}
return nil, nil
}
func main() {
// Phân tích một số RSA mẫu 40-bit: N = 1000003 * 1000033 = 1000036000099
pExpected := big.NewInt(1000003)
qExpected := big.NewInt(1000033)
N := new(big.Int).Mul(pExpected, qExpected)
fmt.Printf("=== PHÂN TÍCH RSA BẰNG GENERAL NUMBER FIELD SIEVE (GNFS) ===\n")
fmt.Printf("Mục tiêu N = %s (Số bit: %d)\n\n", N.String(), N.BitLen())
solver := NewMiniGNFS(N)
p, q := solver.Factorize()
if p != nil && q != nil {
fmt.Printf("\n[THÀNH CÔNG] Đã bẻ khóa và tìm thấy hai thừa số nguyên tố:\n")
fmt.Printf(" p = %s\n", p.String())
fmt.Printf(" q = %s\n", q.String())
fmt.Printf("Kiểm chứng: p * q == N? -> %t\n",
new(big.Int).Mul(p, q).Cmp(N) == 0)
} else {
fmt.Println("\n[KẾT QUẢ] Cần tăng miền sàng (Bound A, B) để thu thập thêm quan hệ.")
}
}
6. Phân tích Độ phức tạp và Cơ chế Đạt mức
Tại sao GNFS lại vượt qua ranh giới của Quadratic Sieve để chạm tới ?
Cân bằng tối ưu giữa Kích thước Số và Kích thước Cơ sở thừa số
-
Trong GNFS, ta chọn bậc đa thức:
-
Khi đó, cơ số .
-
Chuẩn của nhánh hữu tỉ và chuẩn đại số đều có độ lớn bị chặn bởi:
-
Lưu ý rằng số mũ của ở đây chỉ là , nhỏ hơn rất nhiều so với số mũ (tức kích thước ) trong Sàng bậc hai!
-
Khi tối ưu hóa điểm cân bằng giữa thời gian sàng số trơn (tăng khi cơ sở nhỏ) và thời gian giải ma trận thưa (tăng khi cơ sở lớn), điểm dừng tối ưu đạt được chính xác tại:
7. Các Thách thức Kỹ thuật Lớn nhất trong Hiện thực Thực tế
-
Vấn đề Đơn vị và I-đê-an (Units and Ideal Classes in ):
Vành nói chung không phải là vành phân tích duy nhất (Unique Factorization Domain - UFD). Một phần tử có chuẩn là số chính phương trong chưa chắc đã là một bình phương hoàn hảo trong do sự can thiệp của nhóm đơn vị (Unit Group) và nhóm lớp i-đê-an (Class Group). Leonard Adleman đã giải quyết bài toán này bằng cách thêm vào ma trận các cột kiểm tra Ký tự bậc hai (Quadratic Characters) , đảm bảo tích thu được là một bình phương thực sự trong .
-
Khai căn Đại số Khổng lồ (Algebraic Square Root):
Biểu thức bao gồm hàng chục triệu nhân tử. Khai triển trực tiếp sẽ tạo ra các số nguyên có hàng tỷ chữ số. Thuật toán của Couveignes và Montgomery tính toán căn bậc hai bằng cách chiếu modulo nhiều i-đê-an nguyên tố nhỏ , khai căn trong trường hữu hạn , rồi khôi phục lại phần tử thông qua Định lý Số dư Trung Hoa (CRT).
-
Đại số Tuyến tính Siêu Quy mô:
Trong các kỷ lục RSA, ma trận có kích thước vượt quá với hàng tỷ phần tử khác không. Việc giải hệ phương trình đòi hỏi các cụm siêu máy tính chạy thuật toán Block Lanczos song song phân tán liên tục trong nhiều tháng với hàng terabyte bộ nhớ RAM.
8. Tác động Thực tế đối với Chuẩn Mật mã Toàn cầu
| Cột mốc RSA | Năm bẻ khóa | Đơn vị thực hiện | Tài nguyên tính toán GNFS |
|---|---|---|---|
| RSA-512 (155 chữ số) | 1999 | CWI, Inria | 3.7 tháng trên cụm máy tính |
| RSA-768 (232 chữ số) | 2009 | Kleinjung et al. | ~1.500 năm CPU (Core 2 Duo 2.2GHz) |
| RSA-240 (795 bit) | 2019 | Inria, UCSD | ~900 năm CPU |
| RSA-250 (829 bit) | 2020 | CWI, Inria, UCSD | ~2.700 năm CPU |
Định hình Tiêu chuẩn An toàn Mật mã
-
Khai tử RSA-512 và RSA-1024: Sự phát triển của phần cứng và thuật toán GNFS đã biến khóa RSA-1024 thành mục tiêu có thể bị các cơ quan an ninh quốc gia giải mã. NIST đã chính thức loại bỏ RSA-1024 khỏi các tiêu chuẩn sử dụng từ năm 2013.
-
Quy định Khóa Hiện tại (NIST / BSI): Độ dài khóa RSA tối thiểu được khuyến nghị cho các giao dịch bảo mật là RSA-2048 (an toàn đến năm 2030) và RSA-3072 / RSA-4096 cho các dữ liệu cần lưu trữ dài hạn.
-
Thúc đẩy Chuyển dịch Mật mã học: Vì độ phức tạp của GNFS chỉ là , muốn tăng gấp đôi độ an toàn của RSA đòi hỏi phải tăng độ dài khóa theo hàm lập phương, khiến chi phí tính toán phần cứng tăng vọt. Đây là lý do chính thúc đẩy ngành mật mã toàn cầu chuyển dịch sang Mật mã đường cong Elliptic (ECC) (nơi bài toán Logarit rời rạc ECDLP vẫn giữ độ phức tạp hàm mũ ) và chuẩn bị cho các chuẩn Mật mã Hậu lượng tử (PQC).
All rights reserved