Libsodium의 취약점
(00f.net)-
libsodium의 저수준 함수
crypto_core_ed25519_is_valid_point()에서 Edwards25519 곡선의 부적절한 점 검증 오류가 발견됨 - 이 함수는 점이 주된 암호학적 그룹에 속하는지 확인해야 하지만, 혼합 차수(subgroup) 의 일부 점을 잘못 통과시킴
- 원인은 내부 좌표 검증 시 X=0만 확인하고 Y=Z 검증을 누락한 코드 오류로, 잘못된 점이 유효하다고 처리될 수 있었음
- 수정 버전에서는 두 조건(X=0, Y=Z)을 모두 검사하도록 변경되었으며, 1.0.20 이하 버전 또는 2025년 12월 30일 이전 릴리스가 영향을 받음
- 고수준 API(
crypto_sign_*)는 영향을 받지 않으며, Ristretto255 그룹 사용이 안전성과 성능 면에서 권장됨
libsodium 프로젝트 개요
-
libsodium은 13년 전 시작된 프로젝트로, 암호학을 쉽게 사용할 수 있도록 단순한 API를 제공하는 것을 목표로 함
- 내부 알고리듬을 사용자가 알 필요 없이 고수준 연산만 수행할 수 있도록 설계
- API의 호환성 유지를 중시하며, NaCl API를 기반으로 현재까지 일관성을 유지
- 일부 사용자가 문서에 명시된 제한을 넘어 저수준 함수를 직접 사용하면서, 라이브러리가 암호학 툴킷처럼 활용되는 사례가 늘어남
발견된 버그의 원인
- 문제 함수:
crypto_core_ed25519_is_valid_point()- Edwards25519 곡선에서 주된 그룹(L 차수)에 속하지 않은 점을 거부해야 함
- 그러나 혼합 차수(2L, 4L, 8L 등) 의 점 일부가 검증을 통과함
- 내부적으로 점의 차수를 확인하기 위해 L을 곱한 뒤, 결과가 항등점(identity) 인지 검사
- 항등점은 X=0, Y=Z 형태로 표현되지만, 기존 코드는 X=0만 검사
- 이로 인해 Y≠Z인 잘못된 점이 유효하다고 처리됨
- 예시: 주된 그룹의 점 Q에 차수 2의 점 (0, -1)을 더한 Q+(0, -1)은 잘못된 점이지만, 수정 전에는 통과함
수정 내용
-
패치 커밋은 다음과 같이 변경됨
-
기존 코드:
return fe25519_iszero(pl.X); -
수정 코드:
fe25519_sub(t, pl.Y, pl.Z); return fe25519_iszero(pl.X) & fe25519_iszero(t);
-
기존 코드:
- 이제 X=0과 Y=Z 두 조건을 모두 확인하여 올바른 검증 수행
영향 범위
- 다음 조건에 해당하는 경우 영향 가능
- 버전 1.0.20 이하 또는 2025년 12월 30일 이전 릴리스 사용
-
crypto_core_ed25519_is_valid_point()로 신뢰할 수 없는 입력 점을 검증 - Edwards25519 곡선 연산을 직접 구현하는 사용자
- 그러나 대부분의 사용자는 영향 없음
- 고수준 API(
crypto_sign_*)는 해당 함수를 사용하지 않음 -
crypto_scalarmult_ed25519는 잘못된 공개키로도 정보 누출 없음 -
crypto_sign_keypair및crypto_sign_seed_keypair로 생성된 키는 올바른 그룹에 속함
- 고수준 API(
권장 조치
-
Ristretto255 그룹 사용 권장
- 2019년부터 libsodium에 포함되어 있으며, cofactor 관련 문제를 해결
- 디코딩된 점은 자동으로 안전하며, 추가 검증 불필요
- Edwards25519보다 빠른 연산 성능 제공
- 업데이트가 불가능한 경우, 제시된 애플리케이션 수준 대체 함수(
is_on_main_subgroup)를 사용해 검증 가능
수정된 배포 및 지원
- 문제 발견 직후 즉시 수정되어, 2025년 12월 30일 이후 배포된 모든 안정 버전에 포함
- 공식 tarball, Visual Studio/MingW 바이너리, NuGet 패키지, Android용 빌드,
swift-sodiumxcframework, Rustlibsodium-sys-stable,libsodium.js포함
- 공식 tarball, Visual Studio/MingW 바이너리, NuGet 패키지, Android용 빌드,
- 새로운 point release도 예정
- 프로젝트는 1인 유지보수로 운영되며, OpenCollective 후원을 통해 지속적인 개선 지원 가능
Hacker News 의견들
-
PHP 라이브러리 sodium_compat도 이번 문제의 영향을 받았음
관련 내용은 security-advisories PR #756에서 확인 가능함
오늘 저녁에는 오픈소스 생태계 내 다른 Ed25519 구현체들을 모두 점검해, 동일한 검증 누락이 있는지 확인해볼 예정임- 몇몇 라이브러리에서 아예 검증 로직이 빠져 있음을 발견했음
하지만 위에서 언급된 취약점처럼 잘못된 방식으로 구현된 사례는 없었음
내가 이메일을 보내지 않았다면, 아마 ianix의 Ed25519 배포 목록에 없거나, 내가 놓쳤거나, 혹은 안전한 구현일 가능성이 높음 - 오픈소스에 기여해줘서 고맙다는 말을 전하고 싶음
- 몇몇 라이브러리에서 아예 검증 로직이 빠져 있음을 발견했음
-
지난 4개월 동안 Lean4용 sodium 바인딩을 개발 중임
이제 Ristretto255 단계에 도달했는데, 왜 저자가 이 기술에 열광하는지 이해하게 되었음
Ristretto는 Curve25519 위에서 임의의 다항식을 구성할 수 있는 정교한 API로, 실험하는 과정이 정말 즐거움
혹시 저자가 이 글을 본다면, 진심으로 감사의 말을 전하고 싶음- 혹시 이 프로젝트의 공개 저장소가 있는지 궁금함
-
Libsodium의 목표는 저수준 함수가 아니라 고수준 API를 제공하는 것이었음
사용자는 내부 알고리즘을 몰라도 되도록 설계되었지만, 시간이 지나면서 점점 저수준 함수들을 직접 사용하는 사례가 늘어남
결국 Libsodium이 알고리즘 툴킷처럼 쓰이게 되었음
중요한 점은 사용자가 원하는 방향을 인식하고, 프로젝트를 특정 방식으로만 강요하지 않는 것임
일부 프로젝트는 이런 점에서 독단적으로 변하며 실패를 겪음- 흥미로운 지적임. 하지만 반대로 사용자가 원한다고 생각하는 것과 실제로 필요한 것이 다를 수도 있음
비전문가가 암호학적 원시 함수를 직접 사용하는 것은 위험함
Libsodium은 사용자가 스스로를 위험에 빠뜨리지 않도록 설계되었음
라이브러리는 잘못된 사용이 불가능하도록 만드는 것이 이상적임
관련 글로 “If You're Typing The Letters A-E-S Into Your Code, You're Doing It Wrong”을 추천함 - 모든 내부 함수를 API 계약으로 간주하면, 거의 모든 변경이 호환성 깨짐으로 이어짐
따라서 일부 기능을 private 또는 internal로 제한하는 것이 맞는 선택일 때가 많음
Libsodium이 그 경계를 잘 잡았는지는 확신할 수 없지만, 균형이 중요함 - 예전에 CeeFIT이라는 C++ 테스트 프레임워크를 만들었는데, 컴파일 타임에 fixture를 등록하는 방식이 자랑스러웠음
그런데 일부 사용자가 이를 배치 실행기처럼 사용하더라
그들의 요구를 지원하기 위해 몇 가지 버그를 수정했음
결국 사용자가 있다는 사실 자체가 기뻤음
- 흥미로운 지적임. 하지만 반대로 사용자가 원한다고 생각하는 것과 실제로 필요한 것이 다를 수도 있음
-
이번 버그는 미묘하지만 중요한 암호 검증 오류임
“유효한지 확인”이라는 단순한 검사가 실제로는 매우 복잡함
소수 차수 부분군 밖의 점을 허용하면, 즉각적인 취약점이 없어 보여도 상위 계층의 가정을 무너뜨릴 수 있음
또한 저수준 원시 함수는 의도보다 훨씬 널리 재사용되므로, 작은 검증 누락이 큰 파급력을 가질 수 있음- 다만 X25519와 Ed25519는 애초에 이런 검증이 필요 없도록 설계되었음
Curve25519 위에서 더 복잡한 프로토콜을 만들 때만 부분군 문제가 생김
그래서 나는 가능한 한 모든 점을 소수 차수 부분군으로 다시 사상함
Monocypher에는 이런 고급 함수들이 있음
예를 들어crypto_x25519_dirty_fast()나crypto_elligator_map()같은 함수들임
이런 “dirty” 함수는 전체 곡선을 덮는 공개키를 생성해, 무작위성과 구분되지 않게 함
이후 X25519 키 교환 시에도 동일한 공유 비밀을 얻을 수 있음
이는 DJB의 설계 덕분으로, 공개키가 비정상적이어도 공유 비밀이 소수 차수 부분군으로 사상됨
결국 Ristretto는 이런 재사상이 불가능한 경우에만 필요함
물론 소수 차수 그룹 추상화는 유용하지만, 그런 프로토콜을 설계할 수 있는 사람이라면 비자명한 cofactor를 다루는 것도 가능해야 함
- 다만 X25519와 Ed25519는 애초에 이런 검증이 필요 없도록 설계되었음
-
대기업에 근무한다면 Frank를 회사 차원에서 후원하는 것을 고려해보길 권함
- 나는 Apple에 근무하지만, Frank가 누구인지 모르겠고, 후원 절차도 모름
설령 안다고 해도 회사 계좌가 아니라 내 개인 돈으로 후원해야 할 것 같음
- 나는 Apple에 근무하지만, Frank가 누구인지 모르겠고, 후원 절차도 모름
-
libnacl도 영향을 받는지 궁금함
나는 매일 libnacl로 컴파일된 소프트웨어를 쓰지만, “libsodium”으로 컴파일된 것은 없음 -
정말 훌륭한 라이브러리임
Frank Denis에게 감사의 마음을 전함