aead — ChaCha20-Poly1305 봉인과 개봉
메시지를 감추고(chacha) 그 메시지와 부가 데이터를 한꺼번에 증언한다(poly). TLS 1.3 의 기본 스위트 (TLS_CHACHA20_POLY1305_SHA256)가 쓰는 바로 그것이다(RFC 8439 §2.8). 암호를 쓴다면 봉인은 여기서 시작한다.
이 구현이 약속하지 못하는 것
상수 시간이 아니다. 이 코드는 비밀에 따라 갈라지지 않지만, 이 언어에는 경계 검사와 멈춤이 있어 타이밍을 약속할 수 없다. 원격 공격자가 시간을 잴 수 있는 자리에 그대로 쓰지 않는다. 감사받지 않았다 — 확인은 RFC 8439 §2.8.2 벡터이고 “표준이 정한 값이 나온다” 까지가 보증이다. 논스를 재사용하면 끝난다 — 같은 키로 같은 논스를 두 번 쓰면 스트림이 겹쳐 평문이 드러나고 태그 위조가 가능해진다. 이 모듈은 그것을 막지 않는다 — 세는 일은 부르는 쪽의 몫이다.
권한을 받지 않는다 — 열쇠와 논스를 받아서 일한다. 봉인과 개봉은 스스로 할당하지 않으므로 인자가 많다. 결과 자리(ct·msg·tag)와 작업 공간(otk·st·work· ks·pst·pad)을 모두 넘긴다.
| op | 하는 일 | 답 |
|---|---|---|
seal | msg 를 ct 로 봉인하고 16 바이트 tag 를 낸다 | 태그 길이 16. 0 이면 실패 |
unseal | tag 를 먼저 확인하고 맞을 때만 msg 를 낸다 | 평문 길이. 0 이면 실패(또는 위조) |
key_gen | 논스마다 1 회용 Poly1305 키를 만든다 | 32 |
표 50.1 — aead 의 op
seal은 카운터 1 부터 흐름을 만든다 — 카운터 0 은 Poly1305 키로 이미 썼다. 그 한 칸이 겹치면 태그 키가 평문 스트림과 같아진다.- MAC 이 먹는 배열은
AAD ‖ pad16(AAD) ‖ CT ‖ pad16(CT) ‖ le64(|AAD|) ‖ le64(|CT|)다.pad16덕에 모든 블록이 꽉 찬 16 바이트이고, 내부 호출이 모두 같은 모양이다. - 실패는 0 이고, 그것이 위조와 같은 값이다.
unseal의 0 은 “태그가 맞지 않다” 이거나 “버퍼가 모자라다” 다. 둘을 가르지 않는 것은 부르는 쪽이 둘 다 똑같이 다뤄야 하기 때문이다 — 어느 쪽이든 평문을 쓰면 안 된다. 0 을 받고 계속 가는 코드는 이미 틀렸다. - 태그 비교는 XOR 누적이다 — 이른 반환이 없다. 이 언어에서 할 수 있는 최선이고, 위 경고가 말하는 한계이기도 하다.
확인하는 것 — RFC 8439 §2.8.2 벡터와 변조 거절(암호문이나 AAD 를 한 비트 바꾸면 unseal 이 0 을 답해야 한다), VM·네이티브 일치. 짓지 않은 것 — 상수 시간 보장, 이어 봉인하기, 논스 관리와 카운터 소진 감지, XChaCha20, 키 유도(hmac 의 HKDF). 함께 보기 — x25519 (키 합의).