- Đã phát hiện lỗi xác thực điểm không phù hợp trên đường cong Edwards25519 trong hàm cấp thấp
crypto_core_ed25519_is_valid_point() của libsodium
- Hàm này lẽ ra phải xác nhận rằng điểm thuộc nhóm mật mã chính, nhưng lại cho phép sai một số điểm thuộc nhóm có bậc hỗn hợp (subgroup)
- Nguyên nhân là lỗi mã trong bước kiểm tra tọa độ nội bộ: chỉ kiểm tra X=0 mà bỏ sót việc xác minh Y=Z, khiến các điểm không hợp lệ có thể bị coi là hợp lệ
- Ở phiên bản đã sửa, hàm được thay đổi để kiểm tra cả hai điều kiện (X=0, Y=Z), và các phiên bản 1.0.20 trở xuống hoặc các bản phát hành trước ngày 30 tháng 12 năm 2025 đều bị ảnh hưởng
- API cấp cao (
crypto_sign_*) không bị ảnh hưởng, và nên dùng nhóm Ristretto255 để có độ an toàn và hiệu năng tốt hơn
Tổng quan về dự án libsodium
- libsodium là một dự án bắt đầu từ 13 năm trước, với mục tiêu cung cấp API đơn giản để việc sử dụng mật mã học trở nên dễ dàng hơn
- Được thiết kế để người dùng chỉ cần thực hiện các phép toán cấp cao mà không cần biết các thuật toán bên trong
- Dự án coi trọng việc duy trì tính tương thích của API và đã giữ được sự nhất quán cho đến nay dựa trên API của NaCl
- Khi một số người dùng trực tiếp dùng các hàm cấp thấp vượt ra ngoài những giới hạn đã nêu trong tài liệu, ngày càng có nhiều trường hợp thư viện được sử dụng như một bộ công cụ mật mã học
Nguyên nhân của lỗi được phát hiện
- Hàm có vấn đề:
crypto_core_ed25519_is_valid_point()
- Trên đường cong Edwards25519, hàm này phải từ chối các điểm không thuộc nhóm chính (bậc L)
- Tuy nhiên, một số điểm thuộc bậc hỗn hợp (2L, 4L, 8L, v.v.) vẫn vượt qua được bước xác thực
- Bên trong, để kiểm tra bậc của điểm, hàm nhân điểm với L rồi kiểm tra xem kết quả có phải là điểm đơn vị (identity) hay không
- Điểm đơn vị được biểu diễn dưới dạng X=0, Y=Z, nhưng mã cũ chỉ kiểm tra X=0
- Vì vậy, các điểm sai có Y≠Z vẫn bị xử lý là hợp lệ
- Ví dụ: với điểm Q thuộc nhóm chính, khi cộng thêm điểm bậc 2 là (0, -1), thì Q+(0, -1) là điểm sai, nhưng trước khi sửa nó vẫn được chấp nhận
Nội dung bản sửa lỗi
- Commit vá lỗi đã thay đổi như sau
- Mã cũ:
return fe25519_iszero(pl.X);
- Mã đã sửa:
fe25519_sub(t, pl.Y, pl.Z); return fe25519_iszero(pl.X) & fe25519_iszero(t);
- Giờ đây, hàm kiểm tra đồng thời hai điều kiện X=0 và Y=Z để thực hiện xác thực chính xác
Phạm vi ảnh hưởng
- Có thể bị ảnh hưởng nếu thuộc các trường hợp sau
- Sử dụng phiên bản 1.0.20 trở xuống hoặc bản phát hành trước ngày 30 tháng 12 năm 2025
- Dùng
crypto_core_ed25519_is_valid_point() để xác thực các điểm đầu vào không đáng tin cậy
- Là người dùng tự triển khai trực tiếp các phép toán trên đường cong Edwards25519
- Tuy nhiên, phần lớn người dùng không bị ảnh hưởng
- API cấp cao (
crypto_sign_*) không sử dụng hàm này
crypto_scalarmult_ed25519 không làm rò rỉ thông tin ngay cả với khóa công khai không hợp lệ
- Các khóa được tạo bằng
crypto_sign_keypair và crypto_sign_seed_keypair đều thuộc đúng nhóm
Khuyến nghị
- Khuyến nghị sử dụng nhóm Ristretto255
- Đã được tích hợp vào libsodium từ năm 2019 và giải quyết các vấn đề liên quan đến cofactor
- Các điểm sau khi giải mã tự động an toàn, không cần xác thực thêm
- Hiệu năng tính toán nhanh hơn Edwards25519
- Nếu không thể cập nhật, có thể xác thực bằng hàm thay thế ở cấp ứng dụng được đề xuất là
is_on_main_subgroup
Bản phát hành đã sửa và hỗ trợ
- Ngay sau khi phát hiện vấn đề, bản sửa đã được áp dụng ngay và đã có trong mọi phiên bản ổn định phát hành sau ngày 30 tháng 12 năm 2025
- Bao gồm tarball chính thức, binary cho Visual Studio/MingW, gói NuGet, bản build cho Android, xcframework của
swift-sodium, Rust libsodium-sys-stable, và libsodium.js
- Một point release mới cũng đang được lên kế hoạch
- Dự án được duy trì bởi một người duy nhất, và có thể hỗ trợ việc cải tiến liên tục thông qua tài trợ OpenCollective
1 bình luận
Ý kiến trên Hacker News
Thư viện PHP sodium_compat cũng bị ảnh hưởng bởi vấn đề lần này
Có thể xem nội dung liên quan trong security-advisories PR #756
Tối nay tôi dự định kiểm tra toàn bộ các implementation Ed25519 khác trong hệ sinh thái mã nguồn mở để xác nhận xem có cùng lỗi thiếu bước xác minh hay không
Tuy nhiên, không có trường hợp nào được triển khai sai cách giống như lỗ hổng được nhắc ở trên
Nếu tôi chưa gửi email cho bạn, thì rất có thể nó không có trong danh sách triển khai Ed25519 của ianix, hoặc tôi đã bỏ sót nó, hoặc đó là một implementation an toàn
Tôi đã phát triển binding sodium cho Lean4 trong 4 tháng qua
Giờ đã đến giai đoạn Ristretto255, và tôi hiểu vì sao tác giả lại hứng thú với công nghệ này đến vậy
Ristretto là một API tinh vi cho phép xây dựng các đa thức tùy ý trên Curve25519, và quá trình thử nghiệm thực sự rất thú vị
Nếu tác giả đọc được bài này, tôi muốn chân thành gửi lời cảm ơn
Mục tiêu của Libsodium là cung cấp API cấp cao, chứ không phải các hàm cấp thấp
Nó được thiết kế để người dùng không cần biết thuật toán bên trong, nhưng theo thời gian ngày càng có nhiều trường hợp dùng trực tiếp các hàm cấp thấp
Cuối cùng Libsodium đã bị dùng như một bộ công cụ thuật toán
Điều quan trọng là nhận ra người dùng muốn đi theo hướng nào, và không ép dự án phải theo đúng một cách duy nhất
Một số dự án trở nên giáo điều ở điểm này và rồi thất bại
Việc người không chuyên trực tiếp dùng các primitive mật mã là rất nguy hiểm
Libsodium được thiết kế để người dùng không tự đưa mình vào tình huống rủi ro
Lý tưởng nhất là thư viện phải khiến việc dùng sai trở nên bất khả thi
Tôi đề xuất bài viết liên quan “If You're Typing The Letters A-E-S Into Your Code, You're Doing It Wrong”
Vì vậy, trong nhiều trường hợp việc giới hạn một số chức năng thành private hoặc internal là lựa chọn đúng đắn
Tôi không chắc Libsodium đã đặt ranh giới đó tốt đến mức nào, nhưng sự cân bằng là rất quan trọng
Nhưng rồi một số người dùng lại dùng nó như một trình chạy batch
Tôi đã sửa vài lỗi để hỗ trợ nhu cầu của họ
Cuối cùng, chỉ riêng việc có người dùng thôi cũng đã khiến tôi vui rồi
Lỗi lần này là một lỗi xác minh mật mã tinh vi nhưng quan trọng
Một phép kiểm tra tưởng như đơn giản là “xác nhận hợp lệ” thực ra lại rất phức tạp
Nếu cho phép các điểm nằm ngoài nhóm con cấp số nguyên tố, thì dù trước mắt có vẻ chưa gây ra lỗ hổng ngay lập tức, nó vẫn có thể làm sụp đổ các giả định ở lớp trên
Ngoài ra, các primitive cấp thấp thường được tái sử dụng rộng hơn nhiều so với mục đích ban đầu, nên một thiếu sót nhỏ trong bước xác minh có thể tạo ra ảnh hưởng rất lớn
Vấn đề nhóm con chỉ xuất hiện khi xây dựng các giao thức phức tạp hơn trên Curve25519
Vì vậy tôi luôn ánh xạ lại mọi điểm vào nhóm con cấp số nguyên tố bất cứ khi nào có thể
Monocypher có những hàm nâng cao như vậy
Ví dụ như
crypto_x25519_dirty_fast()haycrypto_elligator_map()Các hàm “dirty” này tạo ra khóa công khai phủ toàn bộ đường cong, khiến chúng không thể phân biệt với tính ngẫu nhiên
Sau đó khi dùng trao đổi khóa X25519, vẫn có thể thu được cùng một bí mật chia sẻ
Điều này có được là nhờ thiết kế của DJB, vì ngay cả khi khóa công khai bất thường thì bí mật chia sẻ vẫn được ánh xạ vào nhóm con cấp số nguyên tố
Cuối cùng, Ristretto chỉ cần thiết khi không thể thực hiện kiểu ánh xạ lại này
Tất nhiên, phép trừu tượng hóa nhóm cấp số nguyên tố là hữu ích, nhưng ai đủ khả năng thiết kế những giao thức như vậy thì cũng nên có khả năng xử lý cofactor không tầm thường
Nếu bạn làm ở một công ty lớn, tôi khuyên nên cân nhắc hỗ trợ Frank ở cấp độ công ty
Kể cả có biết thì có lẽ tôi cũng phải tài trợ bằng tiền cá nhân, chứ không phải từ tài khoản công ty
Tôi tự hỏi liệu libnacl có bị ảnh hưởng hay không
Tôi dùng phần mềm được biên dịch bằng libnacl mỗi ngày, nhưng không có gì được biên dịch bằng “libsodium”
Đây thực sự là một thư viện tuyệt vời
Xin gửi lời cảm ơn tới Frank Denis