NIST 양자내성 표준을 실제 메신저에 넣어본 안드로이드 앱. 서버는 암호문만 보관하고, 복호화 키는 각자의 기기를 떠나지 않습니다.
오늘 웹과 메신저가 쓰는 암호는 큰 수를 소인수분해하기 어렵다는 성질에 기대고 있습니다. 양자컴퓨터는 바로 그 계산을 빠르게 합니다.
당장 양자컴퓨터가 없어도 문제입니다. 지금 오가는 암호문을 저장해뒀다가 나중에 푸는 방식이 가능하기 때문입니다. 오래 비밀이어야 하는 대화일수록 지금부터 대비해야 합니다.
NIST가 2024년에 대체 표준을 확정했습니다. 다만 그 표준을 실제 메신저에 어떻게 넣는가는 별개의 문제입니다. 암호 자체보다 키를 언제 만들고 언제 버리고 어떻게 나눠주느냐가 훨씬 어렵습니다. 이 프로젝트는 그 부분을 직접 해본 기록입니다.
나머지 코드는 완전히 같고, 키 교환 계층만 다릅니다. 두 브랜치의 차이는 암호 파일 열 개뿐입니다.
BouncyCastle의 ML-KEM-768을 그대로 사용합니다. 부채널 대응과 상수 시간 보장이 들어간 검증된 구현이고, 실제로 쓸 거라면 이쪽입니다.
CRYSTALS-Kyber 논문의 Algorithm 1~5를 라이브러리 없이 구현했습니다. 다항식 곱셈은 NTT 대신 학교식으로 두어 무엇을 하는지 보이게 했습니다.
표준을 쓸 줄 아는 것과 이해한 것은 다르다고 봤습니다. 직접 만들어보니 왜 그렇게 설계됐는지 — 부동소수점을 쓰지 않는 이유, 복호화 실패를 그냥 실패로 알리지 않는 이유 — 가 보였고, 라이브러리를 쓸 때도 무엇을 믿고 쓰는 것인지 알게 됐습니다.
| 쓰임 | 알고리즘 | 비고 |
|---|---|---|
| 키 교환 | ML-KEM-768 | FIPS 203 |
| 전자서명 | ML-DSA-65 | FIPS 204 |
| 메시지 암호화 | AES-256-GCM | 인증 태그 128비트 |
| 키 유도 | HKDF-SHA256 | |
| 기기 저장소 | SQLCipher | 계정별 패스프레이즈 |
| 전송 구간 | TLS 1.3 | Let's Encrypt |
안드로이드는 Kotlin과 Jetpack Compose, 서버는 Spring Boot와 PostgreSQL을 씁니다. 실시간 전달은 WebSocket 위의 STOMP를 쓰고, AWS EC2에 Nginx를 앞에 두어 배포했습니다.
보안 제품은 무엇을 했는지보다 무엇을 하지 않았는지가 더 중요합니다. 알고 있는 한계를 적어둡니다.
두 갈래의 차이는 git diff 로 바로 확인할 수 있습니다.