SequenceHash: Chuẩn mới cho băm đa thành phần an toàn

Công nghệ02 tháng 10, 2026·7 phút đọc

Trail of Bits ra mắt SequenceHash và SequenceMAC — bộ công cụ băm đa thành phần (multihashing) không phụ thuộc vào Keccak, hỗ trợ SHA2, BLAKE, RIPEMD. Giải pháp này khắc phục lỗ hổng từ việc nối chuỗi đầu vào đơn giản, vốn có thể gây giả mạo trong chứng minh zero-knowledge.

SequenceHash: Chuẩn mới cho băm đa thành phần an toàn

Băm đa thành phần (multihashing) là một trong những tác vụ mật mã dễ bị xem nhẹ. Điều này đáng lo ngại, bởi đây là điểm vấp ngã phổ biến khi các chuyên gia mật mã sử dụng hàm băm.

Với mục tiêu "sửa phần mềm, không sửa lỗi", Trail of Bits giới thiệu SequenceHash và người anh em SequenceMAC — cặp cấu trúc băm mang đến giải pháp băm đa thành phần an toàn cho các nhà phát triển dùng hàm băm khác ngoài Keccak. Đặc tả này là mã nguồn mở và đã trở thành một phần của Dự án Đặc tả Mật mã Cộng đồng (C2SP).

Multihashing là gì và tại sao lại nguy hiểm?

"Multihashing" nghĩa là "băm một loạt giá trị lại với nhau". Nếu bạn từng đọc bài báo mật mã có bước kiểu "tính toán authenticator chung N = Hash(X, Y, Z, A, B)", đó chính là multihashing.

Cách "giải quyết" đơn giản là nối các đầu vào rồi băm kết quả. Nhưng cách này có thể dẫn đến vấn đề bảo mật nghiêm trọng. Ví dụ, khi băm ba đầu vào bằng SHA256 "thô":

import hashlib
hasher = hashlib.new('sha256')
hasher.update(b'Test 0')
hasher.update(b'Test 1')
hasher.update(b'Test 2')
print(hasher.hexdigest())

hasher = hashlib.new('sha256')
hasher.update(b'Test 0Test 1')
hasher.update(b'Test 2')
print(hasher.hexdigest())

hasher = hashlib.new('sha256')
hasher.update(b'Test 0')
hasher.update(b'')
hasher.update(b'Test 1Test 2')
print(hasher.hexdigest())

Cả ba đoạn code cho ra cùng một kết quả băm. Dù các đầu vào được nạp qua nhiều lần gọi khác nhau, hàm băm không phân biệt được chúng — về bản chất, các đầu vào chỉ bị nối lại.

Tại sao điều này lại quan trọng?

Multihashing là thành phần then chốt của một trong những công cụ quan trọng nhất trong chứng minh zero-knowledge: phép biến đổi Fiat-Shamir. Khi làm sai multihashing, bạn mở ra nguy cơ giả mạo trong chứng minh zero-knowledge — và vì zero-knowledge đóng vai trò lớn trong tiền mã hóa hiện nay, sai lầm kiểu này có thể gây thiệt hại hàng triệu đô la.

Ngoài Fiat-Shamir, multihashing còn xuất hiện ở nhiều nơi khác:

  • Xác thực các tệp trong một kho lưu trữ
  • Nhóm nhiều giao dịch tiền mã hóa vào một hash duy nhất
  • Băm những thứ "đơn giản" như tên người
  • Tạo cam kết mật mã (cryptographic commitment) cho các giá trị

Vấn đề là không ai đạt được một giải pháp chuẩn thống nhất. Trong hệ sinh thái mã nguồn mở và qua các cuộc kiểm toán, các nhà phát triển giải quyết theo vô số cách khác nhau — có cách không an toàn, có cách mã hóa quá phức tạp và gây rủi ro về timing.

Hạn chế của TupleHash

Chuẩn phổ biến nhất cho multihashing là TupleHash, định nghĩa trong NIST SP 800-185. TupleHash rất tốt: nó giải quyết bài toán theo cách đơn giản (mã hóa độ dài tiền tố), xử lý đầu vào gần như vô hạn, và hoạt động như một XOF.

Tuy nhiên, TupleHash chỉ được định nghĩa để làm việc với Keccak — hàm nền tảng của SHA3. Nếu thay Keccak bằng hàm băm khác, nhiều tính năng bảo mật quan trọng (như chống mở rộng độ dài) sẽ biến mất. Với việc SHA3 và Keccak chưa được ứng dụng rộng rãi trong thập kỷ qua, điều này khiến nhiều chuyên gia mật mã bị bỏ lại phía sau — đặc biệt trong lĩnh vực hợp đồng chính phủ, nơi CNSA 2.0 đã bắt buộc dùng SHA384 và SHA512 cho gần như mọi thứ.

SequenceHash giải quyết thế nào?

SequenceHash là cấu trúc multihashing không phụ thuộc hàm băm, tương tự cách HMAC là cấu trúc MAC không phụ thuộc hàm băm. Bạn có thể dùng nó với SHA2, BLAKE, RIPEMD và nhiều hơn nữa.

Bốn tính năng chính

1. Mã hóa đầu vào không nhập nhằng

Với hai đầu vào (A, B), bạn được đảm bảo không tồn tại chuỗi đầu vào nào khác cho ra cùng kết quả. Nói cách khác, các giá trị bạn băm được đảm bảo "khác biệt về ngữ nghĩa" — không thể tráo đổi phần đầu và phần cuối của các đầu vào.

2. Chống tấn công mở rộng độ dài

Nhiều hàm băm phổ biến như SHA256 và SHA512 dễ bị tấn công mở rộng độ dài. Nếu Alice có giá trị bí mật A và gửi H(A) cho Bob, Bob có thể tạo giá trị B để tính H(A||B) dù không biết A. SequenceHash dùng cấu trúc băm kép để ngăn chặn điều này.

3. Chuỗi tùy biến tích hợp

SequenceHash cho phép gắn giá trị băm với một bước cụ thể trong giao thức hoặc một phiên bản cụ thể của giao thức, ngăn chặn tấn công phát lại. Chuỗi tùy biến chỉ được tích hợp vào lớp ngoài của cấu trúc băm kép, nên nếu cần băm cùng giá trị với nhiều chuỗi tùy biến, lớp băm trong có thể tái sử dụng.

4. Chế độ MAC

SequenceMAC có cấu trúc tương tự HMAC, hỗ trợ khóa từ 32 byte trở lên (tối đa ${2}^{128}-1$ byte). Khác với HMAC, SequenceMAC tích hợp metadata về khóa và chuỗi tùy biến để tránh vấn đề "giả va chạm khóa" (key pseudocollision).

Cách hoạt động

SequenceHash dùng mã hóa độ dài để mã hóa đầu vào không nhập nhằng. Thay vì ghi số bit dưới dạng số nguyên độ dài thay đổi, SequenceHash mã hóa số byte thành số nguyên 128-bit cố định. Nó dùng mã hóa hậu tố độ dài (length-suffix encoding), cho phép nhà phát triển xây dựng API streaming khi cần băm dữ liệu có độ dài chưa biết trước.

Giới hạn ${2}^{128}-1$ byte vượt xa mọi nhu cầu thực tế, và còn vượt cả giới hạn đầu vào của SHA256 (${2}^{61}$ byte) lẫn SHA512 (${2}^{125}$ byte).

Sử dụng trong thực tế

Đã có ba bản triển khai sẵn sàng: Rust, Go và Python. Đặc tả có sẵn trong C2SP, kèm test vector để bạn tự viết bản triển khai riêng.

Điểm khác biệt chính của API SequenceHash là mọi cập nhật đều nguyên tử — mỗi lần ghi một giá trị vào đối tượng băm, nó được thêm vào như một đối tượng độc lập, đã mã hóa độ dài:

import sequencehash
hasher = sequencehash.SequenceHash.new('sha256')
hasher.add(b'Test 0')
hasher.add(b'Test 1')
hasher.add(b'Test 2')
print(hasher.result().hex())

Ba cách nạp đầu vào khác nhau giờ cho ra ba kết quả băm hoàn toàn không liên quan. Tương tự, băm cùng đầu vào với các chuỗi tùy biến khác nhau cũng cho ra các kết quả độc lập.

Lưu ý về kích thước khóa

SequenceMAC có kích thước khóa tối thiểu 32 byte (256 bit). Với hàm băm 256-bit tốt, điều này tương ứng mức bảo mật khoảng 128-bit chống tấn công giả mạo.

Cần nhớ rằng "khóa dài hơn" không đồng nghĩa với "bảo mật cao hơn". Hàm băm nền tảng và bước tiền xử lý khóa đặt ra giới hạn cứng cho mức bảo mật tổng thể. Với các hàm băm có đầu ra nhỏ hơn 256 bit (như SHA224 hay RIPEMD160), mức bảo mật sẽ thấp hơn — đặc tả SequenceMAC khuyến cáo mạnh mẽ việc tránh dùng các hàm băm có đầu ra ngắn.

Những cảnh báo cần lưu ý

SequenceHash là công cụ, không phải thuốc chữa bách bệnh. Bạn vẫn cần:

  • Đảm bảo băm đúng đầu vào. JSON và XML không phải lúc nào cũng đảm bảo thứ tự trường.
  • Dùng phương thức mã hóa nhất quán cho chuỗi ("cả thế giới là ASCII" là điều không đúng và chưa bao giờ đúng).
  • Với Fiat-Shamir, phải cẩn thận bao gồm tất cả đầu vào — chứng minh Schnorr nên luôn bao gồm group descriptor, phần tử generator, v.v.
  • Chọn hàm băm có đầu ra đủ lớn để tránh sai lệch modulo khi đầu ra được diễn giải là số nguyên modulo một giá trị khác.

Kết luận

SequenceHash và SequenceMAC mang đến giải pháp gọn gàng, chuẩn hóa cho bài toán multihashing mà không bị ràng buộc vào Keccak. Với ba bản triển khai sẵn dùng và test vector đầy đủ, đây là công cụ đáng cân nhắc cho bất kỳ nhà phát triển nào làm việc với mật mã học — đặc biệt trong bối cảnh các lỗ hổng multihashing có thể gây thiệt hại nghiêm trọng trong ứng dụng zero-knowledge và blockchain.

Đặc tả đã có trên C2SP, các bản triển khai Rust, Go, Python sẵn sàng sử dụng ngay hôm nay, kèm test vector nếu bạn muốn tự viết bản triển khai riêng.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗