AEAD nonce를 재사용하면 무엇이 깨지나: AES-GCM과 ChaCha20-Poly1305의 경우

같은 키로 nonce를 두 번 쓰면 AES-GCM과 ChaCha20-Poly1305에서 각각 무엇을 잃는지, 겹치지 않는 nonce는 어떻게 만드는지 RFC 5116과 RFC 8439 기준으로 정리합니다.

cryptographyaeadaes-gcmchacha20-poly1305nonce

RFC 5116에 등록된 AES-GCM(AEAD_AES_128_GCM, AEAD_AES_256_GCM)이나 RFC 8439의 ChaCha20-Poly1305로 데이터를 암호화할 때는 키 말고도 12바이트짜리 nonce를 넘긴다. nonce는 암호문 옆에 그대로 저장하거나 함께 보내도 된다. 지켜야 할 규칙은 하나, 같은 키로 암호화할 때 같은 nonce를 두 번 쓰지 않는 것이다. 이 규칙을 어겼을 때 무엇을 잃는지는 알고리즘마다 다르고, AES-GCM은 그 대가가 특히 크다.

이 글은 AEAD 인터페이스와 AES-GCM·AES-CCM 알고리즘을 정의한 RFC 5116(2008)과, ChaCha20-Poly1305를 정의하고 RFC 7539를 대체한 RFC 8439(2018)를 근거로 한다. 두 문서 모두 2026-10-12에 확인했다. GCM 자체의 명세인 NIST SP 800-38D는 이 글의 근거로 쓰지 않았다. 그래서 GCM에서 nonce(IV)를 무작위로 만들 때의 한도 같은 내용은 다루지 않는다.

AEAD 인터페이스가 정한 것

RFC 5116 §2.1에서 인증 암호화(authenticated encryption)의 입력은 네 가지다. 비밀 키 K, nonce N, 평문 P, 그리고 암호화하지 않고 인증만 할 연관 데이터(associated data) A다. nonce에 대한 요구는 이렇다.

  • 키 값이 같다면, 서로 다른 암호화 호출에 넘기는 nonce는 모두 달라야 한다(MUST). 유일한 예외는 그 키로 쓰는 nonce가 전부 길이 0인 경우다.
  • 길이는 12옥텟(바이트)을 권장한다(SHOULD).
  • nonce는 암호문과 함께 저장하거나 전송해도 되고, 복호화 직전에 순서 같은 맥락에서 다시 만들어도 된다. 알고리즘 내부에서 인증되므로 A에 따로 넣을 필요도 없다.

길이 0 nonce 예외는 AES-GCM과 ChaCha20-Poly1305에는 해당하지 않는다. 그 예외는 무작위(randomized) 또는 상태 유지(stateful) 알고리즘을 위한 것인데(§3, §4), RFC 5116의 AEAD_AES_128_GCM과 AEAD_AES_256_GCM, RFC 8439의 AEAD_CHACHA20_POLY1305는 모두 nonce 길이의 최솟값과 최댓값(N_MIN, N_MAX)이 12옥텟이다.

중요한 점은 RFC 5116의 공통 인터페이스가 “nonce가 겹치면 이렇게 된다”를 하나로 정하지 않는다는 것이다. §4는 알고리즘 명세마다 nonce를 실수로 재사용했을 때 보안이 어떻게 떨어지는지 설명하라고(SHOULD) 요구한다. 그래서 결과는 알고리즘별로 따로 읽어야 한다.

AES-GCM: 두 메시지의 기밀성과 그 키의 인증 전체

RFC 5116 §5.1.1은 GCM에서 같은 키와 같은 nonce로 서로 다른 평문을 두 번 암호화하면 두 가지가 무너진다고 적는다.

  1. 기밀성. 두 암호문을 본 공격자는 두 평문의 XOR을 복원할 수 있다.
  2. 무결성. 공격자는 무결성 보호에 쓰이는 GCM 내부 해시 키를 복구할 수 있고, 그 키를 알면 이후 위조는 간단해진다(trivial).

두 번째가 GCM을 특히 위험하게 만든다. 기밀성 손실은 nonce가 겹친 두 메시지에 한정되지만, 인증 쪽은 RFC 표현대로 “그 키가 제공하는 모든 인증과 무결성 보호”가 훼손된다. 그 키로 만든 태그가 맞는다는 사실이 더는 메시지가 진짜라는 근거가 되지 못한다. 그래서 GCM에서 nonce 재사용을 발견했다면 해당 nonce를 피하는 것으로는 부족하고 키를 교체해야 한다. 이 결론은 §5.1.1의 서술에서 이끌어 낸 것이고, RFC가 키 교체 절차를 따로 규정하지는 않는다.

같은 절은 GCM을 nonce의 유일성을 확실히 보장할 수 있을 때만 쓰라고 하고, 지연 시간을 줄이려는 GCM의 설계가 이 취약성의 원인이라고 설명한다. 두 번의 암호화에서 평문과 연관 데이터가 모두 같았다면 위의 심각한 결과는 해당하지 않는다고도 적혀 있다. 두 호출의 입력이 완전히 같았던 경우에 한정된 예외이니, 재사용해도 된다는 뜻으로 읽으면 안 된다.

ChaCha20-Poly1305: 키스트림과 일회용 키가 같아진다

RFC 8439 §4는 이 문서를 구현할 때 가장 중요한 보안 고려 사항이 ChaCha20에 쓰는 nonce의 유일성이라고 말한다. nonce가 반복되면 두 메시지 사이에서 키스트림과 Poly1305 일회용 키가 똑같아진다. 키스트림이 같으니 두 암호문의 XOR이 곧 두 평문의 XOR이 되고, 평문 XOR이 드러난다.

AEAD 구성(§2.8)에서는 메시지마다 키와 nonce로 Poly1305 일회용 키를 새로 만든다(§2.6). Poly1305는 이름 그대로 일회용 인증자이고, §2.5는 그 키 쌍 (r, s)가 호출마다 고유해야 하며(should) 예측할 수 없어야 한다(MUST)고 요구한다. nonce를 재사용하면 이 키 쌍이 두 메시지에서 같아지므로 그 전제가 깨진다.

다만 RFC 8439는 GCM처럼 “장기적으로 쓰이는 인증 키를 복구할 수 있다”고는 쓰지 않는다. 이 경우에 위조가 어디까지 가능한지도 따로 서술하지 않는다. 그러니 “ChaCha20-Poly1305는 nonce를 재사용해도 인증은 안전하다”고 읽어서는 안 되고, 반대로 GCM의 키 복구 설명을 그대로 옮겨 붙여서도 안 된다. RFC가 명시한 결과는 키스트림과 일회용 키가 같아지고 평문 XOR이 드러난다는 것까지다.

참고로 RFC 5116 §5.3.1은 AES-CCM의 nonce 재사용 결과로 두 평문의 XOR이 드러나는 기밀성 손실을 든다.

알고리즘 근거 문서가 밝힌 nonce 재사용 결과
AES-GCM RFC 5116 §5.1.1 두 평문의 XOR 노출, 내부 해시 키 복구로 그 키의 인증 전체 훼손
ChaCha20-Poly1305 RFC 8439 §4 키스트림과 Poly1305 일회용 키가 같아짐, 두 평문의 XOR 노출
AES-CCM RFC 5116 §5.3.1 두 평문의 XOR 노출

겹치지 않는 nonce 만들기

RFC 5116 §3.2가 권장하는 형식은 앞쪽의 고정 필드(Fixed)와 뒤쪽의 카운터(Counter)를 이어 붙인 것이다. 구현은 12옥텟 nonce에 카운터 4옥텟인 형태를 지원해야 한다(SHOULD). RFC 8439 §2.3은 여러 송신자가 한 키를 공유할 때 앞 32비트를 송신자마다 다르게 두고 나머지 64비트를 카운터로 채우는 분할을 예로 든다. 두 필드의 길이 배분은 다르지만 모양은 같다.

RFC 5116 §3.2 (12-octet nonce, 4-octet counter)
+--------------------------------+----------------+
|        Fixed (8 octets)        |  Counter (4)   |
+--------------------------------+----------------+

RFC 8439 §2.3 (multiple senders sharing one key)
+----------------+--------------------------------+
|   Sender (4)   |      Counter (8 octets)        |
+----------------+--------------------------------+

RFC 5116은 이 형식을 이렇게 설명한다.

  • 권장 형식에서 연속한 nonce의 카운터 필드는 네트워크 바이트 순서의 부호 없는 정수로 볼 때 단조 증가하는 수열을 이룬다. 카운터는 0에서 시작해 1씩 늘리는 것을 권장하고(SHOULD), 편의상 어떤 값을 건너뛰는 것은 허용된다(MAY).
  • 카운터가 C옥텟이면 만들 수 있는 nonce는 최대 2^(8C)개다. 4옥텟이면 2^32개다. 카운터가 끝에 닿았을 때 0으로 되돌리면 이미 쓴 nonce가 다시 나오므로, 그 전에 키를 바꿔야 한다.
  • 고정 필드는 한 암호화 장치 안에서 바뀌지 않아야 하고, 한 키를 여러 장치가 쓴다면 장치마다 달라야 한다(MUST). 고정 필드가 F옥텟이면 한 키를 공유할 수 있는 장치는 최대 2^(8F)개다.

RFC 8439도 카운터 쪽을 권한다. §2.6은 Poly1305 키 생성에 쓰는 nonce가 같은 키에서 호출마다 고유해야 하므로 무작위로 생성해서는 안 된다(MUST NOT)고 적고, 카운터를 좋은 구현 방법으로, LFSR(선형 귀환 시프트 레지스터)도 허용되는 방법으로 든다. §4는 64비트 블록 암호로 카운터를 암호화하는 방식도 허용하지만, 128비트나 256비트 블록 암호로 암호화한 카운터를 잘라 쓰는 것은 짧은 시간 안에 값이 반복될 수 있어 허용되지 않는다고 덧붙인다.

카운터가 되감기는 경우

카운터 방식의 약점은 상태를 잃는 순간이다. RFC 5116은 이런 상황을 직접 짚는다.

  • 여러 장치나 인스턴스가 한 키를 쓸 때. nonce가 겹치지 않도록 서로 조율해야 한다(§3.1). 장치마다 다른 고정 필드를 두는 것이 간단한 방법이다.
  • 재부팅을 넘어 키를 오래 쓸 때. 메모리에만 있던 카운터는 재부팅 뒤 처음부터 다시 시작할 수 있다. §3.1은 nonce를 비휘발성 저장소에 체크포인트로 남기라고 하고, 다음 값이 저장됐다는 확인을 받은 뒤 암호화하면 재사용을 높은 확신으로 막을 수 있다고 설명한다. 매번 기다리면 지연이 커지므로 몇 단계 앞선 값을 저장하는 방법도 제시한다. 100을 저장해 두고 1부터 99까지 쓰고, 그동안 200을 저장하는 식이다.
  • 가상 머신을 이전 상태로 되돌릴 때. §8은 VM이 이전 상태로 롤백되면 소프트웨어 구현이 nonce를 의도치 않게 재사용할 수 있다고 경고한다.

nonce 조율이 어려운 상황이라면 키를 바꾸는 것으로 많은 재사용 문제를 피할 수 있다는 것도 §3.1의 조언이다.

복호화는 따로 봐야 한다. RFC 5116 §5.1.1은 GCM 복호화 연산에 같은 nonce 값을 여러 번 넣는 것은 괜찮다고 적는데, 이는 GCM 절의 설명이지 공통 인터페이스의 규칙으로 적힌 것은 아니다. 공통 인터페이스 쪽인 §2.1은 nonce를 잘못 재구성해 인증 복호화에 넣으면 복호화가 이를 잡아내므로 보안 실패로 이어지지 않는다고 설명한다. RFC 5116이 모든 AEAD에 요구하는 nonce 유일성은 어디까지나 암호화 호출에 대한 규칙이다. 다만 이 모두는 암호 연산 차원의 이야기다. §1은 AEAD 인터페이스가 재전송 방지(anti-replay) 같은 프로토콜 문제를 다루지 않는다고 밝힌다. 유효한 암호문을 공격자가 다시 보내는 것을 막는 일은 프로토콜의 몫이고, 복호화가 반복돼도 해가 없다는 뜻으로 읽으면 안 된다.

점검할 것

  • 키 하나에 nonce를 만드는 곳이 몇 군데인지 센다. 둘 이상이면 고정 필드나 송신자 필드로 공간을 나눴는지 확인한다.
  • 카운터가 어디에 저장되는지 본다. 메모리에만 있다면 재시작, 스냅샷 복원, VM 롤백 뒤에 다시 0부터 시작하지 않는지 확인한다.
  • 카운터 한도(4옥텟이면 2^32)에 닿기 전에 키를 바꾸는지 확인한다.
  • AES-GCM에서 재사용이 실제로 일어났다면 그 키는 인증 용도로 더 믿을 수 없다고 보고 교체한다.

참고 자료

모두 2026-10-12에 확인했다.

블로그로