| RFC 9846 | TLS | 2026년 7월 |
| Rescorla | 표준 트랙 | [페이지] |
이 문서는 전송 계층 보안 (TLS) 프로토콜 버전 1.3을 명세한다. TLS를 사용하면 클라이언트/서버 애플리케이션이 도청, 변조 및 메시지 위조를 방지하도록 설계된 방식으로 인터넷을 통해 통신할 수 있다.¶
이 문서는 TLS 1.3을 명세한 RFC 8446을 폐기한다. 이 문서는 TLS 1.2를 명세한 RFC 5246과 RFC 5077, 6961, 7627 및 8422를 폐기하며, 이들은 모두 TLS 1.2 이하와 관련되어 있고, RFC 5705와 6066을 갱신한다. 이 문서는 또한 TLS 1.2 구현에 대한 새로운 요구사항을 명세한다.¶
이 문서는 인터넷 표준 트랙 문서이다.¶
이 문서는 인터넷 엔지니어링 태스크 포스 (IETF)의 산출물이다. 이 문서는 IETF 커뮤니티의 합의를 나타낸다. 이 문서는 공개 검토를 거쳤으며 인터넷 엔지니어링 운영 그룹 (IESG)의 발행 승인을 받았다. 인터넷 표준에 대한 자세한 정보는 RFC 7841의 섹션 2에서 확인할 수 있다.¶
이 문서의 현재 상태, 정오표 및 피드백을 제공하는 방법에 대한 정보는 https://www.rfc-editor.org/info/rfc9846에서 확인할 수 있다.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.¶
TLS의 주요 목표는 통신하는 두 피어 사이에 보안 채널을 제공하는 것이다. 기반 전송에 요구되는 유일한 조건은 신뢰할 수 있고 순서가 보장되는 데이터 스트림이다. 구체적으로 보안 채널은 다음 속성을 제공해야 한다.¶
인증: 채널의 서버 측은 항상 인증되며, 클라이언트 측은 선택적으로 인증된다. 인증은 비대칭 암호화 (예: RSA [RSA], 타원 곡선 디지털 서명 알고리즘(ECDSA) [DSS] 또는 에드워즈 곡선 디지털 서명 알고리즘(EdDSA) [RFC8032])이나 대칭 사전 공유 키 (PSK)를 통해 수행할 수 있다.¶
기밀성: 채널이 설정된 후 이를 통해 전송되는 데이터는 엔드포인트에서만 볼 수 있다. TLS는 전송하는 데이터의 길이를 숨기지 않지만, 엔드포인트는 길이를 모호하게 하고 트래픽 분석 기법에 대한 보호를 강화하기 위해 TLS 레코드를 패딩할 수 있다.¶
무결성: 채널이 설정된 후 이를 통해 전송되는 데이터는 공격자가 탐지되지 않은 채 수정할 수 없다.¶
이러한 속성은 [RFC3552]에 설명된 것처럼 네트워크를 완전히 제어하는 공격자가 있는 경우에도 유지되어야 한다. 관련 보안 속성에 대한 더 완전한 설명은 부록 F를 참조한다.¶
TLS는 두 가지 주요 구성요소로 이루어진다.¶
통신 당사자를 인증하고 암호화 알고리즘과 매개변수를 협상하며 공유 키 자료를 설정하는 핸드셰이크 프로토콜 (섹션 4). 핸드셰이크 프로토콜은 변조에 저항하도록 설계되었다. 능동 공격자는 연결이 공격받지 않았을 때 피어가 협상했을 매개변수와 다른 매개변수를 협상하도록 강제할 수 없어야 한다.¶
핸드셰이크 프로토콜에서 설정된 매개변수를 사용해 통신하는 피어 간의 트래픽을 보호하는 레코드 프로토콜 (섹션 5). 레코드 프로토콜은 트래픽을 일련의 레코드로 나누며, 각 레코드는 트래픽 키를 사용해 독립적으로 보호된다.¶
TLS는 애플리케이션 프로토콜과 독립적이며, 상위 수준 프로토콜은 TLS 위에 투명하게 계층화할 수 있다. 그러나 TLS 표준은 프로토콜이 TLS를 사용해 보안을 추가하는 방법을 명세하지 않는다. TLS 핸드셰이크를 시작하는 방법과 교환된 인증 인증서를 해석하는 방법은 TLS 위에서 실행되는 프로토콜의 설계자와 구현자의 판단에 맡겨진다. TLS를 사용하는 애플리케이션 프로토콜은 핸드셰이크가 발생하는 방법과 시점, 신원 확인 방법을 포함하여 TLS가 해당 애플리케이션 프로토콜과 함께 작동하는 방식을 반드시 명세해야 한다. [RFC9525]는 TLS를 애플리케이션 프로토콜과 통합하는 데 유용한 지침을 제공한다.¶
이 문서는 TLS 버전 1.3을 정의한다. TLS 1.3은 이전 버전과 직접 호환되지 않지만, 모든 TLS 버전에는 두 피어가 모두 지원하는 경우 클라이언트와 서버가 상호 운용 가능한 공통 버전을 협상할 수 있게 하는 버전 관리 메커니즘이 포함되어 있다.¶
이 문서는 버전 1.2 [RFC5246], 확장된 주 비밀값 확장 [RFC7627] 및 TLS 1.2의 타원 곡선 정의 [RFC8422]를 포함한 이전 TLS 버전을 대체하고 폐기한다. 또한 [RFC5077]에 정의된 TLS 티켓 메커니즘을 폐기하고 이를 섹션 2.2에 정의된 메커니즘으로 대체한다. TLS 1.3은 키 유도 방식을 변경하므로 섹션 7.5에 설명된 대로 [RFC5705]를 갱신한다. 익스포터는 더 이상 콘텍스트가 없는 경우와 빈 콘텍스트가 있는 경우를 구분하지 않는다. 또한 온라인 인증서 상태 프로토콜(OCSP) 메시지가 전달되는 방식을 변경하므로 [RFC6066]을 갱신하고 섹션 4.5.1.1에 설명된 대로 [RFC6961]을 폐기한다.¶
이 문서의 핵심 단어 "반드시", "해서는 안 됨", "필수", "해야 함", "해서는 안 됨", "권고됨", "권고되지 않음", "권고", "권고하지 않음", "할 수 있음" 및 "선택 사항"은 여기에서처럼 모두 대문자로 표시된 경우에만 BCP 14 [RFC2119] [RFC8174]에 설명된 대로 해석해야 한다.¶
다음 용어를 사용한다.¶
TLS 1.3은 원래 [RFC8446]에 명세되었다. 이 문서는 동일한 버전 번호를 유지하고 하위 호환되는 TLS 1.3의 소규모 갱신이다. 일부 요구사항을 강화하고, 명확하지 않은 것으로 확인된 영역의 갱신된 텍스트와 기타 편집상 개선 사항을 포함한다. 또한 비밀값에 적용되던 "master"라는 용어를 "main"이나, 용어가 필요하지 않은 경우에는 더 짧은 이름으로 대체한다. 이 문서는 다음과 같은 구체적인 기술 변경을 수행한다.¶
연결 간 KeyShare 값의 재사용을 금지한다.¶
PreSharedKeys 및 HelloRetryRequest와 함께 사용되는 해시에 관한 모호성을 제거한다.¶
재개를 지원하지 않는 클라이언트가 NewSessionTicket을 무시하도록 요구한다.¶
키 사용 제한을 초과하기 전에 키 갱신을 시작해야 한다는 요구사항을 반드시로 강화하고 키 사용 제한의 범위를 명확히 한다.¶
허용되는 KeyUpdate 메시지 수를 제한한다.¶
"close_notify" 수준을 "warning"으로 정의하는 텍스트를 복원한다.¶
"user_canceled" 관련 동작을 명확히 하여, "close_notify"를 전송해야 하고 "user_canceled"를 무시하는 것이 좋다고 요구한다.¶
일반 "general_error" 경고를 추가한다.¶
CertificateRequest.extensions의 하한을 0바이트로 수정한다. 확장을 전송하지 않아 길이가 0이 될 수 있으므로 이는 구문상의 오류였다.¶
ClientHello.extensions의 하한을 수정한다. 이는 계산 오류였다.¶
NewSessionTicket.extensions의 상한을 수정한다. 이는 계산 오류였다.¶
이전에 (EC)DHE로만 한정되었던 비대칭 키 교환에 더 일반적인 표현을 사용하여 KEM 기반 키 교환의 사용을 반영한다.¶
트랜스크립트 해시와 향후 명세 간의 상호작용을 명확히 한다.¶
또한 특히 개인정보 보호와 관련된 보안 고려사항이 일부 개선되었다.¶
다음은 TLS 1.2와 TLS 1.3 간의 주요 기능적 차이점 목록이다. 모든 차이점을 열거하려는 것은 아니며, 그 밖에도 사소한 차이점이 많이 있다.¶
지원되는 대칭 암호화 알고리즘 목록에서 레거시로 간주되는 모든 알고리즘이 제거되었다. 남아 있는 알고리즘은 모두 연관 데이터가 있는 인증된 암호화(AEAD) 알고리즘이다. 암호 스위트 개념은 인증 및 키 교환 메커니즘을 레코드 보호 알고리즘 (비밀 키 길이 포함), 키 유도 함수와 핸드셰이크 메시지 인증 코드(MAC)에 모두 사용할 해시로부터 분리하도록 변경되었다.¶
제로 라운드 트립 시간(0-RTT) 모드가 추가되어, 특정 보안 속성을 희생하는 대신 일부 애플리케이션 데이터의 연결 설정 시 라운드 트립을 하나 줄인다.¶
정적 RSA 및 디피-헬먼 암호 스위트가 제거되었다. 이제 모든 공개 키 기반 키 교환 메커니즘은 순방향 비밀성을 제공한다.¶
이제 ServerHello 이후의 모든 핸드셰이크 메시지는 암호화된다. 새로 도입된 EncryptedExtensions 메시지를 통해 이전에 ServerHello에서 평문으로 전송되던 여러 확장도 기밀성 보호를 받을 수 있다.¶
키 유도 함수가 재설계되었다. 새로운 설계는 향상된 키 분리 속성으로 인해 암호학자가 더 쉽게 분석할 수 있게 한다. HMAC 기반 추출 및 확장 키 유도 함수(HKDF)가 기반 프리미티브로 사용된다.¶
핸드셰이크 상태 머신은 일관성을 높이고 ChangeCipherSpec과 같은 불필요한 메시지를 제거하도록 크게 재구성되었다(미들박스 호환성에 필요한 경우 제외).¶
이제 타원 곡선 알고리즘은 기본 명세에 포함되며, EdDSA와 같은 새로운 서명 알고리즘도 포함된다. TLS 1.3은 각 곡선에 단일 포인트 형식을 사용하는 대신 포인트 형식 협상을 제거했다.¶
RSA 패딩을 RSA 확률적 서명 방식(RSASSA-PSS)을 사용하도록 변경하고 압축, 디지털 서명 알고리즘(DSA) 및 사용자 정의 임시 디피-헬먼(DHE) 그룹을 제거하는 등의 암호화 개선이 이루어졌다.¶
TLS 1.2 버전 협상 메커니즘은 확장의 버전 목록을 사용하도록 대체되며 사용 중단되었다. 이는 버전 협상을 잘못 구현한 기존 서버와의 호환성을 높인다.¶
서버 측 상태를 사용하는 세션 재개와 사용하지 않는 세션 재개, 그리고 이전 TLS 버전의 PSK 기반 암호 스위트는 하나의 새로운 PSK 교환으로 대체되었다.¶
참조는 적절한 경우 갱신된 RFC 버전을 가리키도록 갱신되었다(예: RFC 3280 대신 RFC 5280).¶
이 문서는 TLS 1.3을 지원하지 않는 구현을 포함하여 TLS 1.2 구현에 선택적으로 영향을 미치는 몇 가지 변경 사항을 정의한다.¶
보안 채널에서 사용하는 암호화 매개변수는 TLS 핸드셰이크 프로토콜에서 생성된다. TLS의 이 하위 프로토콜은 클라이언트와 서버가 처음 서로 통신할 때 사용한다. 핸드셰이크 프로토콜을 통해 피어는 프로토콜 버전을 협상하고, 암호화 알고리즘을 선택하고, 서로를 인증하며 (클라이언트 인증은 선택 사항), 공유 비밀 키 자료를 설정할 수 있다. 핸드셰이크가 완료되면 피어는 설정된 키를 사용해 애플리케이션 계층 트래픽을 보호한다.¶
핸드셰이크 실패나 기타 프로토콜 오류가 발생하면 연결이 종료되며, 선택적으로 그 전에 경고 메시지가 전송될 수 있다 (섹션 6).¶
TLS는 세 가지 기본 키 교환 모드를 지원한다.¶
이 문서에서 비대칭 키 교환에는 유한체 또는 타원 곡선에 대한 디피-헬먼이 포함된다. 다른 확장은 키 캡슐화 메커니즘(KEM)의 사용을 정의한다.¶
아래 그림 1은 기본적인 전체 TLS 핸드셰이크를 보여준다.¶
클라이언트 서버
키 ^ ClientHello
교환 | + key_share*
| + signature_algorithms*
| + psk_key_exchange_modes*
v + pre_shared_key* -------->
ServerHello ^ 키
+ key_share* | 교환
+ pre_shared_key* v
{EncryptedExtensions} ^ 서버
{CertificateRequest*} v 매개변수
{Certificate*} ^
{CertificateVerify*} | 인증
{Finished} v
<-------- [애플리케이션 데이터*]
^ {Certificate*}
인증 | {CertificateVerify*}
v {Finished} -------->
[애플리케이션 데이터] <-------> [애플리케이션 데이터]
+ 앞서 표시된 메시지에서 전송되는 주목할 만한
확장을 나타낸다.
* 항상 전송되는 것은 아닌 선택적 또는 상황 의존적
메시지/확장을 나타낸다.
{} [sender]_handshake_traffic_secret에서 유도된
키를 사용해 보호되는 메시지를 나타낸다.
[] [sender]_application_traffic_secret_N에서 유도된
키를 사용해 보호되는 메시지를 나타낸다.
핸드셰이크는 위 다이어그램에 표시된 것처럼 세 단계로 구성된다고 볼 수 있다.¶
키 교환: 공유 키 자료를 설정하고 암호화 매개변수를 선택한다. 이 단계 이후의 모든 내용은 암호화된다.¶
서버 매개변수: 기타 핸드셰이크 매개변수 (클라이언트 인증 여부, 애플리케이션 계층 프로토콜 지원 등)를 설정한다.¶
인증: 서버와 선택적으로 클라이언트를 인증하고 키 확인 및 핸드셰이크 무결성을 제공한다.¶
키 교환 단계에서 클라이언트는 무작위 논스 (ClientHello.random), 제공하는 프로토콜 버전, 대칭 암호/해시 쌍의 목록, 비대칭 키 교환 공유값 목록 ("key_share"(섹션 4.3.8) 확장), 사전 공유 키 레이블 목록 ("pre_shared_key"(섹션 4.3.11) 확장) 또는 둘 다, 그리고 잠재적인 추가 확장을 포함하는 ClientHello (섹션 4.2.2) 메시지를 전송한다. 미들박스 호환성을 위해 추가 필드 및/또는 메시지가 포함될 수도 있다.¶
서버는 ClientHello를 처리하고 연결에 적합한 암호화 매개변수를 결정한다. 그런 다음 협상된 연결 매개변수를 나타내는 자체 ServerHello (섹션 4.2.3)로 응답한다. ClientHello와 ServerHello의 조합이 공유 키를 결정한다. 비대칭 키 설정을 사용하는 경우 ServerHello에는 서버의 임시 비대칭 키 교환 공유값을 포함하는 "key_share" 확장이 있으며, 서버의 공유값은 클라이언트 공유값 중 하나와 동일한 그룹에 반드시 속해야 한다. PSK 키 설정을 사용하는 경우 ServerHello에는 클라이언트가 제공한 PSK 중 선택된 것을 나타내는 "pre_shared_key" 확장이 포함된다. 구현은 비대칭 키 교환과 PSK를 함께 사용할 수 있으며, 이 경우 두 확장이 모두 제공된다.¶
그런 다음 서버는 서버 매개변수를 설정하기 위해 두 개의 메시지를 전송한다.¶
개별 인증서에만 해당하는 항목을 제외하고, 암호화 매개변수를 결정하는 데 필요하지 않은 ClientHello 확장에 대한 응답. [섹션 4.4.1]¶
인증서 기반 클라이언트 인증이 필요한 경우 해당 인증서에 요구되는 매개변수. 클라이언트 인증이 필요하지 않으면 이 메시지는 생략된다. [섹션 4.4.2]¶
마지막으로 클라이언트와 서버는 인증 메시지를 교환한다. TLS는 인증서 기반 인증이 필요할 때마다 동일한 메시지 집합을 사용한다. (PSK 기반 인증은 키 교환의 부수 효과로 수행된다.) 구체적으로 다음과 같다.¶
엔드포인트의 인증서와 인증서별 확장. 서버가 인증서를 사용해 인증하지 않는 경우에는 이 메시지를 생략하고, 서버가 CertificateRequest를 전송하지 않아 클라이언트가 인증서를 사용해 인증해서는 안 됨을 나타낸 경우 클라이언트도 이 메시지를 생략한다. 원시 공개 키 [RFC7250] 또는 캐시된 정보 확장 [RFC7924]을 사용하는 경우 이 메시지는 인증서가 아니라 서버의 장기 키에 해당하는 다른 값을 포함한다. [섹션 4.5.1]¶
Certificate 메시지의 공개 키에 대응하는 개인 키를 사용한 전체 핸드셰이크의 서명. 엔드포인트가 인증서를 통해 인증하지 않는 경우 이 메시지는 생략된다. [섹션 4.5.2]¶
전체 핸드셰이크에 대한 MAC(메시지 인증 코드). 이 메시지는 핸드셰이크에서 설정된 공유 비밀값에 대한 키 확인을 제공하고, 엔드포인트의 신원을 교환된 키에 결합하며, PSK 모드에서는 핸드셰이크도 인증한다. [섹션 4.5.3]¶
서버의 메시지를 수신하면 클라이언트는 요청된 경우 Certificate와 CertificateVerify, 그리고 Finished라는 자체 인증 메시지로 응답한다.¶
이 시점에서 핸드셰이크가 완료되고 클라이언트와 서버는 인증된 암호화로 보호되는 애플리케이션 계층 데이터를 교환하기 위해 레코드 계층에 필요한 키 자료를 유도한다. 섹션 2.3에 명세된 경우를 제외하고, Finished 메시지를 전송하기 전에 애플리케이션 데이터를 전송해서는 안 된다. 서버는 클라이언트의 인증 메시지를 수신하기 전에 애플리케이션 데이터를 전송할 수 있지만, 그 시점에 전송되는 모든 데이터는 당연히 인증되지 않은 피어에게 전송된다는 점에 유의한다.¶
TLS PSK는 외부에서 설정할 수 있지만, 이전 연결에서 설정한 후 새 연결을 설정하는 데 사용할 수도 있다(PSK를 통한 "세션 재개" 또는 "재개"). 핸드셰이크가 완료되면 서버는 초기 핸드셰이크에서 유도된 고유 키에 해당하는 PSK 식별자를 클라이언트에 전송할 수 있다(섹션 4.7.1 참조). 그러면 클라이언트는 향후 핸드셰이크에서 해당 PSK 식별자를 사용해 연관된 PSK의 사용을 협상할 수 있다. 서버가 PSK를 수락하면 새 연결의 보안 콘텍스트는 원래 연결에 암호학적으로 연결되며, 전체 핸드셰이크 대신 초기 핸드셰이크에서 유도된 키를 사용해 암호화 상태를 부트스트랩한다. TLS 1.2 이하에서는 이 기능이 "세션 ID"와 "세션 티켓" [RFC5077]으로 제공되었다. 두 메커니즘 모두 TLS 1.3에서 폐기되었다.¶
PSK는 비대칭 키 교환과 함께 사용하여 공유 키와 결합된 순방향 비밀성을 제공할 수 있으며, 단독으로도 사용할 수 있지만 이 경우 애플리케이션 데이터의 순방향 비밀성을 잃게 된다.¶
그림 3은 첫 번째 핸드셰이크에서 PSK를 설정하고 두 번째 핸드셰이크에서 이를 사용하는 핸드셰이크 쌍을 보여준다.¶
클라이언트 서버
초기 핸드셰이크:
ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [애플리케이션 데이터*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
<-------- [NewSessionTicket]
[애플리케이션 데이터] <-------> [애플리케이션 데이터]
후속 핸드셰이크:
ClientHello
+ key_share*
+ psk_key_exchange_modes
+ pre_shared_key -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
{Finished}
<-------- [애플리케이션 데이터*]
{Finished} -------->
[애플리케이션 데이터] <-------> [애플리케이션 데이터]
서버는 PSK를 통해 인증하므로 Certificate 또는 CertificateVerify 메시지를 전송하지 않는다. 클라이언트가 PSK를 통해 재개를 제안하는 경우 필요하다면 서버가 재개를 거부하고 전체 핸드셰이크로 대체할 수 있도록 "key_share" 확장도 서버에 제공하는 것이 권고된다. 서버는 PSK 키 설정의 사용을 협상하기 위해 "pre_shared_key" 확장으로 응답하며, 여기에서처럼 비대칭 키 설정을 수행하기 위해 "key_share" 확장으로 응답하여 순방향 비밀성을 제공할 수도 있다.¶
PSK를 외부에서 프로비저닝하는 경우 PSK 식별자와 PSK에 사용할 KDF 해시 알고리즘도 반드시 프로비저닝해야 한다.¶
참고: 외부에서 프로비저닝된 사전 공유 비밀값을 사용하는 경우, [RFC4086]에서 설명한 것처럼 키 생성 중 충분한 엔트로피를 사용하는 것이 매우 중요하다. 암호나 기타 낮은 엔트로피 소스에서 공유 비밀값을 유도하는 것은 안전하지 않다. 낮은 엔트로피의 비밀값 또는 암호는 PSK 바인더를 기반으로 한 사전 공격에 취약하다. 명세된 PSK 인증은 비대칭 키 설정과 함께 사용하더라도 강력한 암호 기반 인증 키 교환이 아니다. 특히 핸드셰이크를 관찰할 수 있는 공격자가 암호/사전 공유 키에 대해 무차별 대입 공격을 수행하는 것을 방지하지 못한다.¶
클라이언트와 서버가 외부에서 얻었거나 이전 핸드셰이크를 통해 얻은 PSK를 공유하는 경우, TLS 1.3을 사용하면 클라이언트가 첫 번째 플라이트에서 데이터("초기 데이터")를 전송할 수 있다. 클라이언트는 PSK를 사용해 서버를 인증하고 초기 데이터를 암호화한다.¶
그림 4와 같이 0-RTT 데이터는 첫 번째 플라이트의 1-RTT 핸드셰이크에 추가된다. 나머지 핸드셰이크는 PSK 재개를 사용하는 1-RTT 핸드셰이크와 동일한 메시지를 사용한다.¶
클라이언트 서버
ClientHello
+ early_data
+ key_share*
+ psk_key_exchange_modes
+ pre_shared_key
(애플리케이션 데이터*) -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
+ early_data*
{Finished}
<-------- [애플리케이션 데이터*]
(EndOfEarlyData*)
{Finished} -------->
[애플리케이션 데이터] <-------> [애플리케이션 데이터]
+ 앞서 표시된 메시지에서 전송되는 주목할 만한
확장을 나타낸다.
* 항상 전송되는 것은 아닌 선택적 또는 상황 의존적
메시지/확장을 나타낸다.
() client_early_traffic_secret에서 유도된
키를 사용해 보호되는 메시지를 나타낸다.
{} [sender]_handshake_traffic_secret에서 유도된
키를 사용해 보호되는 메시지를 나타낸다.
[] [sender]_application_traffic_secret_N에서 유도된
키를 사용해 보호되는 메시지를 나타낸다.
중요 참고: 0-RTT 데이터의 보안 속성은 다른 종류의 TLS 데이터보다 약하다. 구체적으로 다음과 같다.¶
프로토콜은 이 데이터에 대해 어떠한 순방향 비밀성도 보장하지 않는다. 적용되는 순방향 비밀성 보장이 있다면 서버의 동작에 따라 결정된다 (섹션 8.1 참조). 이 동작은 프로토콜의 일부로 클라이언트에 전달되지 않는다. 따라서 서버 동작에 대한 대역 외 정보가 없다면 클라이언트는 이 데이터에 순방향 비밀성이 없다고 간주해야 한다.¶
연결 간 재전송 불가 보장은 없다. 일반 TLS 1.3 1-RTT 데이터의 재전송 방지는 서버의 Random 값으로 제공되지만, 0-RTT 데이터는 ServerHello에 의존하지 않으므로 보장이 더 약하다. 이는 데이터가 TLS 클라이언트 인증이나 애플리케이션 프로토콜 내부에서 인증되는 경우 특히 중요하다. 동일한 경고가 early_exporter_secret의 모든 사용에도 적용된다.¶
0-RTT 데이터는 연결 내에서 복제될 수 없으며 (즉, 서버는 동일한 연결에서 같은 데이터를 두 번 처리하지 않음), 공격자는 0-RTT 데이터가 1-RTT 데이터인 것처럼 보이게 할 수 없다 (서로 다른 키로 보호되기 때문). 부록 F.5에는 잠재적인 공격에 대한 설명이 있으며, 섹션 8에서는 서버가 재전송의 영향을 제한하는 데 사용할 수 있는 메커니즘을 설명한다.¶
이 문서는 외부 표현에서 데이터의 형식을 다룬다. 다음의 매우 기본적이고 다소 비형식적으로 정의된 표현 구문을 사용한다.¶
아래 정의에서 이 구문의 선택적 구성요소는 "[[ ]]"(이중 대괄호)로 감싸 표시한다.¶
모든 데이터 항목의 표현은 명시적으로 지정된다. 기본 데이터 블록 크기는 1바이트(즉, 8비트)이다. 여러 바이트로 구성된 데이터 항목은 왼쪽에서 오른쪽으로, 위에서 아래로 바이트를 이어 붙인 것이다. 바이트 스트림에서 여러 바이트 항목(다음 예에서는 숫자)은 C 표기법을 사용해 다음과 같이 구성된다.¶
value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) |
... | byte[n-1];
¶
여러 바이트 값에 대한 이 바이트 순서는 일반적인 네트워크 바이트 순서, 즉 빅 엔디언 형식이다.¶
주석은 "/*"로 시작하고 "*/"로 끝난다.¶
해석되지 않은 데이터를 포함하는 단일 바이트 엔터티의 형식은 opaque이다.¶
기존 형식 T에 대한 형식 별칭 T'는 다음과 같이 정의한다.¶
T T';¶
기본 숫자 데이터 형식은 부호 없는 바이트(uint8)이다. 더 큰 모든 숫자 데이터 형식은 섹션 3.1에 설명된 대로 이어 붙인 고정 길이 바이트 시퀀스로 구성되며, 역시 부호가 없다. 다음 숫자 형식은 미리 정의되어 있다.¶
uint8 uint16[2]; uint8 uint24[3]; uint8 uint32[4]; uint8 uint64[8];¶
여기와 명세의 다른 모든 곳에서 모든 값은 네트워크 바이트 (빅 엔디언) 순서로 전송된다. 16진수 바이트 01 02 03 04로 표현된 uint32는 십진수 값 16909060과 같다.¶
벡터(1차원 배열)는 동종 데이터 요소의 스트림이다. 표현을 위해 이 명세에서는 벡터를 목록이라고 부른다. 벡터의 크기는 문서 작성 시 지정하거나 런타임까지 지정하지 않을 수 있다. 어느 경우든 길이는 벡터의 요소 수가 아니라 바이트 수를 선언한다. 형식 T의 고정 길이 벡터인 새 형식 T'를 지정하는 구문은 다음과 같다.¶
T T'[n];¶
여기에서 T'는 데이터 스트림에서 n바이트를 차지하며, n은 T 크기의 배수이다. 벡터 길이는 인코딩된 스트림에 포함되지 않는다.¶
다음 예에서 Datum은 프로토콜이 해석하지 않는 연속된 3바이트로 정의되며, Data는 연속된 세 개의 Datum으로서 총 9바이트를 사용한다.¶
opaque Datum[3]; /* 해석되지 않는 세 바이트 */ Datum Data[9]; /* 연속된 세 개의 3바이트 벡터 */¶
가변 길이 벡터는 <floor..ceiling> 표기법을 사용하여 허용되는 길이의 하위 범위를 양 끝값을 포함해 지정하는 방식으로 정의한다. 이를 인코딩할 때 실제 길이가 바이트 스트림에서 벡터 내용보다 앞에 온다. 길이는 벡터에 지정된 최대(ceiling) 길이를 담는 데 필요한 만큼의 바이트를 사용하는 숫자 형식이다. 실제 길이 필드가 0인 가변 길이 벡터를 빈 벡터라고 한다.¶
T T'<floor..ceiling>;¶
다음 예에서 "mandatory"는 opaque 형식의 데이터를 300바이트 이상 400바이트 이하 포함해야 하는 벡터이다. 빈 벡터일 수 없다. 실제 길이 필드는 값 400을 표현하기에 충분한 uint16인 2바이트를 사용한다(섹션 3.3 참조). 마찬가지로 "longer"는 최대 800바이트의 데이터, 즉 400개의 uint16 요소를 나타낼 수 있으며 비어 있을 수도 있다. 인코딩에는 벡터 앞에 추가되는 2바이트 실제 길이 필드가 포함된다. 인코딩된 벡터의 길이는 단일 요소 길이의 정확한 배수여야 한다 (예: 17바이트 길이의 uint16 벡터는 허용되지 않음).¶
opaque mandatory<300..400>;
/* 길이 필드는 2바이트이며 비어 있을 수 없음 */
uint16 longer<0..800>;
/* 0개에서 400개의 16비트 부호 없는 정수 */
¶
"enum" 또는 "enumerated"라고 하는 추가적인 희소 데이터 형식을 사용할 수 있다. 각 정의는 서로 다른 형식이다. 동일한 형식의 열거형만 할당하거나 비교할 수 있다. 다음 예에서와 같이 열거형의 모든 요소에는 값을 할당해야 한다. 열거형 요소에는 순서가 없으므로 어떤 순서로든 고유한 값을 할당할 수 있다.¶
enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;
¶
향후 프로토콜 확장 또는 추가 사항에서 새로운 값을 정의할 수 있다. 필드 정의에 달리 명시되지 않는 한 구현은 알 수 없는 값을 파싱하고 무시할 수 있어야 한다.¶
열거형은 정의된 최대 서수 값이 차지하는 만큼의 바이트 스트림 공간을 사용한다. 다음 정의에서는 Color 형식의 필드를 전달하는 데 1바이트가 사용된다.¶
enum { red(3), blue(5), white(7) } Color;
¶
불필요한 요소를 정의하지 않고 너비 정의를 강제하기 위해 연관된 태그 없이 값을 선택적으로 지정할 수 있다.¶
다음 예에서 Taste는 데이터 스트림에서 2바이트를 사용하지만 현재 프로토콜 버전에서는 값 1, 2 또는 4만 가질 수 있다.¶
enum { sweet(1), sour(2), bitter(4), (32000) } Taste;
¶
열거형 요소의 이름은 정의된 형식 내에서 범위가 지정된다. 첫 번째 예에서 열거형의 두 번째 요소를 완전히 한정하여 참조하면 Color.blue가 된다. 할당 대상이 잘 지정된 경우에는 이러한 한정이 필요하지 않다.¶
Color color = Color.blue; /* 과도하게 지정되었지만 적법함 */ Color color = blue; /* 올바름, 형식이 암시됨 */¶
열거형에 할당된 이름은 고유할 필요가 없다. 숫자 값은 동일한 이름이 적용되는 범위를 나타낼 수 있다. 값에는 해당 범위의 최솟값과 최댓값이 포함되며, 두 개의 마침표 문자로 구분한다. 이는 주로 공간의 영역을 예약하는 데 유용하다.¶
enum { sad(0), meh(1..254), happy(255) } Mood;
¶
편의를 위해 기본형으로부터 구조체 형식을 구성할 수 있다. 각 명세는 새롭고 고유한 형식을 선언한다. 정의에 사용되는 구문은 C와 매우 유사하다.¶
struct {
T1 f1;
T2 f2;
...
Tn fn;
} T;
¶
표준 목록 구문을 사용하여 고정 길이 및 가변 길이 목록(벡터) 필드를 사용할 수 있다. 변형 예(섹션 3.8)의 구조체 V1과 V2가 이를 보여준다.¶
열거형에 사용할 수 있는 것과 매우 유사한 구문으로, 형식 이름을 사용하여 구조체 내의 필드를 한정할 수 있다. 예를 들어 T.f2는 앞선 선언의 두 번째 필드를 가리킨다.¶
다음과 같이 "="를 사용하여 필드와 변수에 고정 값을 할당할 수 있다.¶
struct {
T1 f1 = 8; /* T.f1은 항상 8이어야 함 */
T2 f2;
} T;
¶
정의된 구조체는 환경 내에서 사용할 수 있는 일부 정보를 기반으로 변형을 가질 수 있다. 선택자는 구조체가 정의하는 가능한 변형을 정의하는 열거형이어야 한다. 아래 select의 각 분기는 해당 변형 필드의 형식과 선택적 필드 레이블을 지정한다. 런타임에 변형을 선택하는 메커니즘은 표현 언어에서 규정하지 않는다.¶
struct {
T1 f1;
T2 f2;
....
Tn fn;
select (E) {
case e1: Te1 [[fe1]];
case e2: Te2 [[fe2]];
....
case en: Ten [[fen]];
};
} Tv;
¶
예를 들면 다음과 같다.¶
enum { apple(0), orange(1) } VariantTag;
struct {
uint16 number;
opaque string<0..10>; /* 가변 길이 */
} V1;
struct {
uint32 number;
opaque string[10]; /* 고정 길이 */
} V2;
struct {
VariantTag type;
select (VariantRecord.type) {
case apple: V1;
case orange: V2;
};
} VariantRecord;
¶
핸드셰이크 프로토콜은 연결의 보안 매개변수를 협상하는 데 사용된다. 핸드셰이크 메시지는 TLS 레코드 계층에 제공되며, 여기에서 하나 이상의 TLSPlaintext 또는 TLSCiphertext 구조체 안에 캡슐화되고 현재 활성 연결 상태에 명세된 대로 처리 및 전송된다.¶
enum {
client_hello(1),
server_hello(2),
new_session_ticket(4),
end_of_early_data(5),
encrypted_extensions(8),
certificate(11),
certificate_request(13),
certificate_verify(15),
finished(20),
key_update(24),
message_hash(254),
(255)
} HandshakeType;
struct {
HandshakeType msg_type; /* 핸드셰이크 유형 */
uint24 length; /* 메시지에 남은 바이트 */
select (Handshake.msg_type) {
case client_hello: ClientHello;
case server_hello: ServerHello;
case end_of_early_data: EndOfEarlyData;
case encrypted_extensions: EncryptedExtensions;
case certificate_request: CertificateRequest;
case certificate: Certificate;
case certificate_verify: CertificateVerify;
case finished: Finished;
case new_session_ticket: NewSessionTicket;
case key_update: KeyUpdate;
};
} Handshake;
¶
프로토콜 메시지는 TLS 확장에 의해 수정되지 않는 한, 섹션 4.1에 정의되고 섹션 2의 다이어그램에 표시된 순서로 반드시 전송해야 한다. 예상하지 못한 순서로 핸드셰이크 메시지를 수신한 피어는 "unexpected_message" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
새로운 핸드셰이크 메시지 유형은 섹션 11에 설명된 대로 IANA에서 할당한다.¶
TLS의 많은 암호화 계산에서는 트랜스크립트 해시를 사용한다. 이 값은 핸드셰이크 메시지 유형과 길이 필드를 전달하는 핸드셰이크 메시지 헤더를 포함하되 레코드 계층 헤더는 포함하지 않고, 포함되는 각 핸드셰이크 메시지를 이어 붙인 값을 해시하여 계산한다. 즉,¶
Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Mn)¶
이 일반 규칙의 예외로, 서버가 ClientHello에 HelloRetryRequest로 응답하는 경우 ClientHello1의 값은 Hash(ClientHello1)을 포함하고 핸드셰이크 유형이 "message_hash"(254)인 특수 합성 핸드셰이크 메시지로 대체된다. 즉,¶
Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
Hash(message_hash || /* 핸드셰이크 유형 */
00 00 Hash.length || /* 핸드셰이크 메시지 길이(바이트) */
Hash(ClientHello1) || /* ClientHello1의 해시 */
HelloRetryRequest || ... || Mn)
¶
이 구성을 사용하는 이유는 서버가 전체 중간 해시 상태를 내보내지 않고 ClientHello1의 해시만 쿠키에 저장하여 상태 비저장 HelloRetryRequest를 수행할 수 있게 하기 위한 것이다 (섹션 4.3.2 참조).¶
메시지 범위를 "..."로 지정한 경우 트랜스크립트 해시는 기본 핸드셰이크 중 전송되거나 수신된 핸드셰이크 메시지의 시퀀스에서, 지정된 메시지로 시작하고 끝나는 범위를 대상으로 한다. 이 문서에서는 다음 핸드셰이크 메시지 시퀀스를 대상으로 한다 (일부는 생략될 수 있음): ClientHello, HelloRetryRequest, ClientHello, ServerHello, EncryptedExtensions, 서버 CertificateRequest, 서버 Certificate, 서버 CertificateVerify, 서버 Finished, EndOfEarlyData, 클라이언트 Certificate, 클라이언트 CertificateVerify 및 클라이언트 Finished. TLS 확장은 메시지를 추가하거나 제거할 수 있으며, 이 경우 트랜스크립트 해시는 수정된 시퀀스를 반영한다.¶
일반적으로 구현은 ServerHello에서 해시가 선택된 후, 협상된 해시를 기반으로 실행 중인 트랜스크립트 해시 값을 유지하여 트랜스크립트를 구현할 수 있다.¶
그러나 이후의 핸드셰이크 후 인증에는 서로가 포함되지 않으며, 기본 핸드셰이크가 끝날 때까지의 메시지만 포함된다는 점에 유의한다.¶
키 교환 메시지는 클라이언트와 서버의 보안 기능을 결정하고, 나머지 핸드셰이크와 데이터를 보호하는 데 사용되는 트래픽 키를 포함한 공유 비밀값을 설정하는 데 사용된다.¶
TLS에서 암호화 협상은 클라이언트가 ClientHello에 다음 네 가지 옵션 집합을 제공하는 방식으로 진행된다.¶
클라이언트가 지원하는 AEAD 알고리즘/HKDF 해시 쌍을 나타내는 암호 스위트 목록.¶
클라이언트가 지원하는 키 교환 그룹을 나타내는 "supported_groups"(섹션 4.3.7) 확장과, 이러한 그룹 중 일부 또는 전부에 대한 키 교환 공유값을 포함하는 "key_share"(섹션 4.3.8) 확장. "groups"라는 용어는 비DHE 키 설정 알고리즘을 지원하지 않았던 TLS 1.3의 원래 버전에서 유래한 역사적 용어이다).¶
클라이언트가 수락할 수 있는 서명 알고리즘을 나타내는 "signature_algorithms"(섹션 4.3.3) 확장. 인증서별 서명 알고리즘을 나타내기 위해 "signature_algorithms_cert" 확장 (섹션 4.3.3)도 추가할 수 있다.¶
클라이언트가 알고 있는 대칭 키 식별자 목록을 포함하는 "pre_shared_key"(섹션 4.3.11) 확장과, PSK와 함께 사용할 수 있는 키 교환 모드를 나타내는 "psk_key_exchange_modes"(섹션 4.3.9) 확장.¶
서버가 PSK를 선택하지 않는 경우 이 옵션 중 처음 세 가지는 서로 완전히 독립적이다. 서버는 암호 스위트, 키 설정을 위한 비대칭 키 교환 그룹과 키 공유값, 그리고 클라이언트에 자신을 인증하기 위한 서명 알고리즘/인증서 쌍을 각각 독립적으로 선택한다. 수신된 "supported_groups"와 서버가 지원하는 그룹 사이에 공통 항목이 없으면 서버는 "handshake_failure" 또는 "insufficient_security" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
서버가 PSK를 선택하면 클라이언트의 "psk_key_exchange_modes" 확장이 나타내는 목록에서 키 설정 모드 (현재는 PSK 단독 또는 비대칭 키 교환과 함께 사용)를 반드시 선택해야 한다. PSK를 비대칭 키 교환 없이 사용할 수 있다면, 이전 단락에서 설명한 비PSK의 경우와 달리 "supported_groups" 매개변수의 불일치가 치명적일 필요는 없다.¶
서버가 비대칭 키 교환 그룹을 선택했으나 클라이언트가 초기 ClientHello에 호환되는 "key_share" 확장을 제공하지 않은 경우, 서버는 HelloRetryRequest(섹션 4.2.4) 메시지로 반드시 응답해야 한다.¶
서버가 매개변수를 성공적으로 선택하고 HelloRetryRequest가 필요하지 않은 경우 다음과 같이 선택한 매개변수를 ServerHello에 나타낸다.¶
PSK를 사용하는 경우 서버는 선택한 키를 나타내는 "pre_shared_key" 확장을 전송한다.¶
비대칭 키 교환을 사용하는 경우 서버는 "key_share" 확장도 제공한다. PSK를 사용하지 않는 경우에는 비대칭 키 교환과 인증서 기반 인증을 항상 사용한다.¶
인증서를 통해 인증하는 경우 서버는 Certificate(섹션 4.5.1)와 CertificateVerify (섹션 4.5.2) 메시지를 전송한다. 이 문서에서 정의하는 TLS 1.3에서는 PSK 또는 인증서 중 하나를 항상 사용하지만 둘을 함께 사용하지는 않는다. 향후 문서에서 둘을 함께 사용하는 방법을 정의할 수 있다.¶
서버가 지원되는 매개변수 집합을 협상할 수 없는 경우 (즉, 클라이언트와 서버의 매개변수 사이에 공통 항목이 없는 경우), "handshake_failure" 또는 "insufficient_security" 치명적 경고와 함께 핸드셰이크를 반드시 중단해야 한다 (섹션 6 참조).¶
클라이언트가 서버에 처음 연결할 때 첫 번째 TLS 메시지로 ClientHello를 전송하는 것이 필수이다. 서버가 ClientHello에 HelloRetryRequest로 응답한 경우에도 클라이언트는 ClientHello를 전송한다. 이 경우 클라이언트는 다음 예외를 제외하고 동일한 ClientHello를 수정 없이 반드시 전송해야 한다.¶
HelloRetryRequest에 "key_share" 확장이 제공된 경우, 공유값 목록을 지정된 그룹의 단일 KeyShareEntry를 포함하는 목록으로 대체한다.¶
"early_data" 확장(섹션 4.3.10)이 있었다면 제거한다. HelloRetryRequest 이후에는 초기 데이터가 허용되지 않는다.¶
HelloRetryRequest에 "cookie" 확장이 제공된 경우 이를 포함한다.¶
존재하는 경우 "obfuscated_ticket_age"와 바인더 값을 다시 계산하고, 서버가 나타낸 암호 스위트와 호환되지 않는 PSK를 선택적으로 제거하여 "pre_shared_key" 확장을 갱신한다.¶
향후 정의되고 HelloRetryRequest에 존재하는 확장에서 허용할 수 있는 기타 수정 사항.¶
TLS 1.3에서는 재협상을 금지하므로 서버가 TLS 1.3을 협상한 후 다른 시점에 ClientHello를 수신하면, "unexpected_message" 경고와 함께 연결을 반드시 종료해야 한다.¶
서버가 이전 TLS 버전으로 TLS 연결을 설정하고 재협상 중 TLS 1.3 ClientHello를 수신한 경우, 이전 프로토콜 버전을 반드시 유지해야 한다. 특히 TLS 1.3을 협상해서는 안 된다.¶
이 메시지의 구조:¶
uint16 ProtocolVersion;
opaque Random[32];
uint8 CipherSuite[2]; /* 암호 스위트 선택자 */
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id<0..32>;
CipherSuite cipher_suites<2..2^16-2>;
opaque legacy_compression_methods<1..2^8-1>;
Extension extensions<7..2^16-1>;
} ClientHello;
¶
이전 TLS 버전에서는 이 필드를 버전 협상에 사용했으며 클라이언트가 지원하는 가장 높은 버전 번호를 나타냈다. 많은 서버가 버전 협상을 올바르게 구현하지 않아, 서버가 지원하는 버전보다 높은 번호를 가진 정상적인 ClientHello를 거부하는 "버전 비호환성"이 발생한다는 사실이 경험을 통해 확인되었다. TLS 1.3에서 클라이언트는 "supported_versions" 확장 (섹션 4.3.1)에 버전 선호도를 나타내며, legacy_version 필드는 TLS 1.2의 버전 번호인 0x0303으로 반드시 설정해야 한다. TLS 1.3 ClientHello는 legacy_version이 0x0303이고 supported_versions 확장이 존재하며 그 안에 표시된 가장 높은 버전이 0x0304인 것으로 식별한다. (하위 호환성에 관한 자세한 내용은 부록 E를 참조한다.) 0x0303과 같지 않은 legacy_version 값을 수신한 서버는 "protocol_version" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
TLS 1.3 이전 버전은 이 버전에서 사전 공유 키와 통합된 "세션 재개" 기능을 지원했다 (섹션 2.2 참조). TLS 1.3 이전 서버가 설정한 캐시된 세션 ID를 가진 클라이언트는 이 필드를 해당 값으로 설정하는 것이 권고된다. 호환 모드 (부록 E.4 참조)에서는 이 필드가 비어 있지 않아야 하므로, TLS 1.3 이전 세션을 제공하지 않는 클라이언트는 새로운 32바이트 값을 반드시 생성해야 한다. 이 값은 무작위일 필요는 없지만, 구현이 특정 값에 고정되는 현상(고착화라고도 함)을 피하기 위해 예측 불가능한 것이 권고된다. 그렇지 않은 경우에는 길이가 0인 목록 (즉, 값이 0인 단일 바이트 길이 필드)으로 반드시 설정해야 한다.¶
클라이언트가 지원하는 대칭 암호 옵션의 목록이며, 구체적으로는 레코드 보호 알고리즘(비밀 키 길이 포함)과 HKDF에 사용할 해시를 클라이언트 선호도가 높은 순서로 나열한다. 값은 부록 B.4에 정의되어 있다. 목록에 서버가 인식하지 못하거나 지원하지 않거나 사용하지 않으려는 암호 스위트가 포함되어 있으면, 서버는 해당 암호 스위트를 반드시 무시하고 나머지를 평소와 같이 처리해야 한다. 클라이언트가 PSK 키 설정을 시도하는 경우 PSK와 연관된 해시를 나타내는 암호 스위트를 하나 이상 알리는 것이 권고된다.¶
TLS 1.3 이전 버전은 압축을 지원했으며, 지원되는 압축 방식 목록이 이 필드에 전송되었다. 모든 TLS 1.3 ClientHello에서 이 목록은 이전 TLS 버전의 "null" 압축 방식에 해당하는 값 0으로 설정된 정확히 1바이트를 반드시 포함해야 한다. 이 필드에 다른 값을 가진 TLS 1.3 ClientHello를 수신하면 서버는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다. TLS 1.3 서버는 다른 압축 방식을 포함하는 TLS 1.2 이하의 ClientHello를 수신할 수 있으며, 그러한 이전 버전을 협상하는 경우 해당 이전 TLS 버전의 절차를 반드시 따라야 한다.¶
클라이언트는 extensions 필드에 데이터를 전송하여 서버에 확장 기능을 요청한다. 실제 "Extension" 형식은 섹션 4.3에 정의되어 있다. TLS 1.3에서는 이전 TLS 버전과의 ClientHello 호환성을 유지하기 위해 기능이 확장으로 이동했으므로 특정 확장의 사용이 필수이다. 서버는 인식하지 못하는 확장을 반드시 무시해야 한다.¶
모든 TLS 버전에서는 extensions 필드가 선택적으로 compression_methods 필드 뒤에 올 수 있다. TLS 1.3 ClientHello 메시지에는 항상 확장이 포함된다(최소한 "supported_versions"가 포함되며, 그렇지 않으면 TLS 1.2 ClientHello 메시지로 해석됨). 그러나 TLS 1.3 서버는 이전 TLS 버전에서 extensions 필드가 없는 ClientHello 메시지를 수신할 수 있다. 확장의 존재 여부는 ClientHello 끝의 compression_methods 필드 뒤에 바이트가 있는지 확인하여 탐지할 수 있다. 이 선택적 데이터 탐지 방법은 가변 길이 필드를 사용하는 일반적인 TLS 방식과 다르지만, 확장이 정의되기 전 TLS와의 호환성을 위해 사용된다. TLS 1.3 서버는 먼저 이 검사를 수행하고 "supported_versions" 확장이 존재하는 경우에만 TLS 1.3 협상을 시도해야 한다. TLS 1.3 이전 버전을 협상하는 경우 서버는 legacy_compression_methods 뒤에 데이터가 없거나, 뒤따르는 데이터 없이 유효한 확장 블록이 포함되어 있는지 반드시 확인해야 한다. 그렇지 않으면 "decode_error" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
클라이언트가 확장을 사용해 추가 기능을 요청했으나 서버가 해당 기능을 제공하지 않는 경우 클라이언트는 핸드셰이크를 중단할 수 있다.¶
ClientHello 메시지를 전송한 후 클라이언트는 ServerHello 또는 HelloRetryRequest 메시지를 기다린다. 초기 데이터를 사용하는 경우 다음 핸드셰이크 메시지를 기다리는 동안 클라이언트는 초기 애플리케이션 데이터 (섹션 2.3)를 전송할 수 있다.¶
서버는 ClientHello를 기반으로 허용 가능한 핸드셰이크 매개변수 집합을 협상할 수 있는 경우 핸드셰이크를 진행하기 위해 ClientHello 메시지에 대한 응답으로 이 메시지를 전송한다.¶
이 메시지의 구조:¶
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id_echo<0..32>;
CipherSuite cipher_suite;
uint8 legacy_compression_method = 0;
Extension extensions<6..2^16-1>;
} ServerHello;
¶
이전 TLS 버전에서는 이 필드를 버전 협상에 사용했으며 연결에 선택된 버전 번호를 나타냈다. 안타깝게도 일부 미들박스는 새로운 값이 제시되면 실패한다. TLS 1.3에서 TLS 서버는 "supported_versions" 확장 (섹션 4.3.1)을 사용해 버전을 나타내며, legacy_version 필드는 TLS 1.2의 버전 번호인 0x0303으로 반드시 설정해야 한다. (하위 호환성에 관한 자세한 내용은 부록 E를 참조한다.) legacy_version 값이 0x0303과 같지 않은 TLS 1.3 Server Hello를 수신한 클라이언트는 "protocol_version" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
안전한 난수 생성기에서 생성한 32바이트. 자세한 내용은 부록 C를 참조한다. TLS 1.2 또는 TLS 1.1을 협상하는 경우 마지막 8바이트는 아래에 설명된 대로 반드시 덮어써야 하지만, 나머지 바이트는 무작위여야 한다. 이 구조체는 서버에서 생성하며 ClientHello.random과 독립적으로 반드시 생성해야 한다.¶
클라이언트의 legacy_session_id 필드 내용. 클라이언트 값이 서버가 재개하지 않기로 선택한 캐시된 TLS 1.3 이전 세션에 해당하더라도 이 필드는 그대로 반환된다. ClientHello에서 전송한 값과 일치하지 않는 legacy_session_id_echo 필드를 수신한 클라이언트는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
서버가 ClientHello.cipher_suites 목록에서 선택한 단일 암호 스위트. 제공하지 않은 암호 스위트를 수신한 클라이언트는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
값이 0이어야 하는 단일 바이트. 이 필드에 다른 값을 가진 TLS 1.3 ServerHello를 수신하면 클라이언트는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
확장 목록. ServerHello에는 암호화 콘텍스트를 설정하고 프로토콜 버전을 협상하는 데 필요한 확장만 반드시 포함해야 한다. 모든 TLS 1.3 ServerHello 메시지는 "supported_versions" 확장을 반드시 포함해야 한다. 현재 ServerHello 메시지에는 "pre_shared_key" 확장 또는 "key_share" 확장, 또는 둘 다 (비대칭 키 설정과 함께 PSK를 사용하는 경우)가 추가로 포함된다. 기타 확장(섹션 4.3 참조)은 EncryptedExtensions 메시지에서 별도로 전송된다.¶
미들박스와의 하위 호환성을 위해 (부록 E.4 참조), HelloRetryRequest 메시지는 ServerHello와 동일한 구조를 사용하지만 Random을 "HelloRetryRequest"의 SHA-256인 특수 값으로 설정한다.¶
CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C¶
server_hello 유형의 메시지를 수신하면 구현은 먼저 Random 값을 반드시 검사하고, 이 값과 일치하면 섹션 4.2.4에 설명된 대로 처리해야 한다.¶
TLS 1.3에는 서버의 random 값에 내장된 다운그레이드 보호 메커니즘이 있다. ClientHello에 대한 응답으로 TLS 1.2 이하를 협상하는 TLS 1.3 서버는 ServerHello의 Random 값 마지막 8바이트를 특별히 반드시 설정해야 한다.¶
TLS 1.2를 협상하는 경우 TLS 1.3 서버는 Random 값의 마지막 8바이트를 다음 바이트로 반드시 설정해야 한다.¶
44 4F 57 4E 47 52 44 01¶
[RFC8996]과 부록 E.5는 TLS 1.2 미만 버전의 협상을 금지한다. 그러나 해당 지침을 따르지 않는 서버 구현은 ServerHello.random 값의 마지막 8바이트를 다음 바이트로 반드시 설정해야 한다.¶
44 4F 57 4E 47 52 44 00¶
TLS 1.2 이하를 나타내는 ServerHello를 수신한 TLS 1.3 클라이언트는 마지막 8바이트가 이 두 값 중 어느 것과도 같지 않은지 반드시 확인해야 한다. TLS 1.2 클라이언트도 ServerHello가 TLS 1.1 이하를 나타내는 경우 마지막 8바이트가 두 번째 값과 같지 않은지 확인하는 것이 권고된다. 일치하는 경우 클라이언트는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다. 이 메커니즘은 Finished 교환에서 제공하는 것 이상의 제한된 다운그레이드 공격 방지 기능을 제공한다. TLS 1.2 이하에 존재하는 ServerKeyExchange 메시지는 두 random 값에 대한 서명을 포함하므로, 임시 암호를 사용하는 한 능동 공격자는 탐지되지 않고 random 값을 수정할 수 없다. 정적 RSA를 사용하는 경우에는 다운그레이드 보호를 제공하지 않는다.¶
참고: 이는 [RFC5246]에서 변경된 사항이므로 실제로 많은 TLS 1.2 클라이언트와 서버는 위에 명세된 대로 동작하지 않을 수 있다.¶
TLS 1.2 이하로 재협상을 수행하는 레거시 TLS 클라이언트가 재협상 중 TLS 1.3 ServerHello를 수신하면 "protocol_version" 경고와 함께 핸드셰이크를 반드시 중단해야 한다. TLS 1.3을 협상한 경우에는 재협상이 불가능하다는 점에 유의한다.¶
서버는 허용 가능한 매개변수 집합을 찾을 수 있지만 ClientHello에 핸드셰이크를 진행하기 위한 충분한 정보가 없는 경우, ClientHello 메시지에 대한 응답으로 이 메시지를 전송한다. 섹션 4.2.3에서 설명한 것처럼 HelloRetryRequest는 ServerHello 메시지와 동일한 형식이며, legacy_version, legacy_session_id_echo, cipher_suite 및 legacy_compression_method 필드는 동일한 의미를 가진다. 그러나 편의를 위해 이 문서에서는 "HelloRetryRequest"를 별개의 메시지인 것처럼 설명한다.¶
서버의 확장에는 "supported_versions"가 반드시 포함되어야 한다. 또한 클라이언트가 올바른 ClientHello 쌍을 생성하는 데 필요한 최소 확장 집합을 포함하는 것이 권고된다. HelloRetryRequest에는 선택적 "cookie" 확장 (섹션 4.3.2 참조)을 제외하고, 클라이언트가 ClientHello에서 먼저 제공하지 않은 확장을 포함해서는 안 된다.¶
HelloRetryRequest를 수신하면 클라이언트는 섹션 4.2.3에 명세된 대로 legacy_version, legacy_session_id_echo, cipher_suite, legacy_compression_method를 반드시 확인한 다음, "supported_versions"를 사용해 버전을 결정하는 것부터 확장을 처리해야 한다. HelloRetryRequest가 ClientHello에 아무런 변경도 발생시키지 않는 경우 클라이언트는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다. 클라이언트가 동일한 연결에서 두 번째 HelloRetryRequest를 수신하는 경우 (즉, ClientHello 자체가 HelloRetryRequest에 대한 응답인 경우), "unexpected_message" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
그렇지 않으면 클라이언트는 HelloRetryRequest의 모든 확장을 반드시 처리하고 갱신된 두 번째 ClientHello를 전송해야 한다. 이 명세에 정의된 HelloRetryRequest 확장은 다음과 같다.¶
제공하지 않은 암호 스위트를 수신한 클라이언트는 핸드셰이크를 반드시 중단해야 한다. 서버는 적합하게 갱신된 ClientHello를 수신할 때 동일한 암호 스위트를 협상하도록 반드시 보장해야 한다 (서버가 협상의 첫 단계에서 암호 스위트를 선택하면 자동으로 충족됨). ServerHello를 수신하면 클라이언트는 ServerHello에 제공된 암호 스위트가 HelloRetryRequest의 암호 스위트와 동일한지 반드시 확인하고, 그렇지 않으면 "illegal_parameter" 경고와 함께 핸드셰이크를 중단해야 한다.¶
또한 갱신된 ClientHello에서 클라이언트는 선택된 암호 스위트의 해시가 아닌 다른 해시와 연관된 사전 공유 키를 제공하지 않는 것이 권고된다. 이를 통해 클라이언트는 두 번째 ClientHello에서 여러 해시에 대한 부분 해시 트랜스크립트를 계산하지 않아도 된다.¶
HelloRetryRequest의 "supported_versions" 확장에 있는 selected_version 값은 ServerHello에서 반드시 유지해야 하며, 값이 변경되면 클라이언트는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
여러 TLS 메시지에는 태그-길이-값으로 인코딩된 확장 구조체가 포함된다.¶
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
server_name(0), /* RFC 6066, 9261 */
status_request(5), /* RFC 6066, 9846 */
supported_groups(10), /* RFC 7919, 9846 */
signature_algorithms(13), /* RFC 9846 */
use_srtp(14), /* RFC 5764 */
heartbeat(15), /* RFC 6520 */
application_layer_protocol_negotiation(16), /* RFC 7301 */
client_certificate_type(19), /* RFC 7250 */
server_certificate_type(20), /* RFC 7250 */
padding(21), /* RFC 7685 */
compress_certificate(27), /* RFC 8879 */
record_size_limit(28), /* RFC 8449 */
delegated_credential(34), /* RFC 9345 */
supported_ekt_ciphers(39), /* RFC 8870 */
pre_shared_key(41), /* RFC 9846 */
early_data(42), /* RFC 9846 */
supported_versions(43), /* RFC 9846 */
cookie(44), /* RFC 9846 */
psk_key_exchange_modes(45), /* RFC 9846 */
certificate_authorities(47), /* RFC 9846 */
oid_filters(48), /* RFC 9846 */
post_handshake_auth(49), /* RFC 9846 */
signature_algorithms_cert(50), /* RFC 9846 */
key_share(51), /* RFC 9846 */
transparency_info(52), /* RFC 9162 */
external_id_hash(55), /* RFC 8844 */
external_session_id(56), /* RFC 8844 */
quic_transport_parameters(57), /* RFC 9001 */
ticket_request(58), /* RFC 9149 */
ech_outer_extensions(64768), /* RFC 9849 */
encrypted_client_hello(65037), /* RFC 9849 */
(65535)
} ExtensionType;
¶
참고: 이 목록에는 이 문서 작성 시점에 "권고"로 표시되고 TLS 1.3에서 허용된 확장만 포함된다.¶
여기에서:¶
"extension_data" 필드의 내용은 일반적으로 TLS 표현 언어로 정의된 확장별 구조체에서 정의한다. 달리 명세하지 않는 한 후행 데이터는 금지된다. 즉, 송신자는 "extension_data" 필드의 구조체 뒤에 데이터를 포함해서는 안 된다. 확장을 처리할 때 구조체를 파싱한 후 데이터가 남아 있으면 수신자는 "decode_error" 경고와 함께 핸드셰이크를 반드시 중단해야 한다. 수신자가 해당 확장을 구현하지 않았거나 무시하도록 구성된 경우에는 적용되지 않는다.¶
확장 유형 목록은 섹션 11에 설명된 대로 IANA에서 관리한다.¶
확장은 일반적으로 요청/응답 방식으로 구성되지만, 일부 확장은 대응하는 응답이 없는 요청(즉, 표시)일 뿐이다. 클라이언트는 ClientHello 메시지에서 확장 요청을 전송하고, 서버는 ServerHello, EncryptedExtensions, HelloRetryRequest 및 Certificate 메시지에서 확장 응답을 전송한다. 서버는 CertificateRequest 메시지에서 확장 요청을 전송하며, 클라이언트는 Certificate 메시지로 응답할 수 있다. 서버는 NewSessionTicket에서 요청하지 않은 확장을 전송할 수도 있지만, 클라이언트는 이에 직접 응답하지 않는다.¶
원격 엔드포인트가 대응하는 확장 요청을 전송하지 않은 경우, 구현은 HelloRetryRequest의 "cookie" 확장을 제외하고 확장 응답(ServerHello, EncryptedExtensions, HelloRetryRequest 및 Certificate 메시지 안의 응답)을 전송해서는 안 된다. 이러한 확장을 수신하면 엔드포인트는 "unsupported_extension" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
아래 표는 각 확장이 나타날 수 있는 메시지를 다음 표기법으로 나타낸다: CH(ClientHello), SH(ServerHello), EE(EncryptedExtensions), CT(Certificate), CR(CertificateRequest), NST(NewSessionTicket), HRR(HelloRetryRequest). 구현이 인식하는 확장이 해당 확장에 대해 명세되지 않은 메시지에 나타난 경우 구현은 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
| 확장 | TLS 1.3 |
|---|---|
| server_name [RFC6066] [RFC9261] | CH, EE, CR |
| status_request [RFC6066] [RFC9846] | CH, CR, CT |
| supported_groups [RFC7919] [RFC9846] | CH, EE |
| signature_algorithms [RFC9846] | CH, CR |
| use_srtp [RFC5764] | CH, EE |
| heartbeat [RFC6520] | CH, EE |
| application_layer_protocol_negotiation [RFC7301] | CH, EE |
| client_certificate_type [RFC7250] | CH, EE |
| server_certificate_type [RFC7250] | CH, EE |
| padding [RFC7685] | CH |
| compress_certificate [RFC8879] | CH, CR |
| record_size_limit [RFC8449] | CH, EE |
| delegated_credential [RFC9345] | CH, CR, CT |
| supported_ekt_ciphers [RFC8870] | CH, EE |
| pre_shared_key [RFC9846] | CH, SH |
| early_data [RFC9846] | CH, EE, NST |
| supported_versions [RFC9846] | CH, SH, HRR |
| cookie [RFC9846] | CH, HRR |
| psk_key_exchange_modes [RFC9846] | CH |
| certificate_authorities [RFC9846] | CH, CR |
| oid_filters [RFC9846] | CR |
| post_handshake_auth [RFC9846] | CH |
| signature_algorithms_cert [RFC9846] | CH, CR |
| key_share [RFC9846] | CH, SH, HRR |
| transparency_info [RFC9162] | CH, CR, CT |
| external_id_hash [RFC8844] | CH, EE |
| external_session_id [RFC8844] | CH, EE |
| quic_transport_parameters [RFC9001] | CH, EE |
| ticket_request [RFC9149] | CH, EE |
| ech_outer_extensions [RFC9849] | CH |
| encrypted_client_hello [RFC9849] | CH, HRR, EE |
참고: 이 표에는 이 문서 작성 시점에 "권고"로 표시되고 TLS 1.3에서 허용된 확장만 포함된다.¶
서로 다른 유형의 확장이 여러 개 존재하는 경우, ClientHello의 마지막 확장이어야 하는 "pre_shared_key"(섹션 4.3.11)를 제외하고 확장은 어떤 순서로든 나타날 수 있다 (ServerHello 확장 블록에서는 어디에나 나타날 수 있음). 특정 확장 블록에는 동일한 유형의 확장이 두 개 이상 있어서는 안 된다.¶
TLS 1.3에서는 TLS 1.2와 달리 재개-PSK 모드에서도 각 핸드셰이크마다 확장을 협상한다. 그러나 0-RTT 매개변수는 이전 핸드셰이크에서 협상된 것이며, 불일치하는 경우 0-RTT를 거부해야 할 수 있다(섹션 4.3.10 참조).¶
이 프로토콜에서는 새로운 기능과 기존 기능 사이에 미묘한 (때로는 명백한) 상호작용이 발생하여 전체 보안이 크게 저하될 수 있다. 새로운 확장을 설계할 때 다음 고려사항을 반영해야 한다.¶
서버가 확장에 동의하지 않는 일부 경우는 오류 조건 (예: 핸드셰이크를 계속할 수 없음)이고, 일부는 특정 기능 지원을 단순히 거부하는 경우이다. 일반적으로 전자에는 오류 경고를, 후자에는 서버 확장 응답의 필드를 사용해야 한다.¶
확장은 가능한 한 핸드셰이크 메시지를 조작하여 특정 기능을 사용하게 하거나 사용하지 못하게 하는 모든 공격을 방지하도록 설계해야 한다. 해당 기능이 보안 문제를 일으킨다고 여겨지는지와 관계없이 이 원칙을 따라야 한다. 확장 필드가 Finished 메시지 해시의 입력에 포함된다는 사실만으로 충분한 경우가 많지만, 확장이 핸드셰이크 단계에서 전송되는 메시지의 의미를 변경하는 경우에는 극도의 주의가 필요하다. 설계자와 구현자는 핸드셰이크가 인증되기 전까지 능동 공격자가 메시지를 수정하고 확장을 삽입, 제거 또는 교체할 수 있다는 사실을 인지해야 한다.¶
struct {
select (Handshake.msg_type) {
case client_hello:
ProtocolVersion versions<2..254>;
case server_hello: /* 및 HelloRetryRequest */
ProtocolVersion selected_version;
};
} SupportedVersions;
¶
"supported_versions" 확장은 클라이언트가 지원하는 TLS 버전을 나타내고 서버가 사용 중인 버전을 나타내는 데 사용된다. 이 확장에는 지원되는 버전이 선호도 순으로 나열되며, 가장 선호하는 버전이 먼저 온다. 이 명세의 구현은 협상할 준비가 된 모든 TLS 버전을 포함하는 이 확장을 ClientHello에 반드시 전송해야 한다 (이 명세에서는 최소한 0x0304를 의미하지만, 이전 TLS 버전의 협상이 허용되는 경우 해당 버전도 반드시 포함해야 함).¶
이 확장이 존재하지 않으면 이 명세를 준수하면서 TLS 1.2도 지원하는 서버는 ClientHello.legacy_version이 0x0304 이상이더라도 [RFC5246]에 명세된 대로 TLS 1.2 이하를 반드시 협상해야 한다. 서버는 legacy_version이 0x0304 이상인 ClientHello를 수신하면 핸드셰이크를 중단할 수 있다.¶
이 확장이 ClientHello에 존재하는 경우 서버는 버전 협상에 ClientHello.legacy_version 값을 사용해서는 안 되며, 클라이언트 선호도를 결정하는 데 "supported_versions" 확장만 반드시 사용해야 한다. 서버는 해당 확장에 존재하는 TLS 버전만 반드시 선택해야 하며, 그 안의 알 수 없는 버전은 반드시 무시해야 한다. 이 메커니즘을 사용하면 한쪽이 희소한 버전 범위를 지원하는 경우 TLS 1.2 이전 버전도 협상할 수 있다. 이전 TLS 버전을 지원하도록 선택하는 TLS 1.3 구현은 TLS 1.2를 지원하는 것이 권고된다. 서버는 이 확장을 포함하지만 버전 목록에 0x0304를 포함하지 않는 ClientHello를 수신할 준비가 반드시 되어 있어야 한다.¶
TLS 1.3 이전 버전을 협상하는 서버는 ServerHello.version을 반드시 설정하고 "supported_versions" 확장을 전송해서는 안 된다. TLS 1.3을 협상하는 서버는 선택한 버전 값(0x0304)을 포함하는 "supported_versions" 확장을 전송하여 반드시 응답해야 한다. ServerHello.legacy_version 필드는 0x0303(TLS 1.2)으로 반드시 설정해야 한다.¶
서버 핸드셰이크 메시지가 ServerHello인지 HelloRetryRequest인지 확인하기 위해 ServerHello.random을 검사한 후, 클라이언트는 ServerHello의 나머지 부분을 처리하기 전에 이 확장을 반드시 확인해야 한다. 이를 위해 클라이언트는 확장을 읽을 수 있도록 ServerHello를 파싱해야 한다. 이 확장이 존재하면 클라이언트는 ServerHello.legacy_version 값을 반드시 무시하고 선택된 버전을 결정하는 데 "supported_versions" 확장만 반드시 사용해야 한다. ServerHello의 "supported_versions" 확장에 클라이언트가 제공하지 않은 버전이나 TLS 1.3 이전 버전이 포함되어 있으면 클라이언트는 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
TLS 1.3은 디지털 서명에 사용할 수 있는 서명 알고리즘을 나타내는 두 가지 확장을 제공한다. "signature_algorithms_cert" 확장은 인증서의 서명에 적용되고, 원래 TLS 1.2에 등장한 "signature_algorithms" 확장은 CertificateVerify 메시지의 서명에 적용된다. 인증서에서 찾은 키도 함께 사용하는 서명 알고리즘에 적합한 유형이어야 한다. 아래에 설명된 것처럼 이는 특히 RSA 키와 PSS 서명에서 문제가 된다. "signature_algorithms_cert" 확장이 존재하지 않으면 "signature_algorithms" 확장이 인증서에 나타나는 서명에도 적용된다. 서버가 인증서를 통해 자신을 인증하기를 원하는 클라이언트는 "signature_algorithms" 확장을 반드시 전송해야 한다. 서버가 인증서를 통해 인증하는데 클라이언트가 "signature_algorithms" 확장을 전송하지 않은 경우 서버는 "missing_extension" 경고와 함께 핸드셰이크를 반드시 중단해야 한다 (섹션 9.2 참조).¶
"signature_algorithms_cert" 확장은 인증서와 TLS 자체에 대해 서로 다른 알고리즘 집합을 지원하는 구현이 그 기능을 명확히 나타낼 수 있게 하기 위해 추가되었다. TLS 1.2 구현도 이 확장을 처리하는 것이 권고된다. 두 경우에 동일한 정책을 사용하는 구현은 "signature_algorithms_cert" 확장을 생략할 수 있다.¶
이 확장의 "extension_data" 필드에는 SignatureSchemeList 값이 포함된다.¶
enum {
/* RSASSA-PKCS1-v1_5 알고리즘 */
rsa_pkcs1_sha256(0x0401),
rsa_pkcs1_sha384(0x0501),
rsa_pkcs1_sha512(0x0601),
/* ECDSA 알고리즘 */
ecdsa_secp256r1_sha256(0x0403),
ecdsa_secp384r1_sha384(0x0503),
ecdsa_secp521r1_sha512(0x0603),
/* 공개 키 OID가 rsaEncryption인 RSASSA-PSS 알고리즘 */
rsa_pss_rsae_sha256(0x0804),
rsa_pss_rsae_sha384(0x0805),
rsa_pss_rsae_sha512(0x0806),
/* EdDSA 알고리즘 */
ed25519(0x0807),
ed448(0x0808),
/* 공개 키 OID가 RSASSA-PSS인 RSASSA-PSS 알고리즘 */
rsa_pss_pss_sha256(0x0809),
rsa_pss_pss_sha384(0x080a),
rsa_pss_pss_sha512(0x080b),
/* 레거시 알고리즘 */
rsa_pkcs1_sha1(0x0201),
ecdsa_sha1(0x0203),
/* 예약된 코드 포인트 */
private_use(0xFE00..0xFFFF),
(0xFFFF)
} SignatureScheme;
struct {
SignatureScheme supported_signature_algorithms<2..2^16-2>;
} SignatureSchemeList;
¶
참고: TLS 1.2에 이미 "SignatureAlgorithm" 형식이 있고 이 형식이 이를 대체하므로 이 enum의 이름은 "SignatureScheme"이다. 본문 전체에서는 "서명 알고리즘"이라는 용어를 사용한다.¶
각 SignatureScheme 값은 클라이언트가 검증할 의향이 있는 단일 서명 알고리즘을 나열한다. 값은 선호도가 높은 순서로 표시한다. 서명 알고리즘은 다이제스트가 아니라 임의 길이 메시지를 입력으로 사용한다는 점에 유의한다. 전통적으로 다이제스트에 작용하는 알고리즘은 TLS에서 먼저 지정된 해시 알고리즘으로 입력을 해시한 후 평소와 같이 진행하도록 정의해야 한다. 위에 나열된 코드 포인트 그룹은 다음 의미를 가진다.¶
[RFC8017]에 정의된 RSASSA-PKCS1-v1_5와 [SHS]에 정의된 대응하는 해시 알고리즘을 사용하는 서명 알고리즘을 나타낸다. 이 값은 인증서에 나타나는 서명에만 해당하며(섹션 4.5.1.2 참조), 서명된 TLS 핸드셰이크 메시지에는 사용할 수 있도록 정의되지 않았다. 다만 TLS 1.2와의 하위 호환성을 위해 "signature_algorithms"와 "signature_algorithms_cert"에 나타날 수 있다.¶
ECDSA [DSS], NIST SP 800-186 [ECDP]에 정의된 대응하는 곡선, [SHS]에 정의된 대응하는 해시 알고리즘을 사용하는 서명 알고리즘을 나타낸다. 서명은 [RFC4492]에 정의된 DER 인코딩 [X690] ECDSA-Sig-Value 구조체로 표현된다.¶
[RFC8017]에 정의된 대로 MGF1 마스크 생성 함수를 사용하는 RSASSA-PSS 서명 알고리즘을 나타낸다. MGF1에서 사용하는 다이제스트와 서명되는 다이제스트는 모두 [SHS]에 정의된 대응하는 해시 알고리즘이다. Salt 길이는 다이제스트 알고리즘 출력 길이와 반드시 같아야 한다. 공개 키가 X.509 인증서에 포함된 경우 rsaEncryption OID [RFC3279]를 반드시 사용해야 한다.¶
[RFC8032] 또는 그 후속 문서에 정의된 EdDSA를 사용하는 서명 알고리즘을 나타낸다. 이는 "prehash" 변형이 아니라 "PureEdDSA" 알고리즘에 해당한다는 점에 유의한다.¶
[RFC8017]에 정의된 대로 MGF1 마스크 생성 함수를 사용하는 RSASSA-PSS 서명 알고리즘을 나타낸다. MGF1에서 사용하는 다이제스트와 서명되는 다이제스트는 모두 [SHS]에 정의된 대응하는 해시 알고리즘이다. Salt 길이는 다이제스트 알고리즘 길이와 반드시 같아야 한다. 공개 키가 X.509 인증서에 포함된 경우 RSASSA-PSS OID [RFC5756]를 반드시 사용해야 한다. 인증서 서명에 사용할 때 알고리즘 매개변수는 DER로 반드시 인코딩해야 한다. 대응하는 공개 키의 매개변수가 존재하면 서명의 매개변수는 공개 키의 매개변수와 반드시 동일해야 한다.¶
알려진 약점이 있는 알고리즘, 구체적으로 이 콘텍스트에서 (1) RSASSA-PKCS1-v1_5를 사용하는 RSA 또는 (2) ECDSA와 함께 사용되는 SHA-1을 사용하므로 사용 중단되는 알고리즘을 나타낸다. 이 값은 인증서에 나타나는 서명에만 해당하며 (섹션 4.5.1.2 참조), 서명된 TLS 핸드셰이크 메시지에는 사용할 수 있도록 정의되지 않았다. 다만 TLS 1.2와의 하위 호환성을 위해 "signature_algorithms"와 "signature_algorithms_cert"에 나타날 수 있다. 엔드포인트는 이러한 알고리즘을 협상하지 않는 것이 권고되지만, 하위 호환성만을 위해 협상할 수 있다. 이러한 값을 제공하는 클라이언트는 SignatureSchemeList에서 가장 낮은 우선순위로(다른 모든 알고리즘 뒤에) 반드시 나열해야 한다. TLS 1.3 서버는 SHA-1 없이 유효한 인증서 체인을 생성할 수 없는 경우를 제외하고 SHA-1로 서명된 인증서를 제공해서는 안 된다 (섹션 4.5.1.2 참조).¶
자체 서명 인증서 또는 신뢰 앵커인 인증서의 서명은 인증 경로를 시작하므로 검증하지 않는다 ([RFC5280], 섹션 3.2 참조). 인증 경로를 시작하는 인증서는 "signature_algorithms"와 "signature_algorithms_cert" 확장에서 지원된다고 알리지 않은 서명 알고리즘을 사용할 수 있다.¶
TLS 1.2는 이 확장을 다르게 정의한다는 점에 유의한다. TLS 1.2를 협상할 의향이 있는 TLS 1.3 구현은 해당 버전을 협상할 때 [RFC5246]의 요구사항에 따라 반드시 동작해야 한다. 특히:¶
TLS 1.2 ClientHello는 이 확장을 생략할 수 있다.¶
TLS 1.2에서 이 확장은 해시/서명 쌍을 포함했다. 이 쌍은 2옥텟으로 인코딩되므로 SignatureScheme 값은 TLS 1.2의 인코딩에 맞춰 할당되었다. 일부 레거시 쌍은 할당되지 않은 채 남아 있다. 이러한 알고리즘은 TLS 1.3부터 사용 중단되었다. 어떤 구현도 이를 제공하거나 협상해서는 안 된다. 특히 MD5 [SLOTH], SHA-224 및 DSA를 사용해서는 안 된다.¶
ECDSA 서명 방식은 TLS 1.2의 ECDSA 해시/서명 쌍과 일치한다. 그러나 이전 의미론에서는 서명 곡선을 제한하지 않았다. TLS 1.2를 협상하는 경우 구현은 "supported_groups" 확장에서 알린 모든 곡선을 사용하는 서명을 수락할 준비가 반드시 되어 있어야 한다.¶
TLS 1.3에서 필수인 RSASSA-PSS 지원을 알리는 구현은 TLS 1.2를 협상하는 경우에도 해당 방식을 사용하는 서명을 수락할 준비가 반드시 되어 있어야 한다. TLS 1.2에서 RSASSA-PSS는 RSA 암호 스위트와 함께 사용된다.¶
"oid_filters" 확장을 사용하면 서버가 클라이언트 인증서와 일치하기를 원하는 OID/값 쌍 목록을 제공할 수 있다. 서버가 이 확장을 제공하는 경우 CertificateRequest 메시지에서만 반드시 전송해야 한다.¶
struct {
opaque certificate_extension_oid<1..2^8-1>;
opaque certificate_extension_values<0..2^16-1>;
} OIDFilter;
struct {
OIDFilter filters<0..2^16-1>;
} OIDFilterExtension;
¶
허용된 값과 함께 DER 인코딩 [X690] 형식으로 표현된 인증서 확장 OID [RFC5280] 목록. 일부 인증서 확장 OID는 여러 값(예: 확장 키 용도)을 허용한다. 서버가 비어 있지 않은 filters 목록을 포함한 경우 응답에 포함된 클라이언트 인증서는 클라이언트가 인식하는 지정된 확장 OID를 모두 반드시 포함해야 한다. 클라이언트가 인식하는 각 확장 OID에 대해 지정된 모든 값이 클라이언트 인증서에 반드시 존재해야 한다 (단, 인증서에 다른 값도 있을 수 있음). 그러나 클라이언트는 인식하지 못하는 인증서 확장 OID를 반드시 무시하고 건너뛰어야 한다. 클라이언트가 필수 인증서 확장 OID 일부를 무시하고 요청을 충족하지 않는 인증서를 제공한 경우, 서버는 재량에 따라 클라이언트 인증 없이 연결을 계속하거나 "unsupported_certificate" 경고와 함께 핸드셰이크를 중단할 수 있다. 특정 OID는 filters 목록에 두 번 이상 나타나서는 안 된다.¶
PKIX RFC는 다양한 인증서 확장 OID와 대응하는 값 형식을 정의한다. 형식에 따라 일치하는 인증서 확장 값이 반드시 비트 단위로 같지는 않다. TLS 구현은 인증서 확장 OID를 사용한 인증서 선택을 수행하기 위해 PKI 라이브러리에 의존할 것으로 예상된다.¶
이 문서는 [RFC5280]에 정의된 두 가지 표준 인증서 확장의 일치 규칙을 정의한다.¶
요청에 설정된 모든 키 사용 비트가 인증서의 키 사용 확장에도 설정되어 있으면 인증서의 키 사용 확장은 요청과 일치한다.¶
요청에 존재하는 모든 키 목적 OID가 인증서의 확장 키 사용 확장에도 존재하면 인증서의 확장 키 사용 확장은 요청과 일치한다. 특수 anyExtendedKeyUsage OID는 요청에 사용해서는 안 된다.¶
별도의 명세에서 다른 인증서 확장의 일치 규칙을 정의할 수 있다.¶
"post_handshake_auth" 확장은 클라이언트가 핸드셰이크 후 인증(섹션 4.7.2)을 수행할 의향이 있음을 나타내는 데 사용된다. 서버는 이 확장을 제공하지 않은 클라이언트에 핸드셰이크 후 CertificateRequest를 전송해서는 안 된다. 서버는 이 확장을 전송해서는 안 된다.¶
struct {} PostHandshakeAuth;
¶
"post_handshake_auth" 확장의 "extension_data" 필드는 길이가 0이다.¶
클라이언트가 전송하는 "supported_groups" 확장은 클라이언트가 키 교환에 지원하는 명명된 그룹을 가장 선호하는 것부터 가장 덜 선호하는 순서로 나타낸다.¶
참고: TLS 1.3 이전 TLS 버전에서는 이 확장의 이름이 "elliptic_curves"였으며 타원 곡선 그룹만 포함했다. [RFC8422]와 [RFC7919]를 참조한다. 이 확장은 ECDSA 곡선을 협상하는 데에도 사용되었다. 이제 서명 알고리즘은 독립적으로 협상된다 (섹션 4.3.3 참조).¶
이 확장의 "extension_data" 필드에는 "NamedGroupList" 값이 포함된다.¶
enum {
/* 타원 곡선 그룹(ECDHE) */
secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019),
x25519(0x001D), x448(0x001E),
/* 유한체 그룹(DHE) */
ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
ffdhe6144(0x0103), ffdhe8192(0x0104),
/* 예약된 코드 포인트 */
ffdhe_private_use(0x01FC..0x01FF),
ecdhe_private_use(0xFE00..0xFEFF),
(0xFFFF)
} NamedGroup;
struct {
NamedGroup named_group_list<2..2^16-1>;
} NamedGroupList;
¶
NIST SP 800-186 [ECDP] 또는 [RFC7748]에 정의된 대응하는 명명된 곡선의 지원을 나타낸다. 값 0xFE00부터 0xFEFF까지는 사적 사용 [RFC8126]을 위해 예약되어 있다.¶
[RFC7919]에 정의된 대응하는 유한체 그룹의 지원을 나타낸다. 값 0x01FC부터 0x01FF까지는 사적 사용을 위해 예약되어 있다.¶
"named_group_list"의 항목은 송신자의 선호도 (가장 선호하는 선택이 먼저)에 따라 정렬된다. "named_group_list"에는 중복 항목이 있어서는 안 된다. 수신자는 중복 항목을 탐지하면 치명적 "illegal_parameter" 경고와 함께 연결을 중단할 수 있다.¶
TLS 1.3부터 서버는 클라이언트에 "supported_groups" 확장을 전송할 수 있다. 클라이언트는 핸드셰이크가 성공적으로 완료되기 전에 "supported_groups"에서 찾은 정보를 기반으로 동작해서는 안 되지만, 성공적으로 완료된 핸드셰이크에서 얻은 정보를 사용해 이후 연결의 "key_share" 확장에서 사용하는 그룹을 변경할 수 있다. 서버가 "key_share" 확장에 있는 그룹보다 더 선호하는 그룹을 가지고 있지만 여전히 ClientHello를 수락할 의향이 있다면, 클라이언트의 선호도 인식을 갱신하기 위해 "supported_groups"를 전송하는 것이 권고된다. 이 확장에는 클라이언트가 현재 지원하는지와 관계없이 서버가 지원하는 모든 그룹을 포함하는 것이 권고된다.¶
PSK를 사용하고 해당 PSK에 초기 데이터가 허용된 경우 (예: 부록 B.3.4 참조), 클라이언트는 첫 번째 메시지 플라이트에서 애플리케이션 데이터를 전송할 수 있다. 클라이언트가 그렇게 하기로 선택하면 "pre_shared_key"와 "early_data" 확장을 모두 반드시 제공해야 한다.¶
이 확장의 "extension_data" 필드에는 "EarlyDataIndication" 값이 포함된다.¶
struct {} Empty;
struct {
select (Handshake.msg_type) {
case new_session_ticket: uint32 max_early_data_size;
case client_hello: Empty;
case encrypted_extensions: Empty;
};
} EarlyDataIndication;
¶
max_early_data_size 필드 사용에 관한 자세한 내용은 섹션 4.7.1을 참조한다.¶
0-RTT 데이터의 매개변수 (버전, 대칭 암호 스위트, 애플리케이션 계층 프로토콜 협상(ALPN) [RFC7301] 프로토콜 등)는 사용 중인 PSK와 연관된 값이다. 외부에서 프로비저닝된 PSK의 경우 연관된 값은 키와 함께 프로비저닝된 값이다. NewSessionTicket 메시지를 통해 설정된 PSK의 경우 연관된 값은 PSK를 설정한 연결에서 협상된 값이다. 초기 데이터를 암호화하는 데 사용하는 PSK는 클라이언트의 "pre_shared_key" 확장에 나열된 첫 번째 PSK여야 한다.¶
NewSessionTicket을 통해 프로비저닝된 PSK의 경우 서버는 선택한 PSK 식별자의 티켓 수명 (PskIdentity.obfuscated_ticket_age에서 ticket_age_add를 빼고 232으로 모듈로 연산한 값)이 티켓 발행 후 경과 시간과 작은 허용 범위 내에서 일치하는지 반드시 검증해야 한다 (섹션 8 참조). 일치하지 않으면 서버는 핸드셰이크를 계속하되 0-RTT를 거부하는 것이 권고되며, 이 ClientHello가 최신이라고 가정하는 다른 동작을 수행하지 않는 것이 권고된다.¶
첫 번째 플라이트에서 전송되는 0-RTT 메시지는 다른 플라이트에서 전송되는 동일한 유형의 메시지와 동일한 (암호화된) 콘텐츠 유형(handshake 및 application_data)을 가지지만 서로 다른 키로 보호된다. 서버의 Finished 메시지를 수신한 후 서버가 초기 데이터를 수락했다면 키 변경을 나타내기 위해 EndOfEarlyData 메시지를 전송한다. 이 메시지는 0-RTT 트래픽 키로 암호화된다.¶
"early_data" 확장을 수신한 서버는 다음 세 가지 방식 중 하나로 반드시 동작해야 한다.¶
확장을 무시하고 일반적인 1-RTT 응답을 반환한다. 그런 다음 서버는 핸드셰이크 트래픽 키를 사용해 수신 레코드의 보호를 해제하려고 시도하고, 보호 해제에 실패한 레코드를 구성된 max_early_data_size까지 폐기하여 초기 데이터를 건너뛴다. 레코드의 보호를 성공적으로 해제하면 이를 클라이언트의 두 번째 플라이트 시작으로 처리하고 일반적인 1-RTT 핸드셰이크처럼 진행한다.¶
HelloRetryRequest로 응답하여 클라이언트가 다른 ClientHello를 전송하도록 요청한다. 클라이언트는 후속 ClientHello에 "early_data" 확장을 포함해서는 안 된다. 그런 다음 서버는 외부 콘텐츠 유형이 "application_data" (암호화되었음을 나타냄)인 모든 레코드를 구성된 max_early_data_size까지 건너뛰어 초기 데이터를 무시한다.¶
초기 데이터를 처리할 의향이 있음을 나타내기 위해 EncryptedExtensions에 자체 "early_data" 확장을 반환한다. 서버가 초기 데이터 메시지의 일부만 수락하는 것은 불가능하다. 서버가 초기 데이터 수락 메시지를 전송하더라도 실제 초기 데이터는 서버가 이 메시지를 생성할 때 이미 전송 중일 수 있다.¶
초기 데이터를 수락하려면 서버는 클라이언트의 "pre_shared_key" 확장에서 제공된 첫 번째 키를 반드시 선택해야 한다. 또한 다음 값이 선택한 PSK와 연관된 값과 동일한지 반드시 확인해야 한다.¶
이러한 요구사항은 해당 PSK를 사용해 1-RTT 핸드셰이크를 수행하는 데 필요한 요구사항의 상위 집합이다.¶
향후 확장은 0-RTT와의 상호작용을 반드시 정의해야 한다.¶
이러한 검사 중 하나라도 실패하면 서버는 해당 확장으로 응답해서는 안 되며, 위에 나열된 처음 두 메커니즘 중 하나를 사용해 첫 번째 플라이트의 모든 데이터를 폐기해야 한다 (따라서 1-RTT 또는 2-RTT로 대체됨). 클라이언트가 0-RTT 핸드셰이크를 시도했으나 서버가 거부한 경우, 서버에는 일반적으로 0-RTT 레코드 보호 키가 없으므로 첫 번째 비0-RTT 메시지를 찾기 위해 시험 복호화 (1-RTT 핸드셰이크 키를 사용하거나, HelloRetryRequest의 경우 평문 ClientHello를 검색)를 사용해야 한다.¶
서버가 "early_data" 확장을 수락하기로 선택하면 초기 데이터 레코드를 처리할 때 모든 레코드에 명세된 것과 동일한 오류 처리 요구사항을 반드시 준수해야 한다. 구체적으로, 수락된 "early_data" 확장 뒤의 0-RTT 레코드 복호화에 실패하면 서버는 섹션 5.2에 따라 "bad_record_mac" 경고와 함께 연결을 반드시 종료해야 한다.¶
서버가 "early_data" 확장을 거부한 경우 클라이언트 애플리케이션은 핸드셰이크가 완료된 후 이전에 초기 데이터로 전송한 애플리케이션 데이터를 재전송하도록 선택할 수 있다. 초기 데이터를 자동으로 재전송하면 연결 상태에 대한 잘못된 가정을 초래할 수 있다는 점에 유의한다. 예를 들어 협상된 연결이 초기 데이터에 사용한 것과 다른 ALPN 프로토콜을 선택하면 애플리케이션은 다른 메시지를 구성해야 할 수 있다. 마찬가지로 초기 데이터가 연결 상태에 관한 무언가를 가정한다면 핸드셰이크 완료 후 잘못 전송될 수 있다.¶
TLS 구현은 초기 데이터를 자동으로 재전송하지 않는 것이 권고된다. 재전송이 적절한 시점을 판단하는 데에는 애플리케이션이 더 적합하다. TLS 구현은 협상된 연결이 동일한 ALPN 프로토콜을 선택하지 않는 한 초기 데이터를 자동으로 재전송해서는 안 된다.¶
서버의 다음 두 메시지인 EncryptedExtensions와 CertificateRequest에는 나머지 핸드셰이크를 결정하는 서버의 정보가 포함된다. 이 메시지는 server_handshake_traffic_secret에서 유도한 키로 암호화된다.¶
모든 핸드셰이크에서 서버는 ServerHello 메시지 직후에 EncryptedExtensions 메시지를 반드시 전송해야 한다. 이는 server_handshake_traffic_secret에서 유도된 키로 암호화되는 첫 번째 메시지이다.¶
EncryptedExtensions 메시지는 보호할 수 있는 확장, 즉 암호화 콘텍스트를 설정하는 데 필요하지 않고 개별 인증서와도 연관되지 않은 확장을 포함한다. 클라이언트는 EncryptedExtensions에 금지된 확장이 존재하는지 반드시 확인하고, 발견되면 "illegal_parameter" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
이 메시지의 구조:¶
struct {
Extension extensions<0..2^16-1>;
} EncryptedExtensions;
¶
인증서로 인증하는 서버는 선택적으로 클라이언트에 인증서를 요청할 수 있다. 이 메시지를 전송하는 경우 EncryptedExtensions 뒤에 반드시 와야 한다.¶
이 메시지의 구조:¶
struct {
opaque certificate_request_context<0..2^8-1>;
Extension extensions<0..2^16-1>;
} CertificateRequest;
¶
인증서 요청을 식별하고 클라이언트의 Certificate 메시지에서 그대로 반환되는 불투명 문자열. certificate_request_context는 이 연결 범위 안에서 고유해야 한다(따라서 클라이언트 CertificateVerify 메시지의 재전송을 방지함). 이 필드는 섹션 4.7.2에 설명된 핸드셰이크 후 인증 교환에 사용하지 않는 한 길이가 0이어야 한다. 핸드셰이크 후 인증을 요청할 때 서버는 클라이언트의 개인 키에 일시적으로 접근한 공격자가 유효한 CertificateVerify 메시지를 미리 계산하는 것을 방지하기 위해 콘텍스트를 클라이언트가 예측할 수 없게 만드는 것이 권고된다 (예: 무작위 생성).¶
요청하는 인증서의 매개변수를 설명하는 확장 목록. "signature_algorithms" 확장은 반드시 지정해야 하며, 이 메시지에 대해 정의된 경우 다른 확장을 선택적으로 포함할 수 있다. 클라이언트는 인식하지 못하는 확장을 반드시 무시해야 한다.¶
이전 TLS 버전에서 CertificateRequest 메시지는 서버가 수락할 서명 알고리즘과 인증 기관 목록을 전달했다. TLS 1.3에서 전자는 "signature_algorithms"와 선택적 "signature_algorithms_cert" 확장을 전송하여 표현한다. 후자는 "certificate_authorities" 확장을 전송하여 표현한다 (섹션 4.3.4 참조).¶
재개 PSK로 인증하는 서버는 기본 핸드셰이크에서 CertificateRequest 메시지를 전송해서는 안 되지만, 클라이언트가 "post_handshake_auth" 확장 (섹션 4.3.6 참조)을 전송한 경우 핸드셰이크 후 인증에서 (섹션 4.7.2 참조) 전송할 수 있다. 달리 규정하는 다른 명세가 없는 경우 외부 PSK로 인증하는 서버는 기본 핸드셰이크에서 CertificateRequest 메시지를 전송해서는 안 되며, 핸드셰이크 후 인증도 요청해서는 안 된다. [RFC8773]은 이를 허용하는 확장을 제공하지만 이 명세보다 분석이 적게 이루어졌다.¶
섹션 2에서 설명한 것처럼 TLS는 일반적으로 인증, 키 확인 및 핸드셰이크 무결성을 위해 Certificate, CertificateVerify 및 Finished라는 공통 메시지 집합을 사용한다. (PSK 바인더도 유사한 방식으로 키 확인을 수행한다.) 이 세 메시지는 항상 해당 핸드셰이크 플라이트의 마지막 메시지로 전송된다. Certificate 및 CertificateVerify 메시지는 아래에 정의된 특정 상황에서만 전송된다. Finished 메시지는 항상 인증 블록의 일부로 전송된다. 이러한 메시지는 핸드셰이크 후 인증을 제외하고 [sender]_handshake_traffic_secret에서 유도된 키로 암호화된다.¶
인증 메시지의 모든 계산에는 일관되게 다음 입력이 사용된다.¶
이러한 입력을 기반으로 메시지는 다음을 포함한다.¶
인증에 사용할 인증서와 체인의 모든 지원 인증서. 인증서 기반 클라이언트 인증은 PSK 핸드셰이크 흐름 (0-RTT 포함)에서 사용할 수 없다는 점에 유의한다.¶
Transcript-Hash(Handshake Context, Certificate) 값에 대한 서명.¶
기본 키에서 유도된 MAC 키를 사용하여 Transcript-Hash(Handshake Context, Certificate, CertificateVerify) 값에 대해 계산한 MAC.¶
다음 표는 각 시나리오의 핸드셰이크 콘텍스트와 MAC 기본 키를 정의한다.¶
| 모드 | 핸드셰이크 콘텍스트 | 기본 키 |
|---|---|---|
| 서버 | ClientHello ... EncryptedExtensions/ CertificateRequest 중 나중 메시지 | server_handshake_traffic_secret |
| 클라이언트 | ClientHello ... 서버 Finished/EndOfEarlyData 중 나중 메시지 | client_handshake_traffic_secret |
| 핸드셰이크 후 | ClientHello ... 클라이언트 Finished + CertificateRequest | [sender]_application_traffic_secret_N |
이 메시지는 엔드포인트의 인증서 체인을 피어에 전달한다.¶
합의된 키 교환 방식이 인증에 인증서를 사용할 때마다 서버는 Certificate 메시지를 반드시 전송해야 한다 (이 문서에 정의된 PSK 이외의 모든 키 교환 방식 포함).¶
서버가 CertificateRequest 메시지를 통해 인증서 기반 클라이언트 인증을 요청한 경우에만 클라이언트는 Certificate 메시지를 반드시 전송해야 한다 (섹션 4.4.2). 서버가 인증서 기반 클라이언트 인증을 요청했지만 적합한 인증서를 사용할 수 없는 경우 클라이언트는 인증서가 없는 Certificate 메시지 (즉, "certificate_list" 필드의 길이가 0인 메시지)를 반드시 전송해야 한다. Certificate 메시지가 비어 있는지와 관계없이 Finished 메시지를 반드시 전송해야 한다.¶
이 메시지의 구조:¶
enum {
X509(0),
RawPublicKey(2),
(255)
} CertificateType;
struct {
select (certificate_type) {
case RawPublicKey:
/* RFC 7250의 ASN.1_subjectPublicKeyInfo */
opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;
case X509:
opaque cert_data<1..2^24-1>;
};
Extension extensions<0..2^16-1>;
} CertificateEntry;
struct {
opaque certificate_request_context<0..2^8-1>;
CertificateEntry certificate_list<0..2^24-1>;
} Certificate;
¶
이 메시지가 CertificateRequest에 대한 응답이면, 해당 메시지의 certificate_request_context 값. 그렇지 않으면 (서버 인증의 경우) 이 필드의 길이는 0이어야 한다.¶
각각 하나의 인증서와 확장 목록을 포함하는 CertificateEntry 구조체의 목록(체인).¶
CertificateEntry의 확장 값 목록. "Extension" 형식은 섹션 4.3에 정의되어 있다. 현재 서버 인증서에 유효한 확장에는 OCSP 상태 확장 [RFC6066]과 SignedCertificateTimestamp 확장 [RFC6962]이 포함되며, 향후 이 메시지에 대한 확장도 정의할 수 있다. 서버의 Certificate 메시지에 있는 확장은 ClientHello 메시지의 확장에 반드시 대응해야 한다. 클라이언트의 Certificate 메시지에 있는 확장은 서버의 CertificateRequest 메시지에 있는 확장에 반드시 대응해야 한다. 확장이 전체 체인에 적용되는 경우 첫 번째 CertificateEntry에 포함하는 것이 권고된다.¶
대응하는 인증서 유형 확장 ("server_certificate_type" 또는 "client_certificate_type")이 EncryptedExtensions에서 협상되지 않았거나 X.509 인증서 유형이 협상된 경우 각 CertificateEntry는 DER 인코딩 X.509 인증서를 포함한다. 송신자의 인증서는 목록의 첫 번째 CertificateEntry에 반드시 와야 한다. 이후 각 인증서는 바로 앞의 인증서를 직접 인증하는 것이 권고된다. 인증서 검증을 위해 신뢰 앵커를 독립적으로 배포해야 하므로, 지원되는 피어가 생략된 인증서를 보유한 것으로 알려진 경우 신뢰 앵커를 지정하는 인증서를 체인에서 생략할 수 있다.¶
참고: TLS 1.3 이전에는 "certificate_list" 순서에서 각 인증서가 바로 앞의 인증서를 인증해야 했지만, 일부 구현은 어느 정도 유연성을 허용했다. 서버는 전환 목적으로 현재 중간 인증서와 사용 중단된 중간 인증서를 함께 전송하기도 하며, 일부는 단순히 잘못 구성되어 있지만 이러한 경우에도 적절하게 검증할 수 있다. 최대한의 호환성을 위해 모든 구현은 최종 엔터티 인증서가 첫 번째여야 한다는 예외를 제외하고, 모든 TLS 버전에서 잠재적으로 불필요한 인증서와 임의의 순서를 처리할 준비가 되어 있는 것이 권고된다.¶
RawPublicKey 인증서 유형을 협상한 경우 certificate_list는 CertificateEntry를 하나보다 많이 포함해서는 안 되며, 해당 항목은 [RFC7250], 섹션 3에 정의된 ASN1_subjectPublicKeyInfo 값을 포함한다.¶
OpenPGP 인증서 유형 [RFC6091]은 TLS 1.3과 함께 사용해서는 안 된다.¶
서버의 certificate_list는 항상 비어 있지 않아야 한다. 클라이언트는 서버의 인증 요청에 응답하여 전송할 적절한 인증서가 없으면 빈 certificate_list를 전송한다.¶
[RFC6066]과 [RFC6961]은 서버가 클라이언트에 OCSP 응답을 전송하도록 협상하는 확장을 제공한다. TLS 1.2 이하에서 서버는 빈 확장으로 응답하여 이 확장의 협상을 나타내고 OCSP 정보는 CertificateStatus 메시지에 전달된다. TLS 1.3에서는 서버의 OCSP 정보가 연관된 인증서를 포함하는 CertificateEntry의 확장에 전달된다. 구체적으로 서버의 "status_request" 확장 본문은 [RFC6066]에 정의된 CertificateStatus 구조체여야 하며, [RFC6960]에 정의된 대로 해석한다.¶
참고: status_request_v2 확장 [RFC6961]은 사용 중단되었다. TLS 1.3 서버는 ClientHello 메시지를 처리할 때 이 확장의 존재나 그 안의 정보를 기반으로 동작해서는 안 된다. 특히 EncryptedExtensions, CertificateRequest 또는 Certificate 메시지에서 status_request_v2 확장을 전송해서는 안 된다. 이전 프로토콜 버전에서 이를 사용하려는 클라이언트가 전송할 수 있으므로, TLS 1.3 서버는 이를 포함하는 ClientHello 메시지를 처리할 수 반드시 있어야 한다.¶
서버는 CertificateRequest 메시지에서 빈 "status_request" 확장을 전송하여 클라이언트가 인증서와 함께 OCSP 응답을 제시하도록 요청할 수 있다. 클라이언트가 OCSP 응답을 전송하기로 선택하면 "status_request" 확장의 본문은 [RFC6066]에 정의된 CertificateStatus 구조체여야 한다.¶
마찬가지로 [RFC6962]는 TLS 1.2 이하에서 서버가 ServerHello의 확장으로 서명된 인증서 타임스탬프(SCT)를 전송하는 메커니즘을 제공한다. TLS 1.3에서 서버의 SCT 정보는 CertificateEntry의 확장에 전달된다.¶
클라이언트 또는 서버가 전송하는 인증서에는 다음 규칙이 적용된다.¶
명시적으로 달리 협상하지 않는 한 (예: [RFC7250]), 인증서 유형은 X.509v3 [RFC5280]여야 한다.¶
최종 엔터티 인증서는 피어의 "signature_algorithms" 확장에 나타난 서명 방식으로 키를 서명에 사용할 수 있도록 반드시 허용해야 한다 (섹션 4.3.3 참조). 즉, 키 사용 확장이 존재하면 digitalSignature 비트를 반드시 설정해야 하며, 공개 키 (연관된 제한 포함)는 지원되는 일부 서명 방식과 반드시 호환되어야 한다.¶
피어가 "certificate_authorities" 확장을 전송한 경우 인증서 체인의 인증서 중 하나 이상은 나열된 CA 중 하나에서 발급된 것이 권고된다.¶
클라이언트가 전송한 인증서에는 다음 규칙이 추가로 적용된다.¶
CertificateRequest 메시지에 비어 있지 않은 "oid_filters" 확장이 포함된 경우 최종 엔터티 인증서는 섹션 4.3.5에 설명된 대로 클라이언트가 인식하는 확장 OID와 반드시 일치해야 한다.¶
서버가 전송한 인증서에는 다음 규칙이 추가로 적용된다.¶
"server_name" [RFC6066] 확장은 인증서 선택을 안내하는 데 사용된다. 서버는 "server_name" 확장의 존재를 요구할 수 있으므로, 서버가 이름으로 식별되는 경우 클라이언트는 이 확장을 전송하는 것이 권고된다.¶
송신자가 제공한 모든 인증서는 피어가 알린 서명 알고리즘으로 서명된 체인을 제공할 수 있는 경우 해당 알고리즘으로 반드시 서명되어야 한다 (섹션 4.3.3 참조). 자체 서명 인증서 또는 신뢰 앵커로 예상되는 인증서는 체인의 일부로 검증되지 않으므로 어떤 알고리즘으로든 서명될 수 있다.¶
송신자가 서버이고 서버가 표시된 지원 알고리즘으로만 서명된 인증서 체인을 생성할 수 없는 경우, 클라이언트가 지원하는 것으로 알려지지 않은 알고리즘을 포함할 수 있는 자체 선택의 인증서 체인을 전송하여 핸드셰이크를 계속하는 것이 권고된다. 클라이언트가 SHA-1을 수락할 의향이 있음을 명시적으로 알리지 않은 한 이 대체 체인은 사용 중단된 SHA-1 해시를 사용해서는 안 된다.¶
송신자가 클라이언트인 경우 위와 같은 대체 체인을 사용하거나 익명으로 핸드셰이크를 계속할 수 있다.¶
수신자가 제공된 인증서를 사용해 허용 가능한 체인을 구성할 수 없어 핸드셰이크를 중단하기로 결정한 경우, 적절한 인증서 관련 경고 (기본값은 "unsupported_certificate"; 자세한 내용은 섹션 6.2 참조)와 함께 핸드셰이크를 반드시 중단해야 한다.¶
송신자가 여러 인증서를 가진 경우 위 기준 (전송 계층 엔드포인트, 로컬 구성 및 선호도 같은 다른 기준 포함)에 따라 그중 하나를 선택한다.¶
일반적으로 자세한 인증서 검증 절차는 TLS의 범위를 벗어난다 ([RFC5280] 참조). 이 섹션에서는 TLS별 요구사항을 제공한다.¶
서버가 빈 Certificate 메시지를 제공하면 클라이언트는 "decode_error" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
클라이언트가 인증서를 전송하지 않는 경우 (즉, 빈 Certificate 메시지를 전송하는 경우), 서버는 재량에 따라 클라이언트 인증 없이 핸드셰이크를 계속하거나 "certificate_required" 경고와 함께 핸드셰이크를 중단할 수 있다. 또한 인증서 체인의 일부 측면이 허용되지 않는 경우 (예: 알려진 신뢰할 수 있는 CA에서 서명하지 않음) 서버는 재량에 따라 클라이언트를 인증되지 않은 것으로 간주하여 핸드셰이크를 계속하거나 핸드셰이크를 중단할 수 있다.¶
MD5 해시를 사용하는 서명 알고리즘으로 검증해야 하는 인증서를 수신한 모든 엔드포인트는 "bad_certificate" 경고와 함께 핸드셰이크를 반드시 중단해야 한다. SHA-1은 사용 중단되었으며, SHA-1 해시를 사용하는 서명 알고리즘으로 검증해야 하는 인증서를 수신한 엔드포인트는 "bad_certificate" 경고와 함께 핸드셰이크를 중단하는 것이 권고된다. 명확히 말하면, 엔드포인트는 자체 서명 인증서 또는 신뢰 앵커인 인증서에 대해 이러한 알고리즘을 수락할 수 있다.¶
현재 SHA-1 지원을 단계적으로 폐지하는 구현과의 상호 운용성을 유지하기 위해 모든 엔드포인트는 가능한 한 빨리 SHA-256 이상으로 전환하는 것이 권고된다.¶
특정 서명 알고리즘용 키를 포함하는 인증서가 다른 서명 알고리즘으로 서명될 수 있다는 점에 유의한다 (예: ECDSA 키로 서명된 RSA 키).¶
이 메시지는 엔드포인트가 인증서에 대응하는 개인 키를 보유하고 있음을 명시적으로 증명하는 데 사용된다. CertificateVerify 메시지는 이 시점까지의 핸드셰이크 무결성도 제공한다. 서버는 인증서를 통해 인증할 때 이 메시지를 반드시 전송해야 한다. 클라이언트는 인증서를 통해 인증할 때마다 (즉, Certificate 메시지가 비어 있지 않은 경우) 이 메시지를 반드시 전송해야 한다. 이 메시지를 전송하는 경우 Certificate 메시지 직후, Finished 메시지 직전에 반드시 나타나야 한다.¶
이 메시지의 구조:¶
struct {
SignatureScheme algorithm;
opaque signature<0..2^16-1>;
} CertificateVerify;
¶
algorithm 필드는 사용한 서명 알고리즘을 지정한다 (이 형식의 정의는 섹션 4.3.3 참조). signature은 해당 알고리즘을 사용하는 디지털 서명이다. 서명이 적용되는 내용은 섹션 4.1에 설명된 해시 출력으로, 다음과 같다.¶
Transcript-Hash(Handshake Context, Certificate)¶
그런 다음 다음 항목을 이어 붙인 값에 대해 디지털 서명을 계산한다.¶
이 구조는 ServerKeyExchange 형식 때문에 공격자가 선택한 32바이트 접두사(ClientHello.random)를 가진 메시지의 서명을 얻을 수 있었던 이전 TLS 버전에 대한 공격을 방지하기 위한 것이다. 초기 64바이트 패딩은 서버가 제어하는 ServerHello.random과 함께 해당 접두사의 효과를 제거한다.¶
서버 서명의 콘텍스트 문자열은 "TLS 1.3, server CertificateVerify"이다. 클라이언트 서명의 콘텍스트 문자열은 "TLS 1.3, client CertificateVerify"이다. 이는 서로 다른 콘텍스트에서 생성된 서명을 분리하여 잠재적인 교차 프로토콜 공격을 방지하는 데 사용된다.¶
예를 들어 트랜스크립트 해시가 값 01인 32바이트였다면(이 길이는 SHA-256에 적합함), 서버 CertificateVerify의 디지털 서명이 적용되는 내용은 다음과 같다.¶
2020202020202020202020202020202020202020202020202020202020202020 2020202020202020202020202020202020202020202020202020202020202020 544c5320312e332c207365727665722043657274696669636174655665726966 79 00 0101010101010101010101010101010101010101010101010101010101010101¶
송신자 측에서 CertificateVerify 메시지의 signature 필드를 계산하는 과정은 다음을 입력으로 사용한다.¶
CertificateVerify 메시지를 서버가 전송하는 경우, 지원되지 않는 알고리즘 없이는 유효한 인증서 체인을 생성할 수 없는 경우를 제외하고 서명 알고리즘은 클라이언트의 "signature_algorithms" 확장에서 제공된 알고리즘 중 하나여야 한다 (섹션 4.3.3 참조).¶
클라이언트가 전송하는 경우 서명에 사용되는 서명 알고리즘은 CertificateRequest 메시지의 "signature_algorithms" 확장에 있는 supported_signature_algorithms 필드의 알고리즘 중 하나여야 한다.¶
또한 서명 알고리즘은 송신자의 최종 엔터티 인증서에 있는 키와 반드시 호환되어야 한다. SHA-1 알고리즘은 CertificateVerify 메시지의 어떠한 서명에도 사용해서는 안 된다. 이 명세의 모든 SHA-1 서명 알고리즘은 레거시 인증서에만 사용하도록 정의되었으며 CertificateVerify 서명에는 유효하지 않다.¶
CertificateVerify 메시지의 수신자는 signature 필드를 반드시 검증해야 한다. 검증 과정은 다음을 입력으로 사용한다.¶
디지털 서명이 적용되는 내용¶
연관된 Certificate 메시지에서 찾은 최종 엔터티 인증서에 포함된 공개 키¶
CertificateVerify 메시지의 signature 필드에서 수신한 디지털 서명¶
검증에 실패하면 수신자는 "decrypt_error" 경고와 함께 핸드셰이크를 반드시 종료해야 한다.¶
Finished 메시지는 인증 블록의 마지막 메시지이다. 이 메시지는 핸드셰이크와 계산된 키를 인증하는 데 필수적이다.¶
Finished 메시지의 수신자는 내용이 올바른지 반드시 검증해야 하며, 올바르지 않으면 "decrypt_error" 경고와 함께 연결을 반드시 종료해야 한다.¶
한쪽이 Finished 메시지를 전송하고 피어의 Finished 메시지를 수신하여 검증하면 연결을 통해 애플리케이션 데이터를 송수신하기 시작할 수 있다. 피어의 Finished를 수신하기 전에 데이터를 전송할 수 있는 경우는 다음 두 가지이다.¶
서버는 첫 번째 플라이트를 전송한 후 데이터를 전송할 수 있지만, 핸드셰이크가 아직 완료되지 않았으므로 피어의 신원이나 피어가 실제로 활성 상태라는 보장이 없다 (즉, ClientHello가 재전송된 것일 수 있음).¶
Finished 메시지를 계산하는 데 사용되는 키는 섹션 4.5에 정의된 기본 키에서 HKDF를 사용해 계산한다(섹션 7.1 참조). 구체적으로 다음과 같다.¶
finished_key =
HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
¶
이 메시지의 구조:¶
struct {
opaque verify_data[Hash.length];
} Finished;
¶
verify_data 값은 다음과 같이 계산한다.¶
verify_data =
HMAC(finished_key,
Transcript-Hash(Handshake Context,
Certificate*, CertificateVerify*))
¶
존재하는 경우에만 포함된다.¶
HMAC [RFC2104]은 핸드셰이크의 해시 알고리즘을 사용한다. 위에서 언급한 것처럼 HMAC 입력은 일반적으로 실행 중인 해시, 즉 이 시점의 핸드셰이크 해시만으로 구현할 수 있다.¶
이전 TLS 버전에서 verify_data의 길이는 항상 12옥텟이었다. TLS 1.3에서는 핸드셰이크에 사용되는 해시의 HMAC 출력 크기이다.¶
참고: 경고와 기타 모든 비핸드셰이크 레코드 유형은 핸드셰이크 메시지가 아니므로 해시 계산에 포함되지 않는다.¶
Finished 메시지 뒤에 오는 모든 레코드는 섹션 7.2에 설명된 대로 적절한 애플리케이션 트래픽 키로 반드시 암호화해야 한다. 특히 여기에는 클라이언트의 Certificate 및 CertificateVerify 메시지에 대한 응답으로 서버가 전송하는 모든 경고가 포함된다.¶
struct {} EndOfEarlyData;
¶
서버가 EncryptedExtensions에서 "early_data" 확장을 전송한 경우, 클라이언트는 서버 Finished를 수신한 후 EndOfEarlyData 메시지를 반드시 전송해야 한다. 서버가 EncryptedExtensions에서 "early_data" 확장을 전송하지 않은 경우 클라이언트는 EndOfEarlyData 메시지를 전송해서는 안 된다. 이 메시지는 존재하는 모든 0-RTT application_data 메시지가 전송되었으며 이후 레코드가 핸드셰이크 트래픽 키로 보호됨을 나타낸다. 서버는 이 메시지를 전송해서는 안 되며, 이 메시지를 수신한 클라이언트는 "unexpected_message" 경고와 함께 연결을 반드시 종료해야 한다. 이 메시지는 client_early_traffic_secret에서 유도된 키로 암호화된다.¶
TLS에서는 기본 핸드셰이크 후에 다른 메시지를 전송할 수도 있다. 이러한 메시지는 핸드셰이크 콘텐츠 유형을 사용하며 적절한 애플리케이션 트래픽 키로 암호화된다.¶
클라이언트의 hello에 적합한 "psk_key_exchange_modes" 확장이 포함된 경우 서버는 클라이언트 Finished 메시지를 수신한 이후 언제든지 NewSessionTicket 메시지를 전송할 수 있다. NewSessionTicket 메시지는 티켓 값과 재개 비밀값에서 유도된 비밀 PSK 사이에 고유한 연관 관계를 생성한다 (섹션 7 참조).¶
클라이언트는 향후 핸드셰이크에서 ClientHello의 "pre_shared_key" 확장에 티켓 값을 포함하여 이 PSK를 사용할 수 있다 (섹션 4.3.11). NewSessionTicket 메시지를 수신했지만 재개를 지원하지 않는 클라이언트는 이 메시지를 조용히 반드시 무시해야 한다. 재개는 원래 연결이 아직 열려 있는 동안 수행할 수 있다. 서버는 단일 연결에서 여러 티켓을 서로 연속해서 즉시 또는 특정 이벤트 이후에 전송할 수 있다 (부록 C.4 참조). 예를 들어 서버는 핸드셰이크 후 인증 후에 새 티켓을 전송하여 추가된 클라이언트 인증 상태를 캡슐화할 수 있다. 여러 티켓은 다음을 포함한 다양한 목적으로 클라이언트에 유용하다.¶
모든 티켓은 원래 연결을 설정하는 데 사용된 것과 동일한 KDF 해시 알고리즘을 사용하는 암호 스위트로만 반드시 재개해야 한다.¶
새 SNI 값이 원래 세션에서 제시된 서버 인증서에 유효한 경우에만 클라이언트는 반드시 재개해야 하며, SNI 값이 원래 세션에서 사용한 값과 일치하는 경우에만 재개하는 것이 권고된다. 후자는 성능 최적화이다. 일반적으로 하나의 인증서가 적용되는 서로 다른 서버가 서로의 티켓을 수락할 수 있다고 예상할 이유가 없으므로, 그러한 경우 재개를 시도하면 일회용 티켓을 낭비하게 된다. 이러한 표시가 외부나 다른 수단을 통해 제공된 경우 클라이언트는 다른 SNI 값으로 재개할 수 있다.¶
재개 시 호출 애플리케이션에 SNI 값을 보고하는 경우, 구현은 이전 세션에서 전송된 값이 아니라 재개 ClientHello에서 전송된 값을 반드시 사용해야 한다. 서버 구현이 SNI 값이 다른 모든 PSK 식별자를 거부하면 이 두 값은 항상 동일하다는 점에 유의한다.¶
참고: 재개 비밀값은 클라이언트의 두 번째 플라이트에 의존하지만, 인증서 기반 클라이언트 인증을 요청하지 않는 서버는 트랜스크립트의 나머지를 독립적으로 계산한 다음 클라이언트 Finished를 기다리지 않고 자체 Finished를 전송하는 즉시 NewSessionTicket을 전송할 수 있다. 예를 들어 클라이언트가 여러 TLS 연결을 병렬로 열 것으로 예상되고 재개 핸드셰이크의 감소된 오버헤드로 이점을 얻을 수 있는 경우 적절할 수 있다.¶
struct {
uint32 ticket_lifetime;
uint32 ticket_age_add;
opaque ticket_nonce<0..255>;
opaque ticket<1..2^16-1>;
Extension extensions<0..2^16-1>;
} NewSessionTicket;
¶
티켓 발행 시점부터의 수명을 초 단위 32비트 부호 없는 정수로 네트워크 바이트 순서로 나타낸다. 서버는 604800초(7일)보다 큰 값을 사용해서는 안 된다. 값 0은 티켓을 즉시 폐기해야 함을 나타낸다. 클라이언트는 ticket_lifetime과 관계없이 발행 후 7일보다 오래 티켓을 사용해서는 안 되며, 로컬 정책에 따라 더 일찍 티켓을 삭제할 수 있다. 서버는 티켓을 ticket_lifetime에 명시된 기간보다 짧은 기간 동안 유효한 것으로 처리할 수 있다.¶
클라이언트가 "pre_shared_key" 확장에 포함하는 티켓 수명을 난독화하는 데 사용하는 안전하게 생성된 무작위 32비트 값. 클라이언트 측 티켓 수명에 이 값을 232으로 모듈로 덧셈하여 클라이언트가 전송하는 값을 얻는다. 서버는 전송하는 각 티켓마다 새로운 값을 반드시 생성해야 한다.¶
이 연결에서 발행된 모든 티켓 중에서 고유한 티켓별 값.¶
PSK 식별자로 사용할 티켓 값. 티켓 자체는 불투명 레이블이다. 데이터베이스 조회 키 또는 자체 암호화 및 자체 인증된 값일 수 있다.¶
티켓의 확장 값 목록. "Extension" 형식은 섹션 4.3에 정의되어 있다. 클라이언트는 인식하지 못하는 확장을 반드시 무시해야 한다.¶
현재 NewSessionTicket에 정의된 유일한 확장은 "early_data"이며, 티켓을 0-RTT 데이터 전송에 사용할 수 있음을 나타낸다 (섹션 4.3.10). 다음 값을 포함한다.¶
클라이언트가 이 티켓을 사용할 때 전송할 수 있는 0-RTT 데이터의 최대 양(바이트 단위). 애플리케이션 데이터 페이로드 (즉, 평문이며 패딩이나 내부 콘텐츠 유형 바이트는 제외)만 계산한다. max_early_data_size바이트보다 많은 0-RTT 데이터를 수신한 서버는 "unexpected_message" 경고와 함께 연결을 종료하는 것이 권고된다. 암호화 자료 부족으로 초기 데이터를 거부하는 서버는 패딩과 콘텐츠를 구분할 수 없으므로, 클라이언트는 초기 데이터 레코드에서 많은 양의 패딩을 전송할 수 있다는 점에 의존하지 않는 것이 권고된다.¶
티켓과 연관된 PSK는 다음과 같이 계산한다.¶
HKDF-Expand-Label(resumption_secret,
"resumption", ticket_nonce, Hash.length)
¶
ticket_nonce 값은 각 NewSessionTicket 메시지마다 다르므로 각 티켓에서 서로 다른 PSK가 유도된다.¶
원칙적으로 초기 비PSK 핸드셰이크 (대부분 피어의 인증서와 연계됨)에서 원래 유도된 키 자료의 수명을 무기한 연장하는 새 티켓을 계속 발행할 수 있다는 점에 유의한다. 구현은 이러한 키 자료의 전체 수명에 제한을 두는 것이 권고된다. 이러한 제한은 피어 인증서의 수명, 그 사이에 폐기되었을 가능성 및 피어의 온라인 CertificateVerify 서명 이후 경과 시간을 고려해야 한다.¶
클라이언트가 "post_handshake_auth" 확장을 전송한 경우 (섹션 4.3.6 참조), 서버는 핸드셰이크가 완료된 후 언제든지 CertificateRequest 메시지를 전송하여 인증서 기반 클라이언트 인증을 요청할 수 있다. 클라이언트는 적절한 인증 메시지로 반드시 응답해야 한다 (섹션 4.5 참조). 클라이언트가 인증하기로 선택하면 Certificate, CertificateVerify 및 Finished를 반드시 전송해야 한다. 거부하는 경우 인증서가 없는 Certificate 메시지를 전송한 후 Finished를 반드시 전송해야 한다. 특정 응답에 대한 클라이언트의 모든 메시지는 다른 유형의 메시지나 다른 응답의 메시지가 사이에 개입하지 않고 전송선에서 연속으로 반드시 나타나야 한다.¶
"post_handshake_auth" 확장을 전송하지 않고 핸드셰이크 후 CertificateRequest 메시지를 수신한 클라이언트는 치명적 "unexpected_message" 경고를 반드시 전송해야 한다.¶
참고: 인증서 기반 클라이언트 인증에는 사용자에게 입력을 요청하는 과정이 포함될 수 있으므로, 서버는 CertificateRequest를 전송한 후 응답을 수신하기까지 임의 개수의 다른 메시지를 수신하는 것을 포함한 일정한 지연에 대비할 수 반드시 있어야 한다. 또한 여러 CertificateRequest를 짧은 간격으로 수신한 클라이언트는 수신한 순서와 다른 순서로 응답할 수 있다 (certificate_request_context 값을 통해 서버가 응답을 구분할 수 있음).¶
KeyUpdate 핸드셰이크 메시지는 송신자가 송신 암호화 키를 갱신하고 있음을 나타내는 데 사용된다. 이 메시지는 어느 피어든 Finished 메시지를 전송한 후에 전송할 수 있다. Finished 메시지를 수신하기 전에 KeyUpdate 메시지를 수신한 구현은 "unexpected_message" 경고와 함께 연결을 반드시 종료해야 한다. KeyUpdate 메시지를 전송한 후 송신자는 섹션 7.2에 설명된 대로 계산한 다음 세대의 키를 사용해 모든 트래픽을 전송해야 한다. KeyUpdate를 수신하면 수신자는 수신 키를 반드시 갱신해야 한다.¶
enum {
update_not_requested(0), update_requested(1), (255)
} KeyUpdateRequest;
struct {
KeyUpdateRequest request_update;
} KeyUpdate;
¶
KeyUpdate 수신자가 자체 KeyUpdate로 응답해야 하는지를 나타낸다. 구현이 다른 값을 수신하면 "illegal_parameter" 경고와 함께 연결을 반드시 종료해야 한다.¶
request_update 필드가 "update_requested"로 설정된 경우, 수신자는 다음 애플리케이션 데이터 레코드를 전송하기 전에 request_update를 "update_not_requested"로 설정한 자체 KeyUpdate를 반드시 전송해야 한다. 이 메커니즘을 통해 어느 쪽이든 전체 연결의 갱신을 강제할 수 있지만, 아무 메시지도 전송하지 않는 동안 여러 KeyUpdate를 수신한 구현은 하나의 갱신으로 응답한다. 피어로부터 이후 KeyUpdate를 수신하기 전까지 송신자는 request_update가 "update_requested"로 설정된 다른 KeyUpdate를 전송해서는 안 된다.¶
메시지가 이미 전송 중일 수 있으므로 구현은 request_update가 "update_requested"로 설정된 KeyUpdate를 전송한 후 피어의 KeyUpdate를 수신하기까지 관련 없는 KeyUpdate를 포함한 임의 개수의 메시지를 수신할 수 있다는 점에 유의한다. 그러나 송신 키와 수신 키는 독립적인 트래픽 비밀값에서 유도되므로, 수신 트래픽 비밀값을 유지해도 송신자가 키를 변경하기 전에 전송한 데이터의 순방향 비밀성이 위협받지 않는다.¶
구현이 각각 독립적으로 request_update가 "update_requested"로 설정된 자체 KeyUpdate를 전송하고 이들이 전송 중에 교차하면 양쪽 모두 응답을 추가로 전송하게 되어 각 쪽이 두 세대만큼 증가한다.¶
송신자와 수신자는 모두 이전 키로 KeyUpdate 메시지를 반드시 암호화해야 한다. 또한 양쪽 모두 새 키로 암호화된 메시지를 수락하기 전에 이전 키로 암호화된 KeyUpdate를 수신하도록 반드시 강제해야 한다. 이를 수행하지 않으면 메시지 잘라내기 공격이 허용될 수 있다.¶
AES-128과 같은 128비트 키에서는 264번 키를 갱신하면 특정 연결 내에서 키가 재사용될 가능성이 높다. 키가 반복되더라도 IV도 독립적으로 생성되므로 키/IV가 함께 충돌할 가능성은 훨씬 낮다는 점에 유의한다. 추가적인 보안 여유를 제공하기 위해 송신 구현은 에포크, 따라서 키 갱신 횟수가 248-1을 초과하도록 허용해서는 안 된다. 예를 들어 128비트보다 긴 키를 사용하는 암호를 위해 이 값을 나중에 변경할 수 있도록 수신 구현은 이 규칙을 강제해서는 안 된다. 송신 구현이 request_update가 "update_requested"로 설정된 KeyUpdate를 수신한 경우, 자체 KeyUpdate를 전송하면 이러한 제한을 초과하게 된다면 이를 전송해서는 안 되며, 대신 "update_requested" 플래그를 무시하는 것이 권고된다. 이로 인해 결국 섹션 5.5의 제한에 도달하면 연결을 종료해야 할 수 있다.¶
TLS 레코드 프로토콜은 전송할 메시지를 받아 데이터를 처리 가능한 블록으로 조각화하고, 레코드를 보호한 후 그 결과를 전송한다. 수신된 데이터는 검증, 복호화 및 재조립된 다음 상위 수준 클라이언트에 전달된다.¶
TLS 레코드에는 유형이 지정되므로 여러 상위 수준 프로토콜을 동일한 레코드 계층에서 다중화할 수 있다. 이 문서는 handshake, application_data, alert 및 change_cipher_spec의 네 가지 콘텐츠 유형을 명세한다. change_cipher_spec 레코드는 호환성 목적으로만 사용된다 (부록 E.4 참조).¶
구현은 첫 번째 ClientHello 메시지가 전송되거나 수신된 후부터 피어의 Finished 메시지를 수신하기 전까지 언제든지 단일 바이트 값 0x01로 구성된 암호화되지 않은 change_cipher_spec 유형의 레코드를 수신할 수 있으며, 추가 처리 없이 이를 단순히 반드시 폐기해야 한다. 이 레코드는 구현이 보호된 레코드를 예상하는 핸드셰이크 지점에 나타날 수 있으므로 레코드의 보호를 해제하기 전에 이 조건을 탐지해야 한다는 점에 유의한다. 다른 change_cipher_spec 값을 수신하거나 보호된 change_cipher_spec 레코드를 수신한 구현은 "unexpected_message" 경고와 함께 핸드셰이크를 반드시 중단해야 한다. 구현이 첫 번째 ClientHello 메시지 전에 또는 피어의 Finished 메시지 후에 수신된 change_cipher_spec 레코드를 탐지하면 예상하지 못한 레코드 유형으로 반드시 처리해야 한다(다만 상태 비저장 서버는 이러한 경우와 허용된 경우를 구분하지 못할 수 있음).¶
구현은 일부 확장에서 협상하지 않는 한 이 문서에 정의되지 않은 레코드 유형을 전송해서는 안 된다. TLS 구현이 예상하지 못한 레코드 유형을 수신하면 "unexpected_message" 경고와 함께 연결을 반드시 종료해야 한다. 새로운 레코드 콘텐츠 유형 값은 섹션 11에 설명된 대로 TLS ContentType 레지스트리에서 IANA가 할당한다.¶
레코드 계층은 정보 블록을 214바이트 이하의 청크로 데이터를 전달하는 TLSPlaintext 레코드로 조각화한다. 메시지 경계는 기반 ContentType에 따라 다르게 처리된다. 향후 콘텐츠 유형은 적절한 규칙을 반드시 명세해야 한다. 이러한 규칙은 TLS 1.2에서 적용된 규칙보다 엄격하다는 점에 유의한다.¶
다음 조건을 충족하는 경우 핸드셰이크 메시지를 하나의 TLSPlaintext 레코드로 병합하거나 여러 레코드에 걸쳐 조각화할 수 있다.¶
핸드셰이크 메시지는 다른 레코드 유형과 번갈아 배치되어서는 안 된다. 즉, 하나의 핸드셰이크 메시지가 둘 이상의 레코드로 분할되면 그 사이에 다른 레코드가 있어서는 안 된다.¶
핸드셰이크 메시지는 키 변경에 걸쳐 있어서는 안 된다. 구현은 키 변경 직전의 모든 메시지가 레코드 경계와 정렬되는지 반드시 확인해야 하며, 그렇지 않으면 "unexpected_message" 경고와 함께 연결을 반드시 종료해야 한다. ClientHello, EndOfEarlyData, ServerHello, Finished 및 KeyUpdate 메시지는 키 변경 직전에 올 수 있으므로 구현은 이러한 메시지를 레코드 경계에 맞춰 반드시 전송해야 한다.¶
구현은 패딩이 포함된 경우에도 Handshake 유형의 길이가 0인 조각을 전송해서는 안 된다.¶
경고 메시지(섹션 6)는 여러 레코드에 걸쳐 조각화되어서는 안 되며, 여러 경고 메시지를 하나의 TLSPlaintext 레코드로 병합해서도 안 된다. 즉, Alert 유형의 레코드는 정확히 하나의 메시지를 반드시 포함해야 한다.¶
애플리케이션 데이터 메시지는 TLS에서 해석하지 않는 데이터를 포함한다. 애플리케이션 데이터 메시지는 항상 보호된다. 애플리케이션 데이터의 길이가 0인 조각 (즉, 콘텐츠 길이가 0인 application_data 유형의 TLSInnerPlaintext 레코드)은 트래픽 분석 대응책으로 잠재적으로 유용하므로 전송할 수 있다. 애플리케이션 데이터 조각은 여러 레코드에 걸쳐 분할하거나 하나의 레코드로 병합할 수 있다.¶
enum {
invalid(0),
change_cipher_spec(20),
alert(21),
handshake(22),
application_data(23),
(255)
} ContentType;
struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
¶
포함된 조각을 처리하는 데 사용되는 상위 수준 프로토콜.¶
초기 ClientHello (즉, HelloRetryRequest 후에 생성되지 않은 ClientHello)를 제외하고 TLS 1.3 구현에서 생성하는 모든 레코드는 0x0303으로 반드시 설정해야 하며, 초기 ClientHello에서는 호환성 목적으로 0x0301일 수도 있다. 이 필드는 사용 중단되었으며 모든 목적으로 반드시 무시해야 한다. 이전 TLS 버전에서는 일부 상황에서 이 필드에 다른 값을 사용했다.¶
뒤따르는 TLSPlaintext.fragment의 길이 (바이트 단위). 길이는 214바이트를 초과해서는 안 된다. 이 길이를 초과하는 레코드를 수신한 엔드포인트는 "record_overflow" 경고와 함께 연결을 반드시 종료해야 한다.¶
전송되는 데이터. 이 값은 투명하며 type 필드에 지정된 상위 수준 프로토콜에서 처리할 독립적인 블록으로 취급된다.¶
이 문서는 버전 0x0304를 사용하는 TLS 1.3을 설명한다. 이 버전 값은 TLS 1.0에 0x0301, SSL 3.0에 0x0300을 사용한 데서 유래한 역사적인 값이다. 하위 호환성을 최대화하기 위해 초기 ClientHello가 포함된 레코드는 TLS 1.0을 나타내는 버전 0x0301을 사용하는 것이 권고되며, 두 번째 ClientHello 또는 ServerHello가 포함된 레코드는 TLS 1.2를 나타내는 버전 0x0303을 반드시 사용해야 한다. 이전 TLS 버전을 협상할 때 엔드포인트는 부록 E에 제시된 절차와 요구사항을 따른다.¶
레코드 보호가 아직 활성화되지 않은 경우 TLSPlaintext 구조체는 전송선에 직접 기록된다. 레코드 보호가 시작되면 TLSPlaintext 레코드는 다음 섹션에 설명된 대로 보호되어 전송된다. 애플리케이션 데이터 레코드는 보호되지 않은 상태로 전송선에 기록해서는 안 된다 (자세한 내용은 섹션 2 참조).¶
레코드 보호 함수는 TLSPlaintext 구조체를 TLSCiphertext 구조체로 변환한다. 보호 해제 함수는 이 과정을 되돌린다. 이전 TLS 버전과 달리 TLS 1.3에서는 모든 암호를 "연관 데이터가 있는 인증된 암호화"(AEAD) [RFC5116]로 모델링한다. AEAD 함수는 평문을 인증된 암호문으로 변환하고 다시 되돌리는 통합 암호화 및 인증 연산을 제공한다. 암호화된 각 레코드는 평문 헤더와 그 뒤의 암호화된 본문으로 구성되며, 본문 자체에는 유형과 선택적 패딩이 포함된다.¶
struct {
opaque content[TLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} TLSInnerPlaintext;
struct {
ContentType opaque_type = application_data; /* 23 */
ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
uint16 length;
opaque encrypted_record[TLSCiphertext.length];
} TLSCiphertext;
¶
핸드셰이크 또는 경고 메시지의 바이트 인코딩이나 전송할 애플리케이션 데이터의 원시 바이트를 포함하는 TLSPlaintext.fragment 값.¶
레코드의 콘텐츠 유형을 포함하는 TLSPlaintext.type 값.¶
type 필드 뒤의 평문에는 값이 0인 임의 길이의 연속 바이트가 나타날 수 있다. 이를 통해 송신자는 전체 크기가 레코드 크기 제한 이내에 있는 한 모든 TLS 레코드를 원하는 크기만큼 패딩할 수 있다. 자세한 내용은 섹션 5.4를 참조한다.¶
이전 TLS 버전을 파싱하는 데 익숙한 미들박스와의 외부 호환성을 위해 TLSCiphertext 레코드의 외부 opaque_type 필드는 항상 값 23(application_data)으로 설정된다. 레코드의 실제 콘텐츠 유형은 복호화 후 TLSInnerPlaintext.type에서 찾을 수 있다.¶
legacy_record_version 필드는 항상 0x0303이다. TLS 1.3 TLSCiphertext는 TLS 1.3이 협상된 후에야 생성되므로 다른 값을 수신할 수 있는 역사적 호환성 문제는 없다. ClientHello와 ServerHello 메시지를 포함한 핸드셰이크 프로토콜이 프로토콜 버전을 인증하므로 이 값은 중복된다는 점에 유의한다.¶
뒤따르는 TLSCiphertext.encrypted_record의 길이(바이트 단위)이며, 콘텐츠와 패딩의 길이 합계에 내부 콘텐츠 유형을 위한 1바이트와 AEAD 알고리즘에서 추가된 확장량을 더한 값이다. 길이는 214 + 256바이트를 초과해서는 안 된다. 이 길이를 초과하는 레코드를 수신한 엔드포인트는 "record_overflow" 경고와 함께 연결을 반드시 종료해야 한다.¶
직렬화된 TLSInnerPlaintext 구조체를 AEAD로 암호화한 형태.¶
AEAD 알고리즘은 [RFC5116]의 섹션 2.1에 설명된 대로 하나의 키, 논스, 평문 및 인증 검사에 포함할 "추가 데이터"를 입력으로 사용한다. 키는 client_write_key 또는 server_write_key이고, 논스는 시퀀스 번호와 client_write_iv 또는 server_write_iv에서 유도되며 (섹션 5.3 참조), 추가 데이터 입력은 레코드 헤더이다. 즉,¶
additional_data = TLSCiphertext.opaque_type ||
TLSCiphertext.legacy_record_version ||
TLSCiphertext.length
¶
AEAD 알고리즘에 입력되는 평문은 인코딩된 TLSInnerPlaintext 구조체이다. 트래픽 키 유도는 섹션 7.3에 정의되어 있다.¶
AEAD 출력은 AEAD 암호화 연산의 암호문 출력으로 구성된다. TLSInnerPlaintext.type과 송신자가 제공한 패딩이 포함되므로 평문의 길이는 대응하는 TLSPlaintext.length보다 크다. AEAD 출력의 길이는 일반적으로 평문보다 크지만 증가량은 AEAD 알고리즘에 따라 달라진다. 암호 자체에 패딩이 포함될 수 있으므로 오버헤드의 양은 평문 길이에 따라 달라질 수 있다. 기호로 나타내면 다음과 같다.¶
AEADEncrypted =
AEAD-Encrypt(write_key, nonce, additional_data, plaintext)
¶
TLSCiphertext의 encrypted_record 필드는 AEADEncrypted로 설정한다.¶
복호화하고 검증하기 위해 암호는 키, 논스, 추가 데이터 및 AEADEncrypted 값을 입력으로 사용한다. 출력은 평문 또는 복호화 실패를 나타내는 오류이다. 별도의 무결성 검사는 없다. 기호로 나타내면 다음과 같다.¶
plaintext of encrypted_record =
AEAD-Decrypt(peer_write_key, nonce, additional_data, AEADEncrypted)
¶
복호화에 실패하면 수신자는 "bad_record_mac" 경고와 함께 연결을 반드시 종료해야 한다.¶
TLS 1.3에서 사용하는 AEAD 알고리즘은 255옥텟보다 큰 확장량을 생성해서는 안 된다. 피어로부터 TLSCiphertext.length가 214 + 256옥텟보다 큰 레코드를 수신한 엔드포인트는 "record_overflow" 경고와 함께 연결을 반드시 종료해야 한다. 이 제한은 최대 TLSInnerPlaintext 길이인 214옥텟, ContentType을 위한 1옥텟 및 최대 AEAD 확장량인 255옥텟에서 도출된다.¶
레코드를 읽고 쓰기 위한 64비트 시퀀스 번호를 별도로 유지한다. 각 레코드를 읽거나 쓴 후 해당 시퀀스 번호를 1씩 증가시킨다. 각 시퀀스 번호는 연결 시작 시와 키가 변경될 때마다 0으로 설정되며, 특정 트래픽 키로 전송되는 첫 번째 레코드는 시퀀스 번호 0을 반드시 사용해야 한다.¶
시퀀스 번호의 크기가 64비트이므로 순환해서는 안 된다. TLS 구현에서 시퀀스 번호를 순환해야 하는 경우 키를 다시 설정하거나 (섹션 4.7.3) 연결을 반드시 종료해야 한다.¶
각 AEAD 알고리즘은 레코드별 논스에 허용되는 입력 길이 범위를 N_MIN바이트에서 N_MAX바이트까지 지정한다 [RFC5116]. TLS 레코드별 논스의 길이(iv_length)는 8바이트와 AEAD 알고리즘의 N_MIN 중 더 큰 값으로 설정한다 ([RFC5116], 섹션 4 참조). N_MAX가 8바이트보다 작은 AEAD 알고리즘은 TLS와 함께 사용해서는 안 된다. AEAD 구성의 레코드별 논스는 다음과 같이 형성한다.¶
64비트 레코드 시퀀스 번호를 네트워크 바이트 순서로 인코딩하고 iv_length가 되도록 왼쪽을 0으로 패딩한다.¶
패딩된 시퀀스 번호를 역할에 따라 정적 client_write_iv 또는 server_write_iv와 XOR한다.¶
그 결과 얻은 iv_length 길이의 값을 레코드별 논스로 사용한다.¶
참고: 이는 일부가 명시적인 논스를 지정한 TLS 1.2의 구성과 다르다.¶
암호화된 모든 TLS 레코드는 TLSCiphertext의 크기를 늘리기 위해 패딩할 수 있다. 이를 통해 송신자는 관찰자에게 트래픽의 크기를 숨길 수 있다.¶
TLSCiphertext 레코드를 생성할 때 구현은 패딩을 선택할 수 있다. 패딩되지 않은 레코드는 패딩 길이가 0인 레코드일 뿐이다. 패딩은 암호화 전에 ContentType 필드 뒤에 추가되는 값이 0인 바이트 문자열이다. 구현은 암호화하기 전에 모든 패딩 옥텟을 0으로 반드시 설정해야 한다.¶
송신자가 원하는 경우 애플리케이션 데이터 레코드는 길이가 0인 TLSInnerPlaintext.content를 포함할 수 있다. 이를 통해 활동의 유무가 민감할 수 있는 콘텍스트에서 그럴듯한 크기의 위장 트래픽을 생성할 수 있다. 구현은 길이가 0인 TLSInnerPlaintext.content를 가진 Handshake 및 Alert 레코드를 전송해서는 안 된다. 이러한 메시지를 수신한 구현은 "unexpected_message" 경고와 함께 연결을 반드시 종료해야 한다.¶
전송된 패딩은 레코드 보호 메커니즘에서 자동으로 검증된다. TLSCiphertext.encrypted_record를 성공적으로 복호화하면 수신 구현은 0이 아닌 옥텟을 찾을 때까지 필드를 끝에서 시작 방향으로 검색한다. 이 0이 아닌 옥텟이 메시지의 콘텐츠 유형이다. 이 패딩 방식은 새로운 콘텐츠 유형을 도입하지 않고도 암호화된 모든 TLS 레코드를 0부터 TLS 레코드 크기 제한까지 임의의 크기로 패딩할 수 있으므로 선택되었다. 이 설계는 모든 패딩 옥텟을 0으로 제한하므로 패딩 오류를 빠르게 탐지할 수도 있다.¶
구현은 검색 범위를 AEAD 복호화에서 반환된 평문으로 반드시 제한해야 한다. 수신 구현이 평문에서 0이 아닌 옥텟을 찾지 못하면 "unexpected_message" 경고와 함께 연결을 반드시 종료해야 한다.¶
패딩이 있어도 전체 레코드 크기 제한은 변경되지 않는다. 인코딩된 전체 TLSInnerPlaintext는 214 + 1옥텟을 초과해서는 안 된다. 예를 들어 [RFC8449]의 record_size_limit 확장에 의해 최대 조각 길이가 감소하면, 감소된 제한은 콘텐츠 유형과 패딩을 포함한 전체 평문에 적용된다.¶
패딩 시점과 패딩량을 결정하는 패딩 정책을 선택하는 것은 복잡한 주제이며 이 명세의 범위를 벗어난다. TLS 위의 애플리케이션 계층 프로토콜에 자체 패딩이 있는 경우 애플리케이션 계층에서 애플리케이션 데이터 TLS 레코드를 패딩하는 편이 더 바람직할 수 있다. 그러나 암호화된 Handshake 또는 Alert 레코드의 패딩은 여전히 TLS 계층에서 처리해야 한다. 향후 문서에서 패딩 선택 알고리즘을 정의하거나 TLS 확장 또는 다른 수단을 통한 패딩 정책 요청 메커니즘을 정의할 수 있다.¶
특정 키 집합으로 안전하게 암호화할 수 있는 평문의 양에는 암호학적 제한이 있다. [AEAD-LIMITS]는 기반 프리미티브 (AES 또는 ChaCha20)에 약점이 없다고 가정하여 이러한 제한을 분석한다. 구현은 이러한 제한에 도달하기 전에 연결을 닫거나 섹션 4.7.3에 설명된 대로 키 갱신을 반드시 수행해야 한다. 초기 데이터에는 KeyUpdate를 수행할 수 없으므로 구현은 초기 데이터를 전송할 때 이 제한을 초과해서는 안 된다. 향후 분석에서 값이 갱신될 수 있으므로 수신 구현은 이러한 제한을 강제하지 않는 것이 권고된다.¶
AES-GCM의 경우 인증된 암호화(AE) 보안에 대해 약 2-57의 안전 여유를 유지하면서 특정 키 집합으로 최대 224.5개의 최대 크기 레코드 (약 2,400만 개)를 암호화할 수 있다. ChaCha20/Poly1305의 경우 안전 제한에 도달하기 전에 레코드 시퀀스 번호가 순환한다.¶
TLS는 종료 정보와 오류를 나타내기 위한 Alert 콘텐츠 유형을 제공한다. 다른 메시지와 마찬가지로 경고 메시지는 현재 연결 상태에서 명세한 대로 암호화된다.¶
경고 메시지는 경고에 대한 설명과 이전 TLS 버전에서 메시지의 심각도 수준을 전달하던 레거시 필드를 포함한다. 경고는 종료 경고와 오류 경고의 두 종류로 나뉜다. TLS 1.3에서는 심각도가 전송되는 경고 유형에 내포되어 있으므로 "level" 필드를 안전하게 무시할 수 있다. "close_notify" 경고는 연결의 한쪽 방향이 정상적으로 종료됨을 나타내는 데 사용된다. 이러한 경고를 수신하면 TLS 구현은 애플리케이션에 데이터 종료를 알리는 것이 권고된다.¶
오류 경고는 연결이 비정상적으로 종료됨을 나타낸다 (섹션 6.2 참조). 오류 경고를 수신하면 TLS 구현은 애플리케이션에 오류를 알리는 것이 권고되며, 연결에서 추가 데이터를 송수신하도록 허용해서는 안 된다. 서버와 클라이언트는 실패한 연결에서 설정된 비밀값과 키를 반드시 폐기해야 한다. 다만 세션 티켓과 연관된 PSK는 예외이며, 가능하면 폐기하는 것이 권고된다.¶
섹션 6.2에 나열된 모든 경고는 AlertLevel=fatal로 반드시 전송해야 하며, 메시지의 AlertLevel과 관계없이 수신 시 오류 경고로 반드시 처리해야 한다. 알 수 없는 Alert 유형도 오류 경고로 반드시 처리해야 한다.¶
참고: TLS는 메시지 파싱 실패 시 사용할 두 가지 일반 경고를 정의한다 (섹션 6 참조). 구문에 따라 파싱할 수 없는 메시지 (예: 길이가 메시지 경계를 넘거나 범위를 벗어난 길이를 포함함)를 수신한 피어는 "decode_error" 경고와 함께 연결을 반드시 종료해야 한다. 구문상 올바르지만 의미상 유효하지 않은 메시지(예: p - 1인 DHE 공유값 또는 유효하지 않은 enum)를 수신한 피어는 "illegal_parameter" 경고와 함께 연결을 반드시 종료해야 한다.¶
enum { warning(1), fatal(2), (255) } AlertLevel;
enum {
close_notify(0),
unexpected_message(10),
bad_record_mac(20),
record_overflow(22),
handshake_failure(40),
bad_certificate(42),
unsupported_certificate(43),
certificate_revoked(44),
certificate_expired(45),
certificate_unknown(46),
illegal_parameter(47),
unknown_ca(48),
access_denied(49),
decode_error(50),
decrypt_error(51),
protocol_version(70),
insufficient_security(71),
internal_error(80),
inappropriate_fallback(86),
user_canceled(90),
missing_extension(109),
unsupported_extension(110),
unrecognized_name(112),
bad_certificate_status_response(113),
unknown_psk_identity(115),
certificate_required(116),
general_error(117),
no_application_protocol(120),
(255)
} AlertDescription;
struct {
AlertLevel level;
AlertDescription description;
} Alert;
¶
잘라내기 공격을 방지하려면 클라이언트와 서버가 연결이 종료되고 있음을 서로 알고 있어야 한다.¶
이 경고는 송신자가 이 연결에서 더 이상 메시지를 전송하지 않을 것임을 수신자에게 알린다. 종료 경고를 수신한 후 수신되는 모든 데이터는 반드시 무시해야 한다. 이 경고는 AlertLevel=warning으로 반드시 전송해야 한다.¶
이 경고는 송신자가 프로토콜 실패와 관련 없는 어떤 이유로 핸드셰이크를 취소하고 있음을 수신자에게 알린다. 핸드셰이크가 완료된 후 사용자가 작업을 취소하는 경우에는 "close_notify"를 전송하여 연결을 닫는 것이 더 적절하다. 이 경고 뒤에는 "close_notify"가 반드시 와야 한다. 이 경고의 AlertLevel은 일반적으로 warning이다. 수신 구현은 "close_notify"를 수신할 때까지 피어에서 데이터를 계속 읽는 것이 권고되지만, 이를 로그에 남기거나 다른 방식으로 기록할 수 있다.¶
어느 쪽이든 "close_notify" 경고를 전송하여 연결의 쓰기 측 종료를 시작할 수 있다. "close_notify"보다 먼저 전송 계층 종료를 수신하면 수신자는 전송된 모든 데이터를 수신했는지 알 수 없다.¶
각 당사자는 이미 오류 경고를 전송한 경우를 제외하고 연결의 쓰기 측을 닫기 전에 "close_notify" 경고를 반드시 전송해야 한다. 이는 연결의 읽기 측에는 영향을 미치지 않는다. 이는 구현이 "close_notify"에 반응하여 대기 중인 쓰기를 폐기하고 즉시 자체 "close_notify" 경고를 전송해야 했던 TLS 1.3 이전 버전과 달라진 사항이다. 이전 요구사항은 읽기 측에서 잘라내기를 일으킬 수 있었다. 양쪽 모두 연결의 읽기 측을 닫기 전에 "close_notify" 경고를 수신할 때까지 기다릴 필요는 없지만, 기다리지 않으면 잘라내기가 발생할 가능성이 생긴다.¶
애플리케이션 프로토콜은 "close_notify"를 수신하면 송신 버퍼를 비우고 즉시 "close_notify"를 전송하도록 선택할 수 있지만, 이 경우 공격자가 "close_notify"나 애플리케이션 패킷의 전송 계층 전달을 지연시켜 피어가 수신하는 데이터에 영향을 줄 수 있다. 이러한 문제는 예를 들어 "close_notify"를 전송한 후 수신되는 패킷을 무시하는 방식으로 애플리케이션 계층에서 해결할 수 있다.¶
TLS를 사용하는 애플리케이션 프로토콜에서 TLS 연결이 닫힌 후에도 기반 전송을 통해 데이터를 전달할 수 있도록 하는 경우, TLS 구현은 애플리케이션 계층에 데이터 종료를 알리기 전에 "close_notify" 경고를 반드시 수신해야 한다. 연결을 열거나 닫는 시점을 포함하여 TLS 사용 프로필이 데이터 전송을 관리하는 방식을 이 표준의 어떤 부분도 규정하는 것으로 해석해서는 안 된다.¶
참고: 연결의 쓰기 측을 닫으면 전송을 폐기하기 전에 대기 중인 데이터가 신뢰성 있게 전달된다고 가정한다.¶
TLS의 오류 처리는 매우 간단하다. 오류가 탐지되면 이를 탐지한 쪽이 피어에 메시지를 전송한다. 치명적 경고 메시지를 전송하거나 수신하면 양쪽 모두 즉시 연결을 반드시 닫아야 한다.¶
구현이 치명적 오류 조건을 만날 때마다 적절한 치명적 경고를 전송하는 것이 권고되며, 추가 데이터를 송수신하지 않고 연결을 반드시 닫아야 한다. 이 명세 전체에서 특정 경고 없이 "연결을 종료한다" 또는 "핸드셰이크를 중단한다"라는 표현을 사용하면 구현이 아래 설명에 지정된 경고를 전송하는 것이 권고된다는 의미이다. "X 경고와 함께 연결을 종료한다" 또는 "X 경고와 함께 핸드셰이크를 중단한다"라는 표현은 구현이 경고를 전송하는 경우 X 경고를 반드시 전송해야 한다는 의미이다. 이 섹션에서 아래에 정의된 모든 경고와 알 수 없는 모든 경고는 TLS 1.3부터 보편적으로 치명적 경고로 간주된다 (섹션 6 참조). 구현은 경고 송수신을 기록하기 위한 방법을 제공하는 것이 권고된다.¶
다음 오류 경고를 정의한다.¶
부적절한 메시지 (예: 잘못된 핸드셰이크 메시지, 너무 이른 애플리케이션 데이터 등)를 수신했다. 올바른 구현 간 통신에서는 이 경고가 절대로 나타나서는 안 된다.¶
보호를 해제할 수 없는 레코드를 수신하면 이 경고를 반환한다. AEAD 알고리즘은 복호화와 검증을 결합하며 부채널 공격을 방지해야 하므로, 모든 보호 해제 실패에 이 경고를 사용한다. 네트워크에서 메시지가 손상된 경우를 제외하면 올바른 구현 간 통신에서는 이 경고가 절대로 나타나서는 안 된다.¶
길이가 214 + 256바이트보다 큰 TLSCiphertext 레코드를 수신했거나, 복호화한 결과 214바이트 (또는 협상된 다른 제한)보다 큰 TLSPlaintext 레코드가 되었다. 네트워크에서 메시지가 손상된 경우를 제외하면 올바른 구현 간 통신에서는 이 경고가 절대로 나타나서는 안 된다.¶
"handshake_failure" 경고 메시지를 수신했다는 것은 송신자가 사용 가능한 옵션으로 허용 가능한 보안 매개변수 집합을 협상할 수 없었음을 나타낸다.¶
인증서가 손상되었거나 올바르게 검증되지 않는 서명이 포함되어 있는 등의 경우.¶
인증서 유형이 지원되지 않는다.¶
인증서가 서명자에 의해 폐기되었다.¶
인증서가 만료되었거나 현재 유효하지 않다.¶
인증서 처리 중 다른 명시되지 않은 문제가 발생하여 인증서를 수락할 수 없다.¶
핸드셰이크의 필드가 잘못되었거나 다른 필드와 일관되지 않는다. 이 경고는 형식적 프로토콜 구문에는 부합하지만 그 밖의 측면에서 올바르지 않은 오류에 사용된다.¶
유효한 인증서 체인 또는 부분 체인을 수신했지만, CA 인증서를 찾을 수 없거나 알려진 신뢰 앵커와 일치시킬 수 없어 인증서를 수락하지 않았다.¶
유효한 인증서 또는 PSK를 수신했지만 접근 제어를 적용한 결과 송신자가 협상을 진행하지 않기로 결정했다.¶
일부 필드가 지정된 범위를 벗어났거나 메시지 길이가 올바르지 않아 메시지를 디코딩할 수 없다. 이 경고는 메시지가 형식적 프로토콜 구문에 부합하지 않는 오류에 사용된다. 네트워크에서 메시지가 손상된 경우를 제외하면 올바른 구현 간 통신에서는 이 경고가 절대로 나타나서는 안 된다.¶
서명을 올바르게 검증하지 못하거나 Finished 메시지 또는 PSK 바인더를 검증하지 못하는 경우를 포함하여, 핸드셰이크의 암호화 연산 (레코드 계층 연산 제외)이 실패했다.¶
서버가 클라이언트에서 지원하는 것보다 더 안전한 매개변수를 요구하여 협상이 실패한 경우 "handshake_failure" 대신 반환된다.¶
피어나 프로토콜의 정확성과 관련 없는 내부 오류 (예: 메모리 할당 실패) 때문에 계속 진행할 수 없다.¶
제공된 TLS 버전 또는 다른 협상된 매개변수에서 전송이 필수인 확장을 포함하지 않은 핸드셰이크 메시지를 수신한 엔드포인트가 전송한다.¶
대응하는 ClientHello 또는 CertificateRequest에서 먼저 제공하지 않은 확장을 ServerHello, HelloRetryRequest, EncryptedExtensions 또는 Certificate에 포함한 핸드셰이크 메시지를 수신한 엔드포인트가 전송한다.¶
클라이언트가 "server_name" 확장을 통해 제공한 이름으로 식별되는 서버가 존재하지 않을 때 서버가 전송한다 ([RFC6066] 참조).¶
서버가 "status_request" 확장을 통해 유효하지 않거나 수락할 수 없는 OCSP 응답을 제공한 경우 클라이언트가 전송한다 ([RFC6066] 참조).¶
PSK 키 설정을 원하지만 클라이언트가 허용 가능한 PSK 식별자를 제공하지 않은 경우 서버가 전송한다. 이 경고를 전송하는 것은 선택 사항이며, 서버는 유효하지 않은 PSK 식별자임만 나타내기 위해 "decrypt_error" 경고를 대신 전송하도록 선택할 수 있다.¶
클라이언트 인증서가 필요하지만 클라이언트가 인증서를 제공하지 않은 경우 서버가 전송한다.¶
더 구체적인 오류가 없거나 송신자가 구체적인 오류 코드를 숨기려는 경우 오류 조건을 나타내기 위해 전송한다. 구현은 가능한 경우 더 구체적인 오류를 사용하는 것이 권고된다.¶
클라이언트의 "application_layer_protocol_negotiation" 확장이 서버가 지원하지 않는 프로토콜만 알리는 경우 서버가 전송한다 ([RFC7301] 참조).¶
TLS 핸드셰이크는 아래에 자세히 설명된 대로 실제로 사용할 키 자료를 생성하기 위해 결합되는 하나 이상의 입력 비밀값을 설정한다. 키 유도 과정에는 입력 비밀값과 핸드셰이크 트랜스크립트가 모두 포함된다. 핸드셰이크 트랜스크립트에는 Hello 메시지의 무작위 값이 포함되므로 동일한 PSK를 여러 연결에 사용하는 경우처럼 동일한 입력 비밀값을 사용하더라도 각 핸드셰이크의 트래픽 비밀값은 서로 다르다는 점에 유의한다.¶
키 유도 과정에서는 HKDF [RFC5869]에 정의된 HKDF-Extract 및 HKDF-Expand 함수와 아래에 정의된 함수를 사용한다.¶
HKDF-Expand-Label(Secret, Label, Context, Length) =
HKDF-Expand(Secret, HkdfLabel, Length)
¶
여기에서 HkdfLabel은 다음과 같이 지정한다.¶
struct {
uint16 length = Length;
opaque label<7..255> = "tls13 " + Label;
opaque context<0..255> = Context;
} HkdfLabel;
Derive-Secret(Secret, Label, Messages) =
HKDF-Expand-Label(Secret, Label,
Transcript-Hash(Messages), Hash.length)
¶
Transcript-Hash와 HKDF에서 사용하는 Hash 함수는 암호 스위트의 해시 알고리즘이다. Hash.length는 해당 출력의 바이트 단위 길이이다. Messages는 핸드셰이크 메시지 유형과 길이 필드를 포함하되 레코드 계층 헤더는 포함하지 않은, 표시된 핸드셰이크 메시지를 이어 붙인 값이다. 일부 경우 길이가 0인 Context (""로 표시됨)가 HKDF-Expand-Label에 전달된다는 점에 유의한다. 이 문서에 지정된 레이블은 모두 ASCII 문자열이며 후행 NUL 바이트를 포함하지 않는다.¶
"HKDF-Expand-Label"을 사용하는 TLS 확장은 해당 확장을 함께 사용하는 TLS 버전에 연결된 HkdfLabel 정의를 사용한다. 이 명세와 함께 사용하는 경우 위에 정의된 HkdfLabel을 사용하며, DTLS [RFC9147]와 함께 사용하는 경우 [RFC9147], 섹션 5.9에 정의된 버전을 사용한다.¶
참고: 일반적인 해시 함수에서는 12자보다 긴 레이블을 계산하려면 해시 함수를 한 번 더 반복해야 한다. 이 명세의 모든 레이블은 이 제한 안에 들어가도록 선택되었다.¶
키는 HKDF-Extract 및 Derive-Secret 함수를 사용해 두 입력 비밀값에서 유도한다. 새 비밀값을 추가하는 일반적인 방식은 현재 비밀 상태를 Salt로, 새로 추가할 비밀값을 입력 키 자료(IKM)로 하여 HKDF-Extract를 사용하는 것이다. 이 TLS 1.3 버전에서 두 입력 비밀값은 다음과 같다.¶
이로부터 아래 다이어그램 (그림 5)에 표시된 키 스케줄이 생성된다. 이 다이어그램에는 다음 형식 규칙이 적용된다.¶
HKDF-Extract는 위쪽에서 Salt 인수를, 왼쪽에서 IKM 인수를 받고, 출력은 아래쪽에, 출력 이름은 오른쪽에 표시한다.¶
Derive-Secret의 Secret 인수는 들어오는 화살표로 표시한다. 예를 들어 Early Secret은 client_early_traffic_secret을 생성하는 Secret이다.¶
"0"은 값이 0으로 설정된 Hash.length바이트 문자열을 나타낸다.¶
참고: 값은 "main" 비밀값이라고 부르지만 키 유도 레이블에는 "master" 문자열을 사용한다. 이 불일치는 호환성을 유지하면서 값의 이름을 변경한 결과이다.¶
참고: 여기에는 별도의 AEAD 키와 IV 키 같은 모든 말단 키가 표시된 것이 아니라 핸드셰이크에서 유도된 첫 번째 비밀값 집합이 표시되어 있다.¶
0
|
v
PSK --> HKDF-Extract = 초기 비밀값
|
+-----> Derive-Secret(.,
| "ext binder" |
| "res binder",
| "")
| = binder_key
|
+-----> Derive-Secret(., "c e traffic",
| ClientHello)
| = client_early_traffic_secret
|
+-----> Derive-Secret(., "e exp master",
| ClientHello)
| = early_exporter_secret
v
Derive-Secret(., "derived", "")
|
비대칭 v
공유 비밀값 --> HKDF-Extract = 핸드셰이크 비밀값
|
+-----> Derive-Secret(., "c hs traffic",
| ClientHello...ServerHello)
| = client_handshake_traffic_secret
|
+-----> Derive-Secret(., "s hs traffic",
| ClientHello...ServerHello)
| = server_handshake_traffic_secret
v
Derive-Secret(., "derived", "")
|
v
0 --> HKDF-Extract = 주 비밀값
|
+-----> Derive-Secret(., "c ap traffic",
| ClientHello...서버 Finished)
| = client_application_traffic_secret_0
|
+-----> Derive-Secret(., "s ap traffic",
| ClientHello...서버 Finished)
| = server_application_traffic_secret_0
|
+-----> Derive-Secret(., "exp master",
| ClientHello...서버 Finished)
| = exporter_secret
|
+-----> Derive-Secret(., "res master",
ClientHello...클라이언트 Finished)
= resumption_secret
여기에서 일반적인 방식은 다이어그램 왼쪽에 표시된 비밀값은 콘텍스트가 없는 원시 엔트로피이고, 오른쪽에 표시된 비밀값은 핸드셰이크 콘텍스트를 포함하므로 추가 콘텍스트 없이 실제 키를 유도하는 데 사용할 수 있다는 것이다. 동일한 비밀값을 사용하더라도 Derive-Secret 호출마다 서로 다른 Messages 인수를 사용할 수 있다는 점에 유의한다. 0-RTT 교환에서 Derive-Secret은 서로 다른 네 개의 트랜스크립트로 호출되며, 1-RTT 전용 교환에서는 서로 다른 세 개의 트랜스크립트로 호출된다.¶
특정 비밀값을 사용할 수 없는 경우 값이 0으로 설정된 Hash.length바이트 문자열로 구성된 0 값을 사용한다. 이는 단계를 건너뛴다는 의미가 아니므로 PSK를 사용하지 않더라도 Early Secret은 여전히 HKDF-Extract(0, 0)이 된다. binder_key 계산에서 외부 PSK (TLS 외부에서 프로비저닝됨)에는 "ext binder" 레이블을, 재개 PSK(이전 핸드셰이크의 재개 비밀값으로 프로비저닝됨)에는 "res binder" 레이블을 사용한다. 서로 다른 레이블은 한 PSK 유형을 다른 유형으로 대체하는 것을 방지한다.¶
서버가 최종적으로 선택하는 PSK에 따라 가능한 Early Secret 값이 여러 개 있다. 클라이언트는 가능한 각 PSK에 대해 하나씩 계산해야 하며, PSK가 선택되지 않으면 0 PSK에 대응하는 Early Secret도 계산해야 한다.¶
특정 비밀값에서 유도할 모든 값을 계산한 후에는 해당 비밀값을 삭제하는 것이 권고된다.¶
핸드셰이크가 완료되면 어느 쪽이든 섹션 4.7.3에 정의된 KeyUpdate 핸드셰이크 메시지를 사용해 송신 트래픽 키를 갱신할 수 있다. 다음 세대의 트래픽 키는 이 섹션에 설명된 대로 client_/server_application_traffic_secret_N에서 client_/server_application_traffic_secret_N+1을 생성한 후, 섹션 7.3에 설명된 대로 트래픽 키를 다시 유도하여 계산한다.¶
다음 세대 application_traffic_secret은 다음과 같이 계산한다.¶
application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N,
"traffic upd", "", Hash.length)
¶
client_/server_application_traffic_secret_N+1과 연관된 트래픽 키를 계산한 후 구현은 client_/server_application_traffic_secret_N과 연관된 트래픽 키를 삭제하는 것이 권고된다.¶
트래픽 키 자료는 다음 입력 값에서 생성한다.¶
트래픽 키 자료는 입력 트래픽 비밀값에서 다음을 사용해 생성한다.¶
[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)¶
[sender]는 송신 측을 나타낸다. 각 데이터 범주의 Secret 값은 아래 표에 표시되어 있다.¶
| 데이터 유형 | 비밀값 |
|---|---|
| 0-RTT 애플리케이션 및 EndOfEarlyData | client_early_traffic_secret |
| 초기 핸드셰이크 | [sender]_handshake_traffic_secret |
| 핸드셰이크 후 및 애플리케이션 데이터 | [sender]_application_traffic_secret_N |
경고는 당시의 현재 송신 키 (그러한 키가 설정되지 않은 경우 평문)를 사용해 전송한다. 기반 Secret이 변경될 때마다 (예: 핸드셰이크 키에서 애플리케이션 데이터 키로 변경하거나 키를 갱신할 때) 모든 트래픽 키 자료를 다시 계산한다.¶
[RFC5705]는 TLS 의사 난수 함수(PRF)를 기준으로 TLS용 키 자료 익스포터를 정의한다. 이 문서는 PRF를 HKDF로 대체하므로 새로운 구성이 필요하다. 익스포터 인터페이스는 동일하게 유지된다.¶
익스포터 값은 다음과 같이 계산한다.¶
TLS-Exporter(label, context_value, key_length) =
HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
"exporter", Hash(context_value), key_length)
¶
여기에서 Secret은 early_exporter_secret 또는 exporter_secret이다. 애플리케이션이 명시적으로 지정하지 않는 한 구현은 exporter_secret을 반드시 사용해야 한다. early_exporter_secret은 0-RTT 데이터에 익스포터가 필요한 상황에서 사용하도록 정의되어 있다. 초기 익스포터에는 별도의 인터페이스를 사용하는 것이 권고된다. 이렇게 하면 익스포터 사용자가 일반 익스포터를 원할 때 실수로 초기 익스포터를 사용하거나 그 반대의 경우를 방지할 수 있다.¶
콘텍스트를 제공하지 않으면 context_value의 길이는 0이다. 따라서 콘텍스트를 제공하지 않은 경우와 빈 콘텍스트를 제공한 경우는 동일한 값을 계산한다. 이는 빈 콘텍스트가 콘텍스트가 없는 경우와 다른 출력을 생성했던 이전 TLS 버전에서 변경된 사항이다. 이 문서 발행 시점에는 할당된 어떤 익스포터 레이블도 콘텍스트가 있는 경우와 없는 경우 모두에 사용되지 않는다. 향후 명세는 동일한 레이블에서 빈 콘텍스트와 콘텍스트 없음이 모두 허용되는 익스포터 사용을 정의해서는 안 된다. 새로운 익스포터 사용은 값이 비어 있을 수 있더라도 모든 익스포터 계산에 콘텍스트를 제공하는 것이 권고된다.¶
섹션 2.3과 부록 F.5에서 언급한 것처럼, TLS는 0-RTT 데이터에 내재된 재전송 방지 기능을 제공하지 않는다. 고려해야 할 잠재적 위협은 두 가지이다.¶
0-RTT 데이터 플라이트를 단순히 복제하여 재전송 공격을 수행하는 네트워크 공격자.¶
클라이언트 재시도 동작을 이용해 서버가 애플리케이션 메시지의 여러 복사본을 수신하도록 만드는 네트워크 공격자. 견고성을 중시하는 클라이언트는 네트워크 오류에 응답하여 요청을 재시도하므로 이 위협은 이미 어느 정도 존재하지만, 클라이언트가 핸드셰이크 완료 후 거부된 0-RTT 데이터를 자동으로 재전송하면 0-RTT에서 추가적인 문제가 발생한다. 공격자는 데이터가 연결 1에서는 0-RTT 데이터로, 연결 2에서는 1-RTT 데이터로 처리되도록 만들 수 있기 때문이다.¶
첫 번째 공격 유형은 0-RTT 데이터가 최대 한 번만 수락되도록 상태를 공유하여 방지할 수 있다. 서버는 이 섹션에 설명된 방법 중 하나 또는 동등한 수단을 구현하여 해당 수준의 재전송 안전성을 제공하는 것이 권고된다. 그러나 운영상 문제로 모든 배포가 해당 수준의 상태를 유지하지는 못한다는 점을 인정한다. 따라서 정상적인 동작에서 클라이언트는 서버가 이러한 메커니즘 중 어떤 것을 실제로 구현하는지 알 수 없으므로, 재전송되어도 안전하다고 판단한 초기 데이터만 반드시 전송해야 한다.¶
재전송의 직접적인 영향 외에도 일반적으로 멱등하다고 여겨지는 작업조차 대량의 재전송으로 악용할 수 있는 공격 유형이 있다 (타이밍 공격, 리소스 제한 고갈 등이며 부록 F.5에 설명되어 있음). 모든 0-RTT 페이로드가 제한된 횟수만 재전송될 수 있도록 보장하여 이를 완화할 수 있다. 서버는 관련 서비스 인프라 안의 모든 인스턴스 (머신, 스레드 또는 기타 엔터티)가 동일한 0-RTT 핸드셰이크에 대해 0-RTT를 최대 한 번만 수락하도록 반드시 보장해야 한다. 이는 재전송 횟수를 배포에 있는 서버 인스턴스 수로 제한한다. 이러한 보장은 최근 수신한 ClientHello의 데이터를 로컬에 기록하고 반복을 거부하거나, 동일하거나 더 강한 보장을 제공하는 다른 방법으로 달성할 수 있다. "서버 인스턴스당 최대 한 번"이라는 보장은 최소 요구사항이며, 가능한 경우 서버는 0-RTT 재전송을 더 제한하는 것이 권고된다.¶
두 번째 공격 유형은 TLS 계층에서 방지할 수 없으며 모든 애플리케이션에서 반드시 처리해야 한다 (예를 들어 HTTP에서 0-RTT 데이터를 사용하는 방법에 대한 지침을 제공하는 [RFC8470] 참조). 어떤 형태로든 재시도 동작을 구현하는 클라이언트를 가진 애플리케이션은 서로 다른 TLS 연결에 도착하는 중복 애플리케이션 계층 요청을 올바르게 처리하는 것을 포함하여 이미 어떤 형태의 재전송 방지 기능을 구현해야 한다는 점에 유의한다.¶
가장 단순한 재전송 방지 방식은 서버가 각 세션 티켓을 한 번만 사용하도록 허용하는 것이다. 예를 들어 서버는 아직 사용되지 않은 모든 유효한 티켓의 데이터베이스를 유지하고 각 티켓이 사용되면 데이터베이스에서 삭제할 수 있다. 알 수 없는 티켓이 제공되면 서버는 전체 핸드셰이크로 대체한다.¶
티켓이 자체 포함형이 아니라 데이터베이스 키이고 대응하는 PSK가 사용 시 삭제되는 경우, PSK를 사용해 설정된 연결은 재전송 방지뿐 아니라 데이터베이스 항목에서 PSK의 모든 복사본이 삭제된 후 순방향 비밀성도 얻는다. 이 메커니즘은 비대칭 키 교환 없이 PSK를 사용할 때의 PSK 사용 보안도 향상한다.¶
이 메커니즘은 여러 분산 서버가 있는 환경에서 서버 노드 간 세션 데이터베이스를 공유해야 하므로, 자체 암호화 티켓과 비교하면 성공적인 PSK 0-RTT 연결 비율을 높이기 어려울 수 있다. 세션 데이터베이스와 달리 세션 티켓은 일관된 저장소가 없어도 PSK 기반 세션 설정을 성공적으로 수행할 수 있지만, 0-RTT를 허용하는 경우에는 다음 섹션에 설명된 대로 0-RTT 데이터의 재전송 방지를 위해 여전히 일관된 저장소가 필요하다.¶
다른 재전송 방지 방식은 ClientHello에서 유도된 고유한 값 (일반적으로 random 값 또는 PSK 바인더)을 기록하고 중복을 거부하는 것이다. 모든 ClientHello를 기록하면 상태가 무한히 증가하지만, 서버는 대신 특정 시간 창 안의 ClientHello만 기록하고 "obfuscated_ticket_age"를 사용하여 티켓이 해당 시간 창 밖에서 재사용되지 않도록 보장할 수 있다.¶
이를 구현하려면 서버는 ClientHello를 수신할 때 먼저 섹션 4.3.11에 설명된 대로 PSK 바인더를 검증한다. 그런 다음 다음 섹션에 설명된 대로 expected_arrival_time을 계산하고 기록 시간 창 밖에 있으면 0-RTT를 거부하여 1-RTT 핸드셰이크로 대체한다.¶
expected_arrival_time이 시간 창 안에 있으면 서버는 일치하는 ClientHello를 기록했는지 확인한다. 일치하는 항목을 찾으면 "illegal_parameter" 경고와 함께 핸드셰이크를 중단하거나 PSK는 수락하되 0-RTT를 거부한다. 일치하는 ClientHello를 찾지 못하면 0-RTT를 수락하고 expected_arrival_time이 시간 창 안에 있는 동안 ClientHello를 저장한다. 서버는 Bloom 필터처럼 오탐이 발생할 수 있는 데이터 저장소를 구현할 수도 있으며, 이 경우 명백한 재전송에 대해 0-RTT를 반드시 거부해야 하지만 핸드셰이크를 중단해서는 안 된다.¶
서버는 ClientHello에서 검증된 부분만 사용해 저장소 키를 반드시 유도해야 한다. ClientHello에 여러 PSK 식별자가 포함되어 있으면 공격자는 서버가 선호도가 낮은 식별자의 바인더를 검증하지 않을 것이라는 가정하에 해당 식별자의 바인더 값을 다르게 한 여러 ClientHello를 생성할 수 있다 (섹션 4.3.11에서 권고한 방식). 즉, 클라이언트가 PSK A와 B를 전송하지만 서버가 A를 선호하는 경우 공격자는 A의 바인더에 영향을 주지 않고 B의 바인더를 변경할 수 있다. B의 바인더가 저장소 키의 일부라면 이 ClientHello는 중복으로 나타나지 않으므로 수락되며, 재전송 캐시 오염 같은 부작용이 발생할 수 있다. 다만 모든 0-RTT 데이터는 다른 키를 사용하므로 복호화할 수 없다. 검증된 바인더 또는 ClientHello.random을 저장소 키로 사용하면 이 공격은 불가능하다.¶
이 메커니즘은 아직 사용되지 않은 모든 티켓을 저장할 필요가 없으므로 재개 및 0-RTT 비율이 높은 분산 시스템에서 구현하기 더 쉬울 수 있지만, 수신한 ClientHello 메시지를 신뢰성 있게 저장하고 가져오기 어렵기 때문에 재전송 방지 기능이 잠재적으로 더 약할 수 있다. 이러한 시스템 중 다수에서는 수신한 모든 ClientHello를 전역적으로 일관되게 저장하는 것이 비현실적이다. 이 경우 가장 강력한 재전송 방지는 특정 티켓에 대해 하나의 저장소 영역만 권한을 갖게 하고 다른 모든 영역에서는 해당 티켓의 0-RTT를 거부하는 방식이다. 이 방식은 하나의 영역만 0-RTT 데이터를 수락하므로 공격자의 단순 재전송을 방지한다. 더 약한 설계는 각 영역에 별도의 저장소를 구현하면서 모든 영역에서 0-RTT를 허용하는 것이다. 이 방식은 재전송 횟수를 영역당 한 번으로 제한한다. 어느 설계에서도 애플리케이션 메시지 중복은 여전히 가능하다.¶
구현을 새로 시작한 경우 기록 시간 창의 어떤 부분이라도 시작 시점과 겹치는 동안에는 0-RTT를 거부하는 것이 권고된다. 그렇지 않으면 해당 기간에 원래 전송된 재전송을 수락할 위험이 있다.¶
참고: 클라이언트의 시계가 서버 시계보다 훨씬 빠르면 미래의 시간 창 밖에 있는 ClientHello를 수신할 수 있다. 이 경우 처음에는 1-RTT로 수락되어 클라이언트 재시도를 유발하고, 나중에는 0-RTT로 수락될 수 있다. 이는 섹션 8에 설명된 두 번째 공격 유형의 또 다른 변형이다.¶
ClientHello는 클라이언트가 이를 전송한 시간을 나타내므로 ClientHello가 비교적 최근에 전송되었을 가능성을 효율적으로 판단하고, 그러한 ClientHello에 대해서만 0-RTT를 수락하며 그 밖의 경우에는 1-RTT 핸드셰이크로 대체할 수 있다. 이는 섹션 8.2에 설명된 ClientHello 저장 메커니즘에 필요하다. 그렇지 않으면 서버가 무제한의 ClientHello를 저장해야 하기 때문이다. 또한 0-RTT에 사용할 수 없는 ClientHello를 효율적으로 거부할 수 있으므로 자체 포함형 일회용 티켓에도 유용한 최적화이다.¶
이 메커니즘을 구현하려면 서버는 서버가 세션 티켓을 생성한 시간에 클라이언트와 서버 간 예상 왕복 시간을 더한 값을 저장해야 한다. 즉,¶
adjusted_creation_time = creation_time + estimated_RTT
¶
이 값을 티켓에 인코딩하면 아직 사용되지 않은 각 티켓의 상태를 유지할 필요가 없다. 서버는 클라이언트의 "pre_shared_key" 확장에 있는 "obfuscated_ticket_age" 매개변수에서 티켓의 "ticket_age_add" 값을 빼서 클라이언트가 보는 티켓 수명을 판단할 수 있다. 서버는 ClientHello의 expected_arrival_time을 다음과 같이 판단할 수 있다.¶
expected_arrival_time = adjusted_creation_time + clients_ticket_age¶
새 ClientHello를 수신하면 expected_arrival_time을 현재 서버 벽시계 시간과 비교하고, 둘의 차이가 일정량을 초과하면 0-RTT를 거부하지만 1-RTT 핸드셰이크는 완료하도록 허용할 수 있다.¶
expected_arrival_time과 측정된 시간이 일치하지 않게 만드는 잠재적인 오류 원인은 여러 가지가 있다. 클라이언트와 서버 시계 속도의 차이는 최소일 가능성이 높지만, 절대 시간은 크게 어긋날 수 있다. 경과 시간의 정상적인 값이 일치하지 않는 가장 가능성 높은 원인은 네트워크 전파 지연이다. NewSessionTicket과 ClientHello 메시지는 재전송되어 지연될 수 있으며, 이 지연은 TCP에 의해 숨겨질 수 있다. 인터넷상의 클라이언트에서는 시계 오류와 측정 편차를 고려하기 위해 대략 10초 규모의 시간 창이 필요함을 의미한다. 다른 배포 환경에서는 요구사항이 다를 수 있다. 시계 편차 분포는 대칭이 아니므로 최적의 절충안은 허용 가능한 불일치 값의 비대칭 범위를 사용할 수 있다.¶
최신성 검사만으로는 오류 시간 창 안의 재전송을 탐지하지 못하므로 재전송을 방지하기에 충분하지 않다는 점에 유의한다. 이 시간 창에는 대역폭과 시스템 용량에 따라 실제 환경에서 수십억 회의 재전송이 포함될 수 있다. 또한 이 최신성 검사는 ClientHello를 수신할 때만 수행되며 이후의 초기 애플리케이션 데이터 레코드를 수신할 때는 수행되지 않는다. 초기 데이터를 수락한 후 레코드는 더 긴 시간 동안 서버로 계속 스트리밍될 수 있다.¶
달리 명세하는 애플리케이션 프로필 표준이 없는 경우:¶
TLS 준수 애플리케이션은 TLS_AES_128_GCM_SHA256 [GCM] 암호 스위트를 반드시 구현해야 하며, TLS_AES_256_GCM_SHA384 [GCM] 및 TLS_CHACHA20_POLY1305_SHA256 [RFC8439] 암호 스위트를 구현하는 것이 권고된다 (부록 B.4 참조).¶
TLS 준수 애플리케이션은 rsa_pkcs1_sha256(인증서용), rsa_pss_rsae_sha256(CertificateVerify 및 인증서용), ecdsa_secp256r1_sha256을 사용하는 디지털 서명을 반드시 지원해야 한다. TLS 준수 애플리케이션은 secp256r1(NIST P-256)을 사용한 키 교환을 반드시 지원해야 하며, X25519 [RFC7748]를 사용한 키 교환을 지원하는 것이 권고된다.¶
달리 명세하는 애플리케이션 프로필 표준이 없는 경우, TLS 준수 애플리케이션은 다음 TLS 확장을 반드시 구현해야 한다.¶
모든 구현은 해당 기능을 제공할 때 이러한 확장을 반드시 전송하고 사용해야 한다.¶
"supported_versions"는 모든 ClientHello, ServerHello 및 HelloRetryRequest 메시지에 필수이다.¶
"signature_algorithms"는 인증서 인증에 필수이다.¶
"supported_groups"는 DHE 또는 ECDHE 키 교환을 사용하는 ClientHello 메시지에 필수이다.¶
"key_share"는 DHE 또는 ECDHE 키 교환에 필수이다.¶
"pre_shared_key"는 PSK 키 합의에 필수이다.¶
"psk_key_exchange_modes"는 PSK 키 합의에 필수이다.¶
ClientHello에 본문에 0x0304가 포함된 "supported_versions" 확장이 있으면 클라이언트가 이 명세를 사용해 협상을 시도하는 것으로 간주한다. 이러한 ClientHello 메시지는 다음 요구사항을 반드시 충족해야 한다.¶
"pre_shared_key" 확장을 포함하지 않는 경우 "signature_algorithms" 확장과 "supported_groups" 확장을 모두 반드시 포함해야 한다.¶
"supported_groups" 확장을 포함하는 경우 "key_share" 확장도 반드시 포함해야 하며, 그 반대도 마찬가지이다. 빈 KeyShare.client_shares 목록은 허용된다.¶
이러한 요구사항을 준수하지 않는 ClientHello를 수신한 서버는 "missing_extension" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
또한 모든 구현은 이를 사용할 수 있는 애플리케이션과 함께 "server_name" 확장의 사용을 반드시 지원해야 한다. 서버는 클라이언트가 유효한 "server_name" 확장을 전송하도록 요구할 수 있다. 이 확장을 요구하는 서버는 "server_name" 확장이 없는 ClientHello에 대해 "missing_extension" 경고와 함께 연결을 종료하여 응답하는 것이 권고된다.¶
이 섹션에서는 TLS 엔드포인트와 미들박스가 반드시 따라야 하는 불변 조건을 설명한다. 이는 이전 TLS 버전에도 적용된다.¶
TLS는 안전하고 호환성 있게 확장할 수 있도록 설계되었다. 최신 클라이언트나 서버가 최신 피어와 통신할 때는 공통으로 지원하는 매개변수 중 가장 선호하는 것을 협상해야 한다. TLS 핸드셰이크는 다운그레이드 보호를 제공한다. TLS를 종료하지 않고 최신 클라이언트와 최신 서버 사이의 트래픽을 전달하는 미들박스는 핸드셰이크에 영향을 줄 수 없어야 한다 (부록 F.1 참조). 동시에 배포마다 갱신 속도가 다르므로 최신 클라이언트나 서버는 이전 엔드포인트와 상호 운용할 수 있도록 이전 매개변수를 계속 지원할 수 있다.¶
이를 위해 구현은 확장 가능한 필드를 올바르게 반드시 처리해야 한다.¶
ClientHello를 전송하는 클라이언트는 그 안에서 알린 모든 비GREASE [RFC8701] 매개변수를 반드시 지원해야 한다. 그렇지 않으면 서버가 해당 매개변수 중 하나를 선택하여 상호 운용에 실패할 수 있다.¶
ClientHello를 수신한 서버는 인식하지 못하는 모든 암호 스위트, 확장 및 기타 매개변수를 올바르게 반드시 무시해야 한다. 그렇지 않으면 최신 클라이언트와 상호 운용하지 못할 수 있다. TLS 1.3에서 CertificateRequest 또는 NewSessionTicket을 수신한 클라이언트도 인식하지 못하는 모든 확장을 반드시 무시해야 한다.¶
TLS 연결을 종료하는 미들박스는 원래 클라이언트에 대해서는 클라이언트가 수락할 의향이 있는 인증서를 갖는 것을 포함해 준수하는 TLS 서버처럼 반드시 동작해야 하며, 원래 서버에 대해서는 원래 서버의 인증서를 검증하는 것을 포함해 준수하는 TLS 클라이언트처럼도 동작해야 한다. 특히 자신이 이해하는 매개변수만 포함하는 자체 ClientHello를 반드시 생성해야 하며, 엔드포인트의 값을 전달하는 대신 새로운 ServerHello random 값을 반드시 생성해야 한다.¶
TLS의 프로토콜 요구사항과 보안 분석은 두 연결에 각각 별도로만 적용된다는 점에 유의한다. TLS 종료 장치를 안전하게 배포하려면 이 문서의 범위를 벗어나는 추가 보안 고려사항이 필요하다.¶
이해하지 못하는 ClientHello 매개변수를 전달하는 미들박스는 해당 ClientHello 뒤의 어떤 메시지도 처리해서는 안 된다. 이후의 모든 트래픽을 수정 없이 반드시 전달해야 한다. 그렇지 않으면 최신 클라이언트 및 서버와 상호 운용하지 못할 수 있다.¶
전달된 ClientHello에는 미들박스가 지원하지 않는 기능에 대한 표시가 포함될 수 있으므로 응답에는 미들박스가 인식하지 못하는 향후 TLS 추가 사항이 포함될 수 있다. 이러한 추가 사항은 ClientHello 뒤의 모든 메시지를 임의로 변경할 수 있다. 특히 ServerHello에서 전송되는 값, ServerHello 형식 및 TLSCiphertext 형식이 변경될 수 있다.¶
TLS 1.3의 설계는 널리 배포된 비준수 TLS 미들박스의 제약을 받았지만 (부록 E.4 참조), 불변 조건을 완화하지는 않는다. 이러한 미들박스는 계속 비준수 상태이다.¶
이 문서는 원래 [RFC4346]에서 생성되고 [RFC8446] 및 [RFC8447]에서 갱신된 여러 레지스트리를 사용한다. [RFC8446], [RFC8447] 및 이 문서 사이의 변경 사항은 섹션 11.1에 설명되어 있다. IANA는 이러한 RFC에 대한 참조를 이 문서에 대한 참조로 대체했다. 레지스트리와 해당 할당 정책은 다음과 같다.¶
TLS Cipher Suites 레지스트리: 첫 번째 바이트가 0-254(십진수) 범위인 값은 명세 필요 [RFC8126] 절차를 통해 할당한다. 첫 번째 바이트가 255(십진수)인 값은 사적 사용 [RFC8126]을 위해 예약되어 있다.¶
IANA는 부록 B.4에 나열된 암호 스위트를 레지스트리에 추가했다. "Value" 및 "Description" 열은 해당 표에서 가져온다. 이러한 암호 스위트의 "DTLS-OK" 및 "Recommended" 열은 모두 "Y"로 표시된다.¶
TLS Alerts 레지스트리: 향후 값은 표준 조치 [RFC8126]를 통해 할당한다. IANA는 부록 B.2의 값으로 이 레지스트리를 채웠다. 이러한 모든 값의 "DTLS-OK" 열은 "Y"로 표시된다. "_RESERVED"로 표시된 값에는 이전 사용법을 설명하는 주석이 있다.¶
TLS HandshakeType 레지스트리: 향후 값은 표준 조치 [RFC8126]를 통해 할당한다. IANA는 항목 4의 이름을 "NewSessionTicket"에서 "new_session_ticket"으로 변경하고 부록 B.3의 값으로 이 레지스트리를 채웠다. 이러한 모든 값의 "DTLS-OK" 열은 "Y"로 표시된다. "_RESERVED"로 표시된 값에는 이전 또는 임시 사용법을 설명하는 주석이 있다.¶
이 문서는 원래 [RFC4366]에서 생성된 TLS ExtensionType Values 레지스트리도 사용한다. IANA는 이 문서를 참조하도록 이를 갱신했다. 레지스트리의 변경 사항은 다음과 같다.¶
IANA는 등록 정책을 다음과 같이 갱신했다.¶
첫 번째 바이트가 0-254(십진수) 범위인 값은 명세 필요 [RFC8126] 절차를 통해 할당한다. 첫 번째 바이트가 255(십진수)인 값은 사적 사용 [RFC8126]을 위해 예약되어 있다.¶
IANA는 이 문서에 정의된 값과 "Recommended" 값 "Y"를 사용하여 "key_share", "pre_shared_key", "psk_key_exchange_modes", "early_data", "cookie", "supported_versions", "certificate_authorities", "oid_filters", "post_handshake_auth" 및 "signature_algorithms_cert" 확장을 포함하도록 이 레지스트리를 갱신했다.¶
IANA는 확장이 나타날 수 있는 메시지를 나열하는 "TLS 1.3" 열을 포함하도록 이 레지스트리를 갱신했다. 이 열은 처음에 섹션 4.3의 표를 사용해 채웠으며, 그 표에 나열되지 않은 모든 확장은 TLS 1.3에서 사용되지 않음을 나타내기 위해 "-"로 표시했다.¶
이 문서는 원래 [RFC6091]에서 생성되고 [RFC8447]에서 갱신된 TLS Certificate Types 레지스트리의 두 항목을 갱신한다. IANA는 값 1의 항목을 이름 "OpenPGP_RESERVED", "Recommended" 값 "N", 주석 "TLS 1.3 이전 버전에서 사용됨."으로 갱신했다. IANA는 값 0의 항목을 이름 "X509", "Recommended" 값 "Y", 주석 "TLS 1.3 이전에는 X.509였음"으로 갱신했다.¶
이 문서는 원래 [RFC6961]에서 생성된 TLS Certificate Status Types 레지스트리의 항목 하나를 갱신한다. IANA는 값 2의 항목을 이름 "ocsp_multi_RESERVED", 주석 "TLS 1.3 이전 버전에서 사용됨"으로 갱신했다.¶
이 문서는 TLS Supported Groups 레지스트리의 두 항목을 갱신한다(이 레지스트리는 [RFC4492]에서 다른 이름으로 생성되었고, 현재 [RFC8422]에서 관리되며 [RFC7919] 및 [RFC8447]에서 갱신됨). 값 29와 30(x25519 및 x448)의 항목은 이 문서도 참조하도록 갱신되었다.¶
또한 이 문서는 IANA가 관리하는 두 개의 새로운 레지스트리를 정의한다.¶
TLS SignatureScheme 레지스트리: 첫 번째 바이트가 0-253(십진수) 범위인 값은 명세 필요 [RFC8126] 절차를 통해 할당한다. 첫 번째 바이트가 254 또는 255(십진수)인 값은 사적 사용 [RFC8126]을 위해 예약되어 있다. 첫 번째 바이트가 0-6 범위이거나 두 번째 바이트가 0-3 범위이면서 현재 할당되지 않은 값은 하위 호환성을 위해 예약되어 있다. 이 레지스트리에는 "Recommended" 열이 있다. 이 레지스트리는 처음에 섹션 4.3.3에 설명된 값으로 채워졌다. 다음 값은 "Recommended"로 표시된다: ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512 및 ed25519.¶
TLS PskKeyExchangeMode 레지스트리: 0-253 (십진수) 범위의 값은 명세 필요 [RFC8126] 절차를 통해 할당한다. 값 254와 255(십진수)는 사적 사용 [RFC8126]을 위해 예약되어 있다. 이 레지스트리에는 "Recommended" 열이 있다. 이 레지스트리는 처음에 psk_ke(0)와 psk_dhe_ke(1)로 채워졌다. 둘 다 "Recommended"로 표시된다.¶
TLS ExtensionType Values, TLS Cipher Suites, TLS Supported Groups, TLS Exporter Labels, TLS HashAlgorithm, TLS Certificate Types, TLSPskKeyExchangeMode 및 TLS SignatureScheme 레지스트리에는 "Recommended" 열이 있다. "Recommended" 열의 값과 설정에 관한 추가 정보는 [RFC9847]의 섹션 3을 참조한다.¶
IANA는 IANA 레지스트리의 [RFC8446]에 대한 모든 참조를 이 문서에 대한 참조로 갱신했다.¶
IANA는 TLS SignatureScheme 및 PskKeyExchangeMode 레지스트리의 "Recommended" 열 절차를 [RFC9847]과 일치하도록 갱신했다.¶
IANA는 TLS ExtensionType Values 레지스트리의 "extended_master_secret" 값을 "extended_main_secret"으로 이름을 변경했다.¶
IANA는 섹션 6에 제시된 값을 사용하여 TLS Alerts 레지스트리에 "general_error" 경고 값을 생성했다.¶
IANA는 TLS ExtensionType Values 레지스트리의 status_request 및 supported_groups 항목에 이 문서에 대한 참조를 추가했다.¶
IANA는 cached_info에 대한 "TLS 1.3" 열의 TLS ExtensionType 값을 "CH, EE"로 갱신했다.¶
이 부록은 클라이언트 및 서버 핸드셰이크의 적법한 상태 전이를 요약한다. 상태 이름(모두 대문자, 예: START)은 형식적인 의미가 없으며 이해하기 쉽도록 제공된다. 특정 상황에서만 수행되는 동작은 []로 표시한다. "K_{send,recv} = foo" 표기법은 "송신/수신 키를 지정된 키로 설정한다"라는 의미이다.¶
시작 <----+
ClientHello 전송 | | HelloRetryRequest 수신
[K_send = 초기 데이터] | |
v |
+-> WAIT_SH ----+
| | ServerHello 수신
| | K_recv = 핸드셰이크
초기 | V
데이터 | WAIT_EE
전송 | | EncryptedExtensions 수신
가능 | +---------+-------+
| PSK | | 인증서 사용
| 사용 | v
| | WAIT_CERT_CR
| | 수신 | | CertificateRequest 수신
| | Certificate | v
| | | WAIT_CERT
| | | | Certificate 수신
| | v v
| | WAIT_CV
| | | CertificateVerify 수신
| +-> WAIT_FINISHED <-+
| | Finished 수신
+-> | [EndOfEarlyData 전송]
| K_send = 핸드셰이크
| [Certificate [+ CertificateVerify] 전송]
여기부터 | Finished 전송
애플리케이션 데이터 --> | K_send = K_recv = 애플리케이션
전송 가능 v
연결됨
¶
위에 표시된 전이에서는 클라이언트가 ServerHello 이후 메시지에서 발생한 경고를 평문 또는 초기 데이터 키로 전송할 수 있다는 점에 유의한다. 클라이언트가 이러한 경고를 전송해야 하는 경우 가능하면 먼저 핸드셰이크 키로 키를 다시 설정하는 것이 권고된다.¶
시작 <-----+
ClientHello 수신 | | HelloRetryRequest 전송
v |
RECVD_CH ----+
| 매개변수 선택
v
협상됨
| ServerHello 전송
| K_send = 핸드셰이크
| EncryptedExtensions 전송
| [CertificateRequest 전송]
여기부터 | [Certificate + CertificateVerify 전송]
애플리케이션 데이터 | Finished 전송
전송 가능 --> | K_send = 애플리케이션
+--------+--------+
0-RTT 없음 | | 0-RTT
| |
K_recv = 핸드셰이크 | | K_recv = 초기 데이터
[복호화 오류 건너뜀] | +------> WAIT_EOED --+
| | 수신 | | EndOfEarlyData 수신
| | 초기 데이터 | | K_recv = 핸드셰이크
| +------------+ |
| |
+-> WAIT_FLIGHT2 <--------+
|
+--------+--------+
인증 없음 | | 인증서 기반 클라이언트 인증
| |
| v
| WAIT_CERT
| 수신 | | Certificate 수신
| 빈 | v
| Certificate | WAIT_CV
| | | 수신
| v | CertificateVerify
+-> WAIT_FINISHED <---+
| Finished 수신
| K_recv = 애플리케이션
v
연결됨
¶
이 부록은 규범적 프로토콜 형식과 상수의 정의를 제공한다. "_RESERVED"로 나열된 값은 이전 TLS 버전에서 사용되었으며 완전성을 위해 여기에 나열한다. TLS 1.3 구현은 이를 전송해서는 안 되지만 이전 TLS 구현에서 수신할 수 있다.¶
enum {
invalid(0),
change_cipher_spec(20),
alert(21),
handshake(22),
application_data(23),
(255)
} ContentType;
struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
struct {
opaque content[TLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} TLSInnerPlaintext;
struct {
ContentType opaque_type = application_data; /* 23 */
ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
uint16 length;
opaque encrypted_record[TLSCiphertext.length];
} TLSCiphertext;
¶
enum { warning(1), fatal(2), (255) } AlertLevel;
enum {
close_notify(0),
unexpected_message(10),
bad_record_mac(20),
decryption_failed_RESERVED(21),
record_overflow(22),
decompression_failure_RESERVED(30),
handshake_failure(40),
no_certificate_RESERVED(41),
bad_certificate(42),
unsupported_certificate(43),
certificate_revoked(44),
certificate_expired(45),
certificate_unknown(46),
illegal_parameter(47),
unknown_ca(48),
access_denied(49),
decode_error(50),
decrypt_error(51),
export_restriction_RESERVED(60),
protocol_version(70),
insufficient_security(71),
internal_error(80),
inappropriate_fallback(86),
user_canceled(90),
no_renegotiation_RESERVED(100),
missing_extension(109),
unsupported_extension(110),
certificate_unobtainable_RESERVED(111),
unrecognized_name(112),
bad_certificate_status_response(113),
bad_certificate_hash_value_RESERVED(114),
unknown_psk_identity(115),
certificate_required(116),
general_error(117),
no_application_protocol(120),
(255)
} AlertDescription;
struct {
AlertLevel level;
AlertDescription description;
} Alert;
¶
enum {
hello_request_RESERVED(0),
client_hello(1),
server_hello(2),
hello_verify_request_RESERVED(3),
new_session_ticket(4),
end_of_early_data(5),
hello_retry_request_RESERVED(6),
encrypted_extensions(8),
certificate(11),
server_key_exchange_RESERVED(12),
certificate_request(13),
server_hello_done_RESERVED(14),
certificate_verify(15),
client_key_exchange_RESERVED(16),
finished(20),
certificate_url_RESERVED(21),
certificate_status_RESERVED(22),
supplemental_data_RESERVED(23),
key_update(24),
message_hash(254),
(255)
} HandshakeType;
struct {
HandshakeType msg_type; /* 핸드셰이크 유형 */
uint24 length; /* 메시지에 남은 바이트 */
select (Handshake.msg_type) {
case client_hello: ClientHello;
case server_hello: ServerHello;
case end_of_early_data: EndOfEarlyData;
case encrypted_extensions: EncryptedExtensions;
case certificate_request: CertificateRequest;
case certificate: Certificate;
case certificate_verify: CertificateVerify;
case finished: Finished;
case new_session_ticket: NewSessionTicket;
case key_update: KeyUpdate;
};
} Handshake;
¶
uint16 ProtocolVersion;
opaque Random[32];
uint8 CipherSuite[2]; /* 암호 스위트 선택자 */
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id<0..32>;
CipherSuite cipher_suites<2..2^16-2>;
opaque legacy_compression_methods<1..2^8-1>;
Extension extensions<7..2^16-1>;
} ClientHello;
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id_echo<0..32>;
CipherSuite cipher_suite;
uint8 legacy_compression_method = 0;
Extension extensions<6..2^16-1>;
} ServerHello;
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
server_name(0), /* RFC 6066, 9261 */
status_request(5), /* RFC 6066, 9846 */
supported_groups(10), /* RFC 7919, 9846 */
signature_algorithms(13), /* RFC 9846 */
use_srtp(14), /* RFC 5764 */
heartbeat(15), /* RFC 6520 */
application_layer_protocol_negotiation(16), /* RFC 7301 */
client_certificate_type(19), /* RFC 7250 */
server_certificate_type(20), /* RFC 7250 */
padding(21), /* RFC 7685 */
compress_certificate(27), /* RFC 8879 */
record_size_limit(28), /* RFC 8449 */
delegated_credential(34), /* RFC 9345 */
supported_ekt_ciphers(39), /* RFC 8870 */
pre_shared_key(41), /* RFC 9846 */
early_data(42), /* RFC 9846 */
supported_versions(43), /* RFC 9846 */
cookie(44), /* RFC 9846 */
psk_key_exchange_modes(45), /* RFC 9846 */
certificate_authorities(47), /* RFC 9846 */
oid_filters(48), /* RFC 9846 */
post_handshake_auth(49), /* RFC 9846 */
signature_algorithms_cert(50), /* RFC 9846 */
key_share(51), /* RFC 9846 */
transparency_info(52), /* RFC 9162 */
external_id_hash(55), /* RFC 8844 */
external_session_id(56), /* RFC 8844 */
quic_transport_parameters(57), /* RFC 9001 */
ticket_request(58), /* RFC 9149 */
ech_outer_extensions(64768), /* RFC 9849 */
encrypted_client_hello(65037), /* RFC 9849 */
(65535)
} ExtensionType;
struct {
NamedGroup group;
opaque key_exchange<1..2^16-1>;
} KeyShareEntry;
struct {
KeyShareEntry client_shares<0..2^16-1>;
} KeyShareClientHello;
struct {
NamedGroup selected_group;
} KeyShareHelloRetryRequest;
struct {
KeyShareEntry server_share;
} KeyShareServerHello;
struct {
uint8 legacy_form = 4;
opaque X[coordinate_length];
opaque Y[coordinate_length];
} UncompressedPointRepresentation;
enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
struct {
PskKeyExchangeMode ke_modes<1..255>;
} PskKeyExchangeModes;
struct {} Empty;
struct {
select (Handshake.msg_type) {
case new_session_ticket: uint32 max_early_data_size;
case client_hello: Empty;
case encrypted_extensions: Empty;
};
} EarlyDataIndication;
struct {
opaque identity<1..2^16-1>;
uint32 obfuscated_ticket_age;
} PskIdentity;
opaque PskBinderEntry<32..255>;
struct {
PskIdentity identities<7..2^16-1>;
PskBinderEntry binders<33..2^16-1>;
} OfferedPsks;
struct {
select (Handshake.msg_type) {
case client_hello: OfferedPsks;
case server_hello: uint16 selected_identity;
};
} PreSharedKeyExtension;
¶
struct {
select (Handshake.msg_type) {
case client_hello:
ProtocolVersion versions<2..254>;
case server_hello: /* 및 HelloRetryRequest */
ProtocolVersion selected_version;
};
} SupportedVersions;
¶
enum {
/* RSASSA-PKCS1-v1_5 알고리즘 */
rsa_pkcs1_sha256(0x0401),
rsa_pkcs1_sha384(0x0501),
rsa_pkcs1_sha512(0x0601),
/* ECDSA 알고리즘 */
ecdsa_secp256r1_sha256(0x0403),
ecdsa_secp384r1_sha384(0x0503),
ecdsa_secp521r1_sha512(0x0603),
/* 공개 키 OID가 rsaEncryption인 RSASSA-PSS 알고리즘 */
rsa_pss_rsae_sha256(0x0804),
rsa_pss_rsae_sha384(0x0805),
rsa_pss_rsae_sha512(0x0806),
/* EdDSA 알고리즘 */
ed25519(0x0807),
ed448(0x0808),
/* 공개 키 OID가 RSASSA-PSS인 RSASSA-PSS 알고리즘 */
rsa_pss_pss_sha256(0x0809),
rsa_pss_pss_sha384(0x080a),
rsa_pss_pss_sha512(0x080b),
/* 레거시 알고리즘 */
rsa_pkcs1_sha1(0x0201),
ecdsa_sha1(0x0203),
/* 예약된 코드 포인트 */
obsolete_RESERVED(0x0000..0x0200),
dsa_sha1_RESERVED(0x0202),
obsolete_RESERVED(0x0204..0x0400),
dsa_sha256_RESERVED(0x0402),
obsolete_RESERVED(0x0404..0x0500),
dsa_sha384_RESERVED(0x0502),
obsolete_RESERVED(0x0504..0x0600),
dsa_sha512_RESERVED(0x0602),
obsolete_RESERVED(0x0604..0x06FF),
private_use(0xFE00..0xFFFF),
(0xFFFF)
} SignatureScheme;
struct {
SignatureScheme supported_signature_algorithms<2..2^16-2>;
} SignatureSchemeList;
¶
enum {
unallocated_RESERVED(0x0000),
/* 타원 곡선 그룹(ECDHE) */
obsolete_RESERVED(0x0001..0x0016),
secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019),
obsolete_RESERVED(0x001A..0x001C),
x25519(0x001D), x448(0x001E),
/* 유한체 그룹(DHE) */
ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
ffdhe6144(0x0103), ffdhe8192(0x0104),
/* 예약된 코드 포인트 */
ffdhe_private_use(0x01FC..0x01FF),
ecdhe_private_use(0xFE00..0xFEFF),
obsolete_RESERVED(0xFF01..0xFF02),
(0xFFFF)
} NamedGroup;
struct {
NamedGroup named_group_list<2..2^16-1>;
} NamedGroupList;
¶
"obsolete_RESERVED" 범위의 값은 이전 TLS 버전에서 사용되었으며 TLS 1.3 구현에서 제공하거나 협상해서는 안 된다. 사용 중단된 곡선에는 여러 알려진 또는 이론적인 약점이 있거나, 일부는 의도하지 않은 서버 구성 문제로만 사용되는 등 사용 사례가 매우 적었다. 이러한 곡선은 더 이상 일반적인 용도에 적합한 것으로 간주되지 않으며 잠재적으로 안전하지 않다고 가정해야 한다. 여기에 명세된 곡선 집합은 현재 배포되어 있고 올바르게 구성된 모든 TLS 구현과 상호 운용하기에 충분하다.¶
opaque DistinguishedName<1..2^16-1>;
struct {
DistinguishedName authorities<3..2^16-1>;
} CertificateAuthoritiesExtension;
struct {
opaque certificate_extension_oid<1..2^8-1>;
opaque certificate_extension_values<0..2^16-1>;
} OIDFilter;
struct {
OIDFilter filters<0..2^16-1>;
} OIDFilterExtension;
struct {} PostHandshakeAuth;
struct {
Extension extensions<0..2^16-1>;
} EncryptedExtensions;
struct {
opaque certificate_request_context<0..2^8-1>;
Extension extensions<0..2^16-1>;
} CertificateRequest;
¶
enum {
X509(0),
OpenPGP_RESERVED(1),
RawPublicKey(2),
(255)
} CertificateType;
struct {
select (certificate_type) {
case RawPublicKey:
/* RFC 7250의 ASN.1_subjectPublicKeyInfo */
opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;
case X509:
opaque cert_data<1..2^24-1>;
};
Extension extensions<0..2^16-1>;
} CertificateEntry;
struct {
opaque certificate_request_context<0..2^8-1>;
CertificateEntry certificate_list<0..2^24-1>;
} Certificate;
struct {
SignatureScheme algorithm;
opaque signature<0..2^16-1>;
} CertificateVerify;
struct {
opaque verify_data[Hash.length];
} Finished;
¶
암호 스위트는 HKDF와 함께 사용할 AEAD 알고리즘과 해시 알고리즘의 쌍을 정의한다. 암호 스위트 이름은 다음 명명 규칙을 따른다.¶
CipherSuite TLS_AEAD_HASH = VALUE;¶
| 구성요소 | 내용 |
|---|---|
| TLS | "TLS" 문자열 |
| AEAD | 레코드 보호에 사용하는 AEAD 알고리즘 |
| HASH | HKDF 및 Transcript-Hash와 함께 사용하는 해시 알고리즘 |
| VALUE | 이 암호 스위트에 할당된 2바이트 ID |
이 명세는 TLS 1.3에서 사용할 다음 암호 스위트를 정의한다.¶
| 설명 | 값 |
|---|---|
| TLS_AES_128_GCM_SHA256 | {0x13,0x01} |
| TLS_AES_256_GCM_SHA384 | {0x13,0x02} |
| TLS_CHACHA20_POLY1305_SHA256 | {0x13,0x03} |
| TLS_AES_128_CCM_SHA256 | {0x13,0x04} |
| TLS_AES_128_CCM_8_SHA256 | {0x13,0x05} |
대응하는 AEAD 알고리즘 AEAD_AES_128_GCM, AEAD_AES_256_GCM 및 AEAD_AES_128_CCM은 [RFC5116]에 정의되어 있다. AEAD_CHACHA20_POLY1305는 [RFC8439]에 정의되어 있다. AEAD_AES_128_CCM_8은 [RFC6655]에 정의되어 있다. 대응하는 해시 알고리즘은 [SHS]에 정의되어 있다.¶
TLS 1.3은 이전 TLS 버전과 동일한 암호 스위트 공간을 사용하지만, TLS 1.3 암호 스위트는 대칭 암호만 지정하는 방식으로 다르게 정의되며 TLS 1.2에서 사용할 수 없다. 마찬가지로 TLS 1.2 이하의 암호 스위트는 TLS 1.3과 함께 사용할 수 없다.¶
TLS 프로토콜은 많은 일반적인 보안 실수를 방지할 수 없다. 이 부록은 구현자를 돕기 위한 여러 권고사항을 제공한다. [RFC8448]은 TLS 1.3 핸드셰이크의 테스트 벡터를 제공한다.¶
TLS에는 암호학적으로 안전한 의사 난수 생성기 (CSPRNG)가 필요하다. 성능이 우수하고 적절히 안전한 CSPRNG는 대부분의 운영 체제에서 제공되거나 암호화 라이브러리에서 가져올 수 있다. 새로 만드는 것보다 기존 CSPRNG 구현을 사용하는 것이 권고된다. 적절한 라이선스 조건으로 제공되는 충분한 암호화 라이브러리가 이미 많이 있다. 이러한 라이브러리가 만족스럽지 않다면 [RFC4086]에서 난수 값 생성에 관한 지침을 제공한다.¶
TLS는 난수 값을 (1) ClientHello 및 ServerHello의 공개 Random 값과 같은 공개 프로토콜 필드와 (2) 키 자료 생성에 사용한다. CSPRNG가 올바르게 작동한다면 출력에서 CSPRNG 상태를 알아내는 것이 현실적으로 불가능하므로 보안 문제가 되지 않는다. 그러나 CSPRNG가 손상된 경우 공격자가 공개 출력을 사용해 CSPRNG 내부 상태를 알아내고 그 결과 키 자료를 예측할 수 있으며, 이는 [CHECKOWAY] 및 [DSA-1571-1]에 기록되어 있다.¶
구현은 공개 값과 비공개 값을 생성할 때 별도의 CSPRNG를 사용하여 이러한 형태의 공격에 대한 추가 보안을 제공할 수 있다.¶
[RFC8937]은 보안 프로토콜 구현이 장기 개인 키와 결정적 서명 함수를 사용해 (의사) 난수 생성기를 보강하는 방법을 설명한다. 이는 손상되었거나 다른 방식으로 무력화된 난수 생성기의 무작위성을 개선한다.¶
구현은 인증서의 무결성을 검증할 책임이 있으며 일반적으로 인증서 폐기 메시지를 지원해야 한다. 섹션 4.5.1.1을 참조한다. 애플리케이션 프로필에서 구체적으로 달리 나타내지 않는 한, 신뢰할 수 있는 인증 기관(CA)이 올바르게 서명했는지 확인하도록 인증서를 항상 검증해야 한다. 신뢰 앵커의 선택과 추가는 매우 신중하게 수행해야 한다. 사용자는 인증서와 신뢰 앵커에 관한 정보를 볼 수 있어야 한다. 애플리케이션은 최소 및 최대 키 크기도 강제하는 것이 권고된다. 예를 들어 2048비트 RSA 또는 224비트 ECDSA보다 약한 키나 서명을 포함하는 인증 경로는 안전한 애플리케이션에 적합하지 않다.¶
일부 프로토콜에서는 클라이언트 모드와 서버 모드 모두에서 동일한 인증서를 사용하는 것이 일반적이라는 점에 유의한다. 이 설정은 광범위하게 분석되지 않았으며, 이 경우 상위 수준 의미론에 모호함이 없도록 보장하는 것은 상위 수준 프로토콜의 책임이다.¶
구현 경험에 따르면 이전 TLS 명세의 특정 부분은 이해하기 쉽지 않았고 상호 운용성 및 보안 문제의 원인이 되었다. 이러한 영역 중 많은 부분이 이 문서에서 명확해졌지만, 이 부록에는 구현자가 특별히 주의해야 할 가장 중요한 사항을 간단히 나열한다.¶
TLS 프로토콜 문제:¶
여러 TLS 레코드로 조각화된 핸드셰이크 메시지를 올바르게 처리하는가 (섹션 5.1 참조)? 여러 개의 작은 조각으로 분할된 ClientHello 같은 극단적인 경우도 올바르게 처리하는가? 최대 조각 크기를 초과하는 핸드셰이크 메시지를 조각화하는가? 특히 Certificate 및 CertificateRequest 핸드셰이크 메시지는 조각화가 필요할 만큼 클 수 있다. [RFC8879]에 정의된 인증서 압축을 사용하면 조각화 위험을 줄일 수 있다.¶
TLS 1.3 이상을 지원하는 모든 가능한 구성에서 SSL, RC4, EXPORT 암호 및 MD5 ("signature_algorithms" 확장을 통한 지원)를 완전히 제거하고, 이러한 사용 중단된 기능을 사용하려는 시도가 올바르게 실패하도록 보장했는가 (부록 E 참조)?¶
알 수 없는 확장을 포함하여 ClientHello의 TLS 확장을 올바르게 처리하는가?¶
서버가 클라이언트 인증서를 요청했지만 적합한 인증서를 사용할 수 없는 경우 전체 메시지를 생략하는 대신 빈 Certificate 메시지를 올바르게 전송하는가 (섹션 4.5.1 참조)?¶
AEAD-Decrypt에서 생성한 평문 조각을 처리하고 끝에서부터 ContentType을 검색할 때, 피어가 모두 0으로 이루어진 잘못된 평문을 전송한 경우 평문의 시작 지점을 지나서 검색하지 않도록 하는가?¶
ClientHello에서 인식하지 못하는 암호 스위트 (섹션 4.2.2), hello 확장 (섹션 4.3), 명명된 그룹 (섹션 4.3.7), 키 공유 (섹션 4.3.8), 지원되는 버전 (섹션 4.3.1) 및 서명 알고리즘 (섹션 4.3.3)을 올바르게 무시하는가?¶
서버로서 호환되는 비대칭 키 교환 그룹을 지원하지만 "key_share" 확장에서 이를 예상하여 제공하지 않은 클라이언트에 HelloRetryRequest를 전송하는가? 클라이언트로서 서버의 HelloRetryRequest를 올바르게 처리하는가?¶
암호화 세부사항:¶
디피-헬먼 키 교환을 사용할 때 협상된 키의 선행 0바이트를 올바르게 보존하는가 (섹션 7.4.1 참조)?¶
TLS 클라이언트가 서버에서 전송한 디피-헬먼 매개변수를 수락할 수 있는지 검사하는가 (섹션 4.3.8.1 참조)?¶
디피-헬먼 개인 값, ECDSA "k" 매개변수 및 기타 보안에 중요한 값을 생성할 때 강력하고 무엇보다 올바르게 시드된 난수 생성기를 사용하는가 (부록 C.1 참조)? 구현은 [RFC6979]에 명세된 "결정적 ECDSA"를 구현하는 것이 권고된다. 결정적 ECDSA 및 EdDSA처럼 완전히 결정적인 타원 곡선 암호화(ECC) 서명은 쉽게 접근할 수 있는 사물 인터넷(IoT) 장치에서 특정 부채널 및 오류 주입 공격에 취약할 수 있다는 점에 유의한다.¶
디피-헬먼 공개 키 값과 공유 비밀값을 그룹 크기에 맞도록 0으로 패딩하는가 (섹션 4.3.8.1 및 섹션 7.4.1 참조)?¶
클라이언트는 여러 연결에서 하나의 티켓을 재사용하지 않는 것이 권고된다. 티켓을 재사용하면 수동 관찰자가 서로 다른 연결을 연관 지을 수 있다. 티켓을 발행하는 서버는 클라이언트가 사용할 수 있는 연결 수만큼 티켓을 제공하는 것이 권고된다. 예를 들어 HTTP/1.1 [RFC9112]을 사용하는 웹 브라우저는 서버에 6개의 연결을 열 수 있다. 서버는 모든 연결에서 새 티켓을 발행하는 것이 권고된다. 이를 통해 클라이언트가 새 연결을 생성할 때 항상 새 티켓을 사용할 수 있다.¶
서버에 티켓을 제공하면 서버도 서로 다른 연결을 연관 지을 수 있다. 이는 티켓 재사용 여부와 관계없이 가능하다. 클라이언트 애플리케이션은 서로 연관되지 않아야 하는 연결 사이에서 티켓을 제공하지 않는 것이 권고된다. 예를 들어 [FETCH]는 웹 브라우저의 캐시 조회를 분리하기 위한 네트워크 파티션 키를 정의한다.¶
외부 식별자의 레이블은 사용자의 신원에 관한 추가 정보를 제공하지 않도록 선택하는 것이 권고된다. 예를 들어 레이블에 이메일 주소가 포함되면 암호화되는 클라이언트의 Certificate와 달리 수동 공격자가 사용자를 쉽게 식별할 수 있다. 이 위험을 방지할 수 있는 잠재적 방법에는 (1) 무작위 신원 레이블 사용, (2) 서버가 알고 있는 키로 신원을 미리 암호화, (3) 암호화된 클라이언트 헬로 [RFC9849] 사용 등이 있다.¶
외부 PSK 식별자를 여러 연결에 사용하면 일반적으로 외부 관찰자가 연결 간에 클라이언트 및/또는 서버를 추적할 수 있다. 암호화된 클라이언트 헬로 [RFC9849]를 사용하면 이 위험을 완화할 수 있으며, PSK 식별자를 순환하거나 암호화하는 TLS 외부 메커니즘도 같은 효과를 제공할 수 있다.¶
이전 TLS 버전은 익명 디피-헬먼을 기반으로 명시적으로 인증되지 않은 암호 스위트를 제공했다. 이러한 모드는 TLS 1.3에서 사용 중단되었다. 그러나 다음을 포함한 여러 방법으로 검증 가능한 서버 인증을 제공하지 않는 매개변수를 여전히 협상할 수 있다.¶
두 기법 모두 단독으로 사용하면 중간자 공격에 취약하므로 일반적인 용도로 안전하지 않다. 그러나 서버 공개 키의 대역 외 검증, 최초 사용 시 신뢰 또는 채널 바인딩 같은 메커니즘을 통해 이러한 연결을 외부 인증 메커니즘에 결합할 수도 있다 (다만 [RFC5929]에 설명된 채널 바인딩은 TLS 1.3에 정의되어 있지 않음). 이러한 메커니즘을 사용하지 않으면 연결은 능동적인 중간자 공격에 대해 아무런 보호를 제공하지 않는다. 명시적인 구성이나 특정 애플리케이션 프로필이 없는 경우 애플리케이션은 이러한 방식으로 TLS를 사용해서는 안 된다.¶
이 문서에서 사용하는 이름과 일치하도록 [RFC5246]의 다음 용어를 변경한다.¶
[RFC5246]의 섹션 8.1에서 계산되는 master secret은 main secret으로 이름을 변경한다. 수식과 구조체에서는 master_secret 대신 main_secret으로 참조한다. 그러나 PRF 함수의 label 매개변수는 호환성을 위해 변경하지 않는다.¶
premaster secret은 preliminary secret으로 이름을 변경한다. 수식과 구조체에서는 pre_master_secret 대신 preliminary_secret으로 참조한다.¶
[RFC5246]의 섹션 7.4.7.1에 정의된 PreMasterSecret 및 EncryptedPreMasterSecret 구조체는 각각 PreliminarySecret 및 EncryptedPreliminarySecret으로 이름을 변경한다.¶
이에 따라 [RFC7627]에 정의된 확장은 "Extended Main Secret" 확장으로 이름을 변경한다. 확장 코드 포인트는 "extended_main_secret"으로 이름을 변경한다. [RFC7627]의 섹션 4에 있는 PRF 함수의 label 매개변수는 호환성을 위해 변경하지 않는다.¶
TLS 프로토콜은 서로 다른 TLS 버전을 지원할 수 있는 엔드포인트 사이에서 버전을 협상하기 위한 내장 메커니즘을 제공한다.¶
TLS 1.x와 SSL 3.0은 호환되는 ClientHello 메시지를 사용한다. 또한 서버는 ClientHello 형식이 계속 호환되고 클라이언트와 서버가 모두 지원하는 프로토콜 버전이 하나 이상 있는 한, 향후 TLS 버전을 사용하려는 클라이언트도 처리할 수 있다.¶
이전 TLS 버전은 레코드 계층 버전 번호 (TLSPlaintext.legacy_record_version 및 TLSCiphertext.legacy_record_version)를 다양한 용도로 사용했다. TLS 1.3부터 이 필드는 사용 중단되었다. 모든 구현은 TLSPlaintext.legacy_record_version 값을 반드시 무시해야 한다. TLSCiphertext.legacy_record_version 값은 보호 해제를 위한 추가 데이터에 포함되지만, 그 밖에는 무시할 수도 있고 고정 상수 값과 일치하는지 검증할 수도 있다. 버전 협상은 핸드셰이크 버전 (ClientHello.legacy_version 및 ServerHello.legacy_version과 ClientHello, HelloRetryRequest 및 ServerHello의 "supported_versions" 확장)만 사용해 수행한다. 이전 엔드포인트와의 상호 운용성을 극대화하기 위해 TLS 1.0-1.2 사용을 협상하는 구현은 ServerHello와 그 이후의 모든 레코드에서 레코드 계층 버전 번호를 협상된 버전으로 설정하는 것이 권고된다.¶
이전의 비표준 동작 및 잘못 구성된 배포와 최대한 호환되도록, 모든 구현은 이전 TLS 버전의 핸드셰이크를 처리하는 경우에도 이 문서의 기대 사항에 따른 인증 경로 검증을 지원하는 것이 권고된다 (섹션 4.5.1.2 참조).¶
TLS 1.2 및 이전 버전은 핸드셰이크 트랜스크립트의 많은 부분을 비밀값과 유도 키에 반영하는 "Extended Main Secret" [RFC7627] 확장을 지원했다. 이 확장은 부록 D에서 이름이 변경되었다는 점에 유의한다. TLS 1.3은 항상 서버 Finished까지의 트랜스크립트를 해시하므로, TLS 1.3과 이전 버전을 모두 지원하는 구현은 TLS 1.3을 사용할 때마다 API에서 Extended Main Secret 확장의 사용을 나타내는 것이 권고된다.¶
TLS 1.3을 지원하지 않는 서버와 협상하려는 TLS 1.3 클라이언트는 ClientHello.legacy_version에 0x0303(TLS 1.2)을 포함하되 "supported_versions" 확장에는 올바른 버전을 포함하는 일반적인 TLS 1.3 ClientHello를 전송한다. 서버가 TLS 1.3을 지원하지 않으면 이전 버전 번호를 포함하는 ServerHello로 응답한다. 클라이언트가 이 버전 사용에 동의하면 협상은 협상된 프로토콜에 적합한 방식으로 진행된다. 재개를 위해 티켓을 사용하는 클라이언트는 이전에 협상한 버전을 사용해 연결을 시작하는 것이 권고된다.¶
0-RTT 데이터는 이전 서버와 호환되지 않으며, 서버가 TLS 1.3을 지원한다는 사실을 알지 못하는 경우 전송하지 않는 것이 권고된다. 부록 E.3을 참조한다.¶
서버가 선택한 버전을 클라이언트가 지원하지 않거나 수락할 수 없는 경우 클라이언트는 "protocol_version" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
일부 레거시 서버 구현은 TLS 명세를 올바르게 구현하지 않아 인식하지 못하는 TLS 확장이나 버전을 만나면 연결을 중단하는 것으로 알려져 있다. 결함이 있는 서버와의 상호 운용성은 이 문서의 범위를 벗어나는 복잡한 주제이다. 하위 호환 연결을 협상하려면 여러 번 연결을 시도해야 할 수도 있지만, 이 방식은 다운그레이드 공격에 취약하므로 권고되지 않는다.¶
TLS 서버는 자신이 지원하는 가장 높은 버전보다 작은 버전 번호를 나타내는 ClientHello도 수신할 수 있다. "supported_versions" 확장이 존재하면 서버는 섹션 4.3.1에 설명된 대로 해당 확장을 사용하여 반드시 협상해야 한다. "supported_versions" 확장이 존재하지 않으면 서버는 ClientHello.legacy_version과 TLS 1.2 중 더 낮은 버전을 반드시 협상해야 한다. 예를 들어 서버가 TLS 1.0, 1.1 및 1.2를 지원하고 legacy_version이 TLS 1.0이면 서버는 TLS 1.0 ServerHello로 진행한다. "supported_versions" 확장이 없고 서버가 ClientHello.legacy_version보다 높은 버전만 지원하는 경우, 서버는 "protocol_version" 경고와 함께 핸드셰이크를 반드시 중단해야 한다.¶
이전 TLS 버전은 모든 경우의 레코드 계층 버전 번호 값 (TLSPlaintext.legacy_record_version)을 명확하게 명세하지 않았다는 점에 유의한다. 서버는 이 필드에서 다양한 TLS 1.x 버전을 수신하지만 그 값은 항상 반드시 무시해야 한다.¶
0-RTT 데이터는 이전 서버와 호환되지 않는다. 이전 서버는 ClientHello에 이전 버전의 ServerHello로 응답하지만, 0-RTT 데이터를 올바르게 건너뛰지 못하므로 핸드셰이크 완료에 실패한다. 이는 특히 다중 서버 배포를 대상으로 클라이언트가 0-RTT를 사용하려 할 때 문제를 일으킬 수 있다. 예를 들어 일부 서버는 TLS 1.3을 구현하고 일부는 TLS 1.2를 구현하는 방식으로 TLS 1.3을 점진적으로 배포하거나, TLS 1.3 배포가 TLS 1.2로 다운그레이드될 수 있다.¶
0-RTT 데이터를 전송하려는 클라이언트는 TLS 1.2 이하의 ServerHello를 수신하면 연결을 반드시 실패시켜야 한다. 그런 다음 0-RTT를 비활성화한 상태로 연결을 다시 시도할 수 있다. 다운그레이드 공격을 방지하기 위해 클라이언트는 TLS 1.3이 아니라 0-RTT만 비활성화하는 것이 권고된다.¶
이 오류 조건을 방지하려면 다중 서버 배포는 0-RTT를 활성화하기 전에 0-RTT가 없는 TLS 1.3을 균일하고 안정적으로 배포하는 것이 권고된다.¶
현장 측정 [Ben17a] [Ben17b] [Res17a] [Res17b]에 따르면 상당수 미들박스는 TLS 클라이언트/서버 쌍이 TLS 1.3을 협상할 때 잘못 동작한다. 구현은 TLS 1.3 핸드셰이크를 TLS 1.2 핸드셰이크와 더 비슷하게 보이게 하여 이러한 미들박스를 통과해 연결할 가능성을 높일 수 있다.¶
클라이언트는 섹션 4.2.2의 legacy_session_id 섹션에 설명된 대로 ClientHello에 항상 비어 있지 않은 세션 ID를 제공한다.¶
초기 데이터를 제공하지 않는 경우 클라이언트는 두 번째 플라이트 직전에 더미 change_cipher_spec 레코드 (섹션 5의 세 번째 단락 참조)를 전송한다. 이는 두 번째 ClientHello 전이나 암호화된 핸드셰이크 플라이트 전에 올 수 있다. 초기 데이터를 제공하는 경우 이 레코드는 첫 번째 ClientHello 직후에 배치된다.¶
서버는 첫 번째 핸드셰이크 메시지 직후에 더미 change_cipher_spec 레코드를 전송한다. 이는 ServerHello 또는 HelloRetryRequest 뒤에 올 수 있다.¶
이러한 변경을 함께 적용하면 TLS 1.3 핸드셰이크가 TLS 1.2 세션 재개와 비슷해져 미들박스를 통과해 성공적으로 연결할 가능성이 높아진다. 이 "호환 모드"는 부분적으로 협상된다. 클라이언트는 세션 ID를 제공할지 선택할 수 있으며, 서버는 이를 그대로 반환해야 한다. 피어가 무시해야 하므로 어느 쪽이든 핸드셰이크 중 언제든 change_cipher_spec을 전송할 수 있지만, 클라이언트가 비어 있지 않은 세션 ID를 전송한 경우 서버는 이 부록에 설명된 대로 change_cipher_spec을 반드시 전송해야 한다.¶
이전 TLS 버전의 사용을 협상하는 구현은 가능한 경우 순방향 비밀성을 제공하는 AEAD 암호 스위트를 선호하는 것이 권고된다.¶
RC4 암호 스위트의 보안은 [RFC7465]에 제시된 이유로 충분하지 않은 것으로 간주된다. 구현은 어떤 이유로든 어떤 TLS 버전에서도 RC4 암호 스위트를 제공하거나 협상해서는 안 된다.¶
이전 TLS 버전은 매우 낮은 강도의 암호 사용을 허용했다. 강도가 112비트 미만인 암호는 어떤 이유로든 어떤 TLS 버전에서도 제공하거나 협상해서는 안 된다.¶
SSL 2.0 [SSL2], SSL 3.0 [RFC6101], TLS 1.0 [RFC2246] 및 TLS 1.1 [RFC4346]의 보안은 [RFC6176], [RFC7568] 및 [RFC8996]에 열거된 이유로 충분하지 않은 것으로 간주되며, 어떤 이유로든 협상해서는 안 된다.¶
구현은 SSL 버전 2.0 호환 CLIENT-HELLO를 전송해서는 안 된다. 구현은 SSL 버전 2.0 호환 CLIENT-HELLO를 사용해 TLS 1.3 이상을 협상해서는 안 된다. 구현이 이전 TLS 버전을 협상하기 위해 SSL 버전 2.0 호환 CLIENT-HELLO를 수락하는 것은 권고되지 않는다.¶
구현은 ClientHello.legacy_version 및 ServerHello.legacy_version에 0x0303을 반드시 사용해야 한다. 다른 버전 번호가 있는 Hello 메시지를 수신한 구현은 섹션 4.2.2 및 섹션 4.2.3에 설명된 대로 핸드셰이크를 반드시 중단해야 한다.¶
구현은 0x0300 미만의 버전이 있는 레코드를 전송해서는 안 된다. 구현은 0x0300 미만의 버전이 있는 레코드를 수락하지 않는 것이 권고된다 (다만 레코드 버전 번호를 완전히 무시하는 경우 의도치 않게 수락할 수 있음).¶
구현은 [RFC6066]의 섹션 7에 정의된 Truncated HMAC 확장을 사용해서는 안 된다. 이 확장은 AEAD 알고리즘에 적용되지 않으며 일부 상황에서 안전하지 않은 것으로 확인되었기 때문이다.¶
TLS에 대한 완전한 보안 분석은 이 문서의 범위를 벗어난다. 이 부록에서는 원하는 속성을 비형식적으로 설명하고, 더 형식적인 정의를 제공하는 연구 문헌의 상세한 작업을 참조한다.¶
핸드셰이크의 속성과 레코드 계층의 속성을 별도로 다룬다.¶
TLS 핸드셰이크는 단방향 인증(서버만)과 상호 인증 (클라이언트와 서버) 기능을 모두 제공하기 위한 인증된 키 교환(AKE) 프로토콜이다. 핸드셰이크가 완료되면 각 측은 다음 값에 대한 자신의 관점을 출력한다.¶
작업 키 집합을 유도할 수 있는 "세션 키" 집합(주 비밀값에서 유도된 여러 비밀값). 초기 데이터를 사용하는 경우 초기 비밀값에서도 비밀값이 유도된다는 점에 유의한다. 이러한 비밀값은 아래에서 설명한 것처럼 주 비밀값에서 유도된 비밀값보다 다소 약한 속성을 가진다.¶
암호화 매개변수 집합(알고리즘 등).¶
통신 당사자의 신원.¶
공격자는 당사자 간 통신에 사용되는 네트워크를 완전히 제어하는 능동 네트워크 공격자라고 가정한다 [RFC3552]. 이러한 조건에서도 핸드셰이크는 아래에 나열된 속성을 제공해야 한다. 이러한 속성은 반드시 서로 독립적이지는 않으며, 프로토콜 사용자의 요구사항을 반영한다.¶
각 엔드포인트에서 핸드셰이크가 성공적으로 완료되면 핸드셰이크 양쪽은 동일한 세션 키 집합을 출력해야 한다 ([CK01]의 정의 1, 제1부 참조).¶
공유 세션 키는 통신 당사자만 알고 있어야 하며 공격자는 알 수 없어야 한다 ([CK01]의 정의 1, 제2부 참조). 단방향 인증 연결에서 공격자가 서버와 자체 세션 키를 설정할 수 있지만, 그 세션 키는 클라이언트가 설정한 것과 구별된다는 점에 유의한다.¶
피어 신원에 대한 클라이언트의 관점은 서버의 신원을 반영해야 한다. 클라이언트가 인증된 경우 피어 신원에 대한 서버의 관점은 클라이언트의 신원과 일치해야 한다.¶
서로 다른 두 핸드셰이크는 서로 다르고 관련 없는 세션 키를 생성해야 한다. 하나의 핸드셰이크에서 생성된 개별 세션 키도 서로 다르고 독립적이어야 한다.¶
암호화 매개변수는 양쪽에서 동일해야 하며 공격이 없을 때 피어들이 통신했다면 선택했을 매개변수와 같아야 한다 ([BBFGKZ16]의 정의 8 및 9 참조).¶
핸드셰이크가 완료된 후 장기 키 자료
(이 경우 인증서 기반 인증 모드의 서명 키 또는 비대칭 키 교환
모드와 함께 사용하는 PSK의 외부/재개 PSK)가 침해되더라도,
세션 키 자체와 세션 키를 재생성하는 데 사용할 수 있는 모든 자료가
삭제된 경우 세션 키의 보안은 침해되지 않는다
([DOW92] 참조).
특히 키 공유값에 대응하는 개인 키, 공유 비밀값 및
binder_key, resumption_secret,
resumption_secret에서 유도된 PSK를 제외하고
TLS 키 스케줄에서 유도된 키도 삭제해야 한다.
PSK를 "psk_ke" PskKeyExchangeMode에서 사용하는 경우
순방향 비밀성 속성이 충족되지 않는다.
임시 또는 연결별로 사용하려던 키나 비밀값을 삭제하지 않으면
사실상 보호해야 할 추가 장기 키가 만들어진다.
이러한 장기 키가 침해되면 핸드셰이크가 완료된 후에도
연결 트래픽의 보호가 상실될 수 있다.¶
인증서를 사용하는 상호 인증 연결에서는 한 당사자의 장기 비밀값이 침해되더라도 해당 연결에서 그 당사자가 피어를 인증하는 기능이 깨져서는 안 된다 ([HGFS15] 참조). 예를 들어 클라이언트의 서명 키가 침해되더라도 이후 핸드셰이크에서 임의의 서버가 해당 클라이언트에 자신을 사칭할 수 없어야 한다.¶
서버의 신원(인증서)은 수동 공격자로부터 보호되어야 한다. 클라이언트의 신원(인증서)은 수동 및 능동 공격자 모두로부터 보호되어야 한다. 기밀성을 제공하지 않는 암호 스위트에서는 이 속성이 성립하지 않는다. 이 명세는 그러한 암호 스위트를 정의하지 않지만 다른 문서에서 정의할 수 있다.¶
비형식적으로 TLS 1.3의 서명 기반 모드는 비대칭 키 교환으로 설정된 고유하고 비밀인 공유 키를 제공하며, 핸드셰이크 트랜스크립트에 대한 서버의 서명으로 인증하고 MAC을 통해 서버의 신원과 결합한다. 클라이언트가 인증서로 인증되는 경우 클라이언트도 핸드셰이크 트랜스크립트에 서명하고 두 신원에 결합된 MAC을 제공한다. [SIGMA]는 이러한 유형의 키 교환 프로토콜 설계와 분석을 설명한다. 각 연결에 새로운 비대칭 키를 사용하면 출력 키는 순방향 비밀성을 가진다.¶
외부 PSK와 재개 PSK는 장기 공유 비밀값에서 연결별로 고유한 단기 세션 키 집합을 부트스트랩한다. 이 비밀값은 이전 핸드셰이크에서 설정되었을 수 있다. 비대칭 키 설정과 함께 PSK를 사용하면 이러한 세션 키도 순방향 비밀성을 가진다. 재개 PSK는 연결 N에서 계산하고 연결 N+1을 형성하는 데 필요한 재개 비밀값이 연결 N에서 사용하는 트래픽 키와 분리되도록 설계되어 연결 간 순방향 비밀성을 제공한다. 또한 동일한 연결에서 여러 티켓을 설정하면 서로 다른 키와 연관되므로 한 티켓과 연관된 PSK가 침해되어도 다른 티켓과 연관된 PSK로 설정한 연결이 침해되지 않는다. 이 속성은 티켓이 자체 암호화된 경우보다 데이터베이스에 저장되어 삭제할 수 있는 경우에 더 의미가 있다.¶
순방향 비밀성은 키 유출의 영향을 한 방향으로 제한한다 (시간 T2의 키 침해가 T1 < T2인 시간 T1의 일부 키를 침해하지 않음). 반대 방향의 보호 (시간 T1의 침해가 시간 T2의 키를 침해하지 않음)는 비대칭 키 설정을 다시 수행하여 얻을 수 있다. 장기 인증 키가 침해된 경우 비대칭 키 설정을 사용하는 전체 핸드셰이크는 수동 공격자에 대한 보호를 제공한다. resumption_secret이 침해된 경우 비대칭 키 설정을 사용하는 재개 핸드셰이크는 수동 공격자에 대한 보호를 제공하고, 비대칭 키 설정을 사용하는 전체 핸드셰이크는 능동 공격자에 대한 보호를 제공한다. 트래픽 비밀값이 침해된 경우 비대칭 키 설정을 사용하는 모든 핸드셰이크가 능동 공격자에 대한 보호를 제공한다. [RFC7624]의 용어를 사용하면, 비대칭 키 설정을 다시 수행하지 않는 순방향 비밀성은 공격자가 정적 키 유출을 수행하는 것을 막지 못한다. application_traffic_secret_N이 유출되면 공격자는 예를 들어 application_traffic_secret_N+1, application_traffic_secret_N+2 등으로 암호화된 데이터를 포함해 해당 연결에서 앞으로 전송되는 모든 데이터를 수동으로 도청할 수 있다. 비대칭 키 설정을 자주 다시 수행하면 공격자가 동적 키 유출 또는 콘텐츠 유출을 수행하도록 강제한다.¶
PSK 바인더 값은 PSK와 현재 핸드셰이크 사이, 그리고 PSK가 설정된 세션(NewSessionTicket 메시지를 통해 설정된 경우)과 현재 세션 사이에 결합을 형성한다. 원래 핸드셰이크 트랜스크립트가 재개 비밀값을 생성하는 값에 반영되므로 이 결합에는 원래 핸드셰이크 트랜스크립트도 전이적으로 포함된다. 이를 위해서는 재개 비밀값을 생성하는 KDF와 바인더를 계산하는 MAC이 모두 충돌 저항성을 가져야 한다. 자세한 내용은 부록 F.1.1을 참조한다. 참고: 바인더는 다른 PSK의 바인더 값을 포함하지 않지만, 해당 값은 Finished MAC에 포함된다.¶
참고: 이 명세는 현재 서버가 인증서 기반이 아닌 핸드셰이크(예: PSK)에서 CertificateRequest 메시지를 전송하는 것을 허용하지 않는다. 향후 이 제한이 완화되면 클라이언트의 서명은 서버 인증서를 직접 포함하지 않는다. 그러나 PSK가 NewSessionTicket을 통해 설정된 경우 클라이언트의 서명은 PSK 바인더를 통해 서버 인증서를 전이적으로 포함한다. [PSK-FINISHED]는 서버 인증서에 결합하지 않는 구성에 대한 구체적인 공격을 설명한다 ([Kraw16]도 참조). 클라이언트가 서로 다른 두 엔드포인트와 동일한 PSK/키 ID 쌍을 공유할 가능성이 있는 경우 인증서 기반 클라이언트 인증을 사용하는 것은 안전하지 않다. 달리 규정하는 다른 명세가 없는 경우 구현은 외부 PSK를 클라이언트 또는 서버의 인증서 기반 인증과 결합해서는 안 된다. [RFC8773]은 이를 허용하는 확장을 제공하지만 이 명세만큼 분석되지 않았다.¶
익스포터를 사용하면 고유한 세션 키에서 생성되므로 고유하고 비밀인 값을 생성한다. 서로 다른 레이블과 콘텍스트로 계산된 익스포터는 계산적으로 독립적이므로 하나에서 다른 하나를 계산하거나 내보낸 값에서 세션 비밀값을 계산하는 것은 현실적으로 불가능하다. 참고: 익스포터는 임의 길이 값을 생성할 수 있다. 익스포터를 채널 바인딩으로 사용하는 경우 내보낸 값은 충돌 저항성을 제공할 만큼 충분히 커야 한다. TLS 1.3에서 제공하는 익스포터는 각각 초기 트래픽 키 및 애플리케이션 트래픽 키와 동일한 Handshake Context에서 유도되므로 비슷한 보안 속성을 가진다. 클라이언트 인증서를 포함하지 않는다는 점에 유의한다. 클라이언트 인증서에 결합하려는 향후 애플리케이션은 전체 핸드셰이크 트랜스크립트를 포함하는 새로운 익스포터를 정의해야 할 수 있다.¶
모든 핸드셰이크 모드에서 Finished MAC과 존재하는 경우 서명은 다운그레이드 공격을 방지한다. 또한 섹션 4.2.3에 설명된 대로 무작위 논스의 특정 바이트를 사용하면 이전 TLS 버전으로의 다운그레이드를 탐지할 수 있다. TLS 1.3과 다운그레이드에 관한 자세한 내용은 [BBFGKZ16]을 참조한다.¶
클라이언트와 서버가 공유 키를 설정하기에 충분한 정보를 교환하는 즉시 나머지 핸드셰이크는 암호화되므로, 계산된 공유 키가 인증되지 않았더라도 수동 공격자에 대한 보호를 제공한다. 서버가 클라이언트보다 먼저 인증하므로 클라이언트는 서버에 자신을 인증하는 경우 인증된 서버에만 신원을 공개하도록 보장할 수 있다. 길이로 인해 신원 정보가 누출되는 것을 방지하려면 구현이 핸드셰이크 중 제공된 레코드 패딩 메커니즘을 사용해야 한다는 점에 유의한다. 클라이언트가 제안한 PSK 식별자와 서버가 선택한 식별자는 암호화되지 않는다.¶
TLS 1.3의 키 유도는 [RFC5869]에 정의된 HKDF와 그 두 구성요소인 HKDF-Extract 및 HKDF-Expand를 사용한다. HKDF 구성의 전체 근거는 [Kraw10]에서, TLS 1.3에서 이러한 방식으로 사용하는 근거는 [KW16]에서 확인할 수 있다. 이 문서 전체에서 HKDF-Extract를 적용한 뒤에는 하나 이상의 HKDF-Expand 호출이 뒤따른다. 이 순서는 항상 따라야 한다 (이 문서의 향후 개정에서도 포함). 특히 사이에 HKDF-Expand 없이 한 HKDF-Extract의 출력을 다른 HKDF-Extract 적용의 입력으로 사용하지 않는 것이 권고된다. 일부 동일한 입력에 HKDF-Expand를 여러 번 적용하는 것은 키 및/또는 레이블로 구분되는 한 허용된다.¶
HKDF-Expand는 입력과 출력 모두 길이가 가변인 의사 난수 함수(PRF)를 구현한다는 점에 유의한다. 이 문서에서 HKDF를 사용하는 일부 경우 (예: 익스포터 및 resumption_secret 생성)에는 HKDF-Expand 적용이 충돌 저항성을 가져야 한다. 즉, 동일한 값을 출력하는 서로 다른 두 HKDF-Expand 입력을 찾는 것이 현실적으로 불가능해야 한다. 이를 위해 기반 해시 함수가 충돌 저항성을 가져야 하며 HKDF-Expand의 출력 길이는 최소 256비트 (또는 해시 함수에서 충돌 탐색을 방지하는 데 필요한 만큼)여야 한다.¶
핸드셰이크 중이든 핸드셰이크 후 인증 중이든 서버에 인증서 기반 인증 데이터를 전송한 클라이언트는 이후 서버가 자신을 인증된 것으로 간주하는지 확신할 수 없다. 서버가 연결을 단방향 인증 또는 상호 인증으로 간주하는지 클라이언트가 판단해야 하는 경우 애플리케이션 계층에서 이를 프로비저닝해야 한다. 자세한 내용은 [CHHSV17]을 참조한다. 또한 [Kraw16]의 핸드셰이크 후 인증 분석에 따르면 핸드셰이크 후 단계에서 전송한 인증서로 식별되는 클라이언트는 트래픽 키를 소유한다. 따라서 이 당사자는 원래 핸드셰이크에 참여한 클라이언트이거나, 원래 클라이언트가 트래픽 키를 위임한 당사자이다 (트래픽 키가 침해되지 않았다고 가정).¶
0-RTT 동작 모드는 일반적으로 1-RTT 데이터와 비슷한 보안 속성을 제공하지만, 0-RTT 암호화 키가 완전한 순방향 비밀성을 제공하지 않고 서버가 잠재적으로 과도한 상태를 유지하지 않으면 핸드셰이크의 고유성 (재전송 불가능성)을 보장할 수 없다는 두 가지 예외가 있다. 재전송 노출을 제한하는 메커니즘은 섹션 8을 참조한다.¶
exporter_secret과 early_exporter_secret은 트래픽 키와 독립적이도록 유도되므로 해당 키로 암호화된 트래픽의 보안에 위협이 되지 않는다. 그러나 이러한 비밀값은 모든 익스포터 값을 계산하는 데 사용할 수 있으므로 가능한 한 빨리 삭제하는 것이 권고된다. 익스포터 레이블의 전체 집합을 알고 있다면, 구현은 모든 레이블에 대해 익스포터 계산의 내부 Derive-Secret 단계를 미리 계산한 다음 [early_]exporter_secret을 삭제하고, 각 내부 값이 다시 필요하지 않다는 사실을 알게 되는 즉시 해당 값도 삭제하는 것이 권고된다.¶
레코드 계층은 핸드셰이크가 양방향 암호화 키와 논스를 유도하는 데 사용할 수 있는 강력한 트래픽 비밀값을 생성하는 데 의존한다. 이것이 참이고 키를 섹션 5.5에 표시된 양보다 많은 데이터에 사용하지 않는다고 가정하면 레코드 계층은 다음 보장을 제공해야 한다.¶
공격자는 특정 레코드의 평문 내용을 알아낼 수 없어야 한다.¶
공격자는 수신자가 수락할 기존 레코드와 다른 새 레코드를 만들 수 없어야 한다.¶
공격자는 수신자가 이미 수락한 레코드를 다시 수락하게 하거나, 레코드 N을 먼저 처리하지 않고 레코드 N+1을 수락하게 할 수 없어야 한다.¶
외부 길이가 주어진 레코드에서 공격자는 레코드 중 어느 정도가 콘텐츠이고 어느 정도가 패딩인지 알아낼 수 없어야 한다.¶
섹션 4.7.3에 설명된 트래픽 키 갱신 메커니즘을 사용하고 이전 세대 키를 삭제한 경우, 엔드포인트를 침해한 공격자는 이전 키로 암호화된 트래픽을 복호화할 수 없어야 한다.¶
비형식적으로 TLS 1.3은 강력한 키로 평문을 AEAD 보호하여 이러한 속성을 제공한다. AEAD 암호화 [RFC5116]는 데이터의 기밀성과 무결성을 제공한다. 재전송 불가능성은 각 레코드에 별도의 논스를 사용하여 제공되며, 논스는 레코드 시퀀스 번호 (섹션 5.3)에서 유도되고 시퀀스 번호는 양쪽에서 독립적으로 유지된다. 따라서 순서가 뒤바뀌어 전달된 레코드는 AEAD 보호 해제 실패를 일으킨다. 동일한 평문이 동일한 키로 서로 다른 사용자에 의해 반복해서 암호화될 때 (HTTP에서 흔히 발생) 대규모 암호 분석을 방지하기 위해 논스는 시퀀스 번호와 트래픽 키와 함께 유도된 연결별 비밀 초기화 벡터를 혼합하여 구성한다. 이 구성의 분석은 [BT16]을 참조한다.¶
TLS 1.3의 키 재설정 기법 (섹션 7.2 참조)은 [REKEY]에서 설명한 직렬 생성기 구성을 따른다. 이 문서는 키 재설정을 사용하면 키를 재설정하지 않는 경우보다 더 많은 암호화에 키를 사용할 수 있음을 보여준다. 이는 HKDF-Expand-Label 함수가 의사 난수 함수(PRF)로서 안전하다는 데 의존한다. 또한 이 함수가 실제로 단방향인 한 키 변경 이전의 트래픽 키를 계산하는 것은 불가능하다(순방향 비밀성).¶
TLS는 해당 연결의 트래픽 비밀값이 침해된 후 연결에서 전달되는 데이터에 보안을 제공하지 않는다. 즉, TLS는 트래픽 비밀값에 대해 침해 후 보안/미래 비밀성/후방 비밀성을 제공하지 않는다. 실제로 트래픽 비밀값을 알아낸 공격자는 해당 연결의 향후 모든 트래픽 비밀값을 계산할 수 있다. 이러한 보장을 원하는 시스템은 새로운 핸드셰이크를 수행하고 비대칭 키 교환을 사용하는 새 연결을 설정해야 한다.¶
TLS는 암호화된 패킷의 길이와 타이밍을 관찰하는 다양한 트래픽 분석 공격에 취약하다 [CLINIC] [HCJC16]. 고정된 콘텐츠 모음을 호스팅하는 비디오 서버처럼 구분할 수 있는 가능한 메시지 집합이 작은 경우 특히 쉽지만, 더 복잡한 상황에서도 유용한 정보를 제공한다.¶
TLS는 이러한 형태의 공격에 대한 구체적인 방어책을 제공하지 않지만 애플리케이션에서 사용할 수 있는 패딩 메커니즘을 포함한다. AEAD 함수로 보호되는 평문은 콘텐츠와 가변 길이 패딩으로 구성되므로, 애플리케이션은 임의 길이의 암호화된 레코드뿐 아니라 전송 기간과 무전송 기간의 차이를 숨기기 위한 패딩 전용 위장 트래픽도 생성할 수 있다. 패딩은 실제 콘텐츠와 함께 암호화되므로 공격자는 패딩 길이를 직접 알아낼 수 없지만, 레코드 처리 중 노출되는 타이밍 채널을 사용해 간접적으로 측정할 수 있다 (즉, 레코드 처리 시간을 관찰하거나 레코드를 조금씩 전송해 서버 응답을 유발하는 레코드를 확인함). 일반적으로 상수 시간 패딩 제거 함수조차 콘텐츠를 데이터 의존적 함수에 전달할 가능성이 있으므로 이러한 모든 채널을 제거하는 방법은 알려져 있지 않다. 최소한 완전한 상수 시간 서버 또는 클라이언트를 구현하려면 상위 수준 프로토콜도 상수 시간으로 만드는 것을 포함해 애플리케이션 계층 프로토콜 구현과 긴밀하게 협력해야 한다.¶
참고: 강력한 트래픽 분석 방어는 패킷 전송 지연과 트래픽 양 증가로 인해 성능 저하를 일으킬 가능성이 높다.¶
일반적으로 TLS는 부채널 공격 (즉, 타이밍 같은 보조 채널을 통해 통신을 공격하는 방식)에 대한 구체적인 방어책을 제공하지 않으며, 관련 암호화 프리미티브의 구현에 이를 맡긴다. 그러나 TLS의 특정 기능은 부채널 저항성 코드를 더 쉽게 작성할 수 있도록 설계되었다.¶
MAC 후 암호화 복합 구조를 사용한 이전 TLS 버전과 달리, TLS 1.3은 AEAD 알고리즘만 사용하므로 구현이 이러한 프리미티브의 자체 완결적 상수 시간 구현을 사용할 수 있다.¶
TLS는 모든 복호화 오류에 통일된 "bad_record_mac" 경고를 사용하여 공격자가 메시지 일부에 대한 단편적인 정보를 얻는 것을 방지한다. 이러한 오류가 발생하면 연결을 종료하여 추가적인 저항성을 제공한다. 새 연결은 서로 다른 암호화 자료를 사용하므로 여러 번의 시도가 필요한 암호화 프리미티브 공격을 방지한다.¶
부채널을 통한 정보 누출은 TLS보다 상위 계층의 애플리케이션 프로토콜과 이를 사용하는 애플리케이션에서 발생할 수 있다. 부채널 공격에 대한 저항성은 애플리케이션과 애플리케이션 프로토콜이 기밀 정보가 의도치 않게 누출되지 않도록 각각 보장하는 데 달려 있다.¶
재전송 가능한 0-RTT 데이터는 TLS를 사용하는 애플리케이션이 재전송에 안전하도록 특별히 설계되지 않은 경우 여러 보안 위협을 일으킨다 (최소한 멱등성을 의미하지만 많은 경우 상수 시간 응답 같은 더 강한 조건도 필요할 수 있음). 잠재적 공격에는 다음이 포함된다.¶
부작용을 일으키는 동작 (예: 물품 구매 또는 송금)을 복제하여 사이트 또는 사용자에게 피해를 주는 경우.¶
공격자가 0-RTT 메시지를 저장하고 재전송하여 다른 메시지와의 순서를 바꿀 수 있다 (예: 생성 후에 삭제가 오도록 이동).¶
캐싱 같은 부작용으로 발생하는 기존 정보 누출을 증폭한다. 공격자는 관심 있는 리소스를 캐시하지 않은 캐시 노드에 0-RTT 메시지를 재전송한 다음 별도의 연결을 사용해 해당 리소스가 캐시에 추가되었는지 확인하여 0-RTT 메시지 내용에 관한 정보를 알아낼 수 있다. 이는 0-RTT 메시지를 재전송할 수 있는 만큼 서로 다른 캐시 노드에서 반복할 수 있다.¶
데이터를 매우 많이 재전송할 수 있으면 암호화 연산의 속도를 반복 측정하는 등 추가 공격이 가능해진다. 또한 속도 제한 시스템을 과부하시킬 수 있다. 이러한 공격에 대한 자세한 설명은 [Mac17]을 참조한다.¶
궁극적으로 서버는 0-RTT 데이터 복제를 사용하는 공격으로부터 자신을 보호할 책임이 있다. 섹션 8에 설명된 메커니즘은 TLS 계층에서 재전송을 방지하기 위한 것이지만 클라이언트 데이터의 여러 복사본 수신에 대한 완전한 보호를 제공하지는 않는다. 서버가 클라이언트에 관한 정보를 가지고 있지 않은 경우 (예: 상태를 공유하지 않는 다른 클러스터에 있거나 섹션 8.1에 설명된 대로 티켓이 삭제된 경우) TLS 1.3은 1-RTT 핸드셰이크로 대체된다. 이 상황에서 애플리케이션 계층 프로토콜이 데이터를 재전송하면, 공격자는 ClientHello를 원래 클러스터 (데이터를 즉시 처리함)와 다른 클러스터 (1-RTT로 대체하고 애플리케이션 계층 재전송 시 데이터를 처리함)에 모두 전송하여 메시지 복제를 유발할 수 있다. 이 공격의 규모는 클라이언트가 트랜잭션을 재시도하려는 의향에 의해 제한되므로 제한된 양의 복제만 가능하며, 각 복사본은 서버에 새 연결로 나타난다.¶
올바르게 구현하면 섹션 8.1과 섹션 8.2에 설명된 메커니즘은 일관된 상태를 가진 모든 클러스터에서 재전송된 ClientHello와 연관된 0-RTT 데이터가 여러 번 수락되는 것을 방지한다. 단일 티켓에 대해 하나의 클러스터만 0-RTT 사용을 허용하는 서버에서는 특정 ClientHello와 연관된 0-RTT 데이터가 한 번만 수락된다. 그러나 상태가 완전히 일관되지 않으면 복제 창 동안 공격자가 데이터의 여러 복사본을 수락하게 할 수 있다. 클라이언트는 서버 동작의 정확한 세부사항을 알 수 없으므로, 재전송되어도 안전하지 않고 여러 1-RTT 연결에서 재시도할 의향도 없는 메시지를 초기 데이터로 전송해서는 안 된다.¶
애플리케이션 프로토콜은 사용법을 정의하는 프로필 없이 0-RTT 데이터를 사용해서는 안 된다. 해당 프로필은 어떤 메시지나 상호작용이 0-RTT와 함께 사용하기 안전한지, 서버가 0-RTT를 거부하고 1-RTT로 대체하는 상황을 어떻게 처리할지 정의해야 한다.¶
또한 실수로 잘못 사용하는 것을 방지하기 위해 TLS 구현은 애플리케이션이 구체적으로 요청하지 않는 한 0-RTT(전송 또는 수락)를 활성화해서는 안 되며, 서버가 0-RTT 데이터를 거부한 경우 애플리케이션의 지시 없이 자동으로 재전송해서는 안 된다. 서버 측 애플리케이션은 일부 애플리케이션 트래픽 유형에 대해 0-RTT 데이터를 특별하게 처리할 수 있다 (예: 연결 중단, 애플리케이션 계층에서 데이터 재전송 요청, 핸드셰이크 완료까지 처리 지연). 애플리케이션이 이러한 처리를 구현할 수 있도록 TLS 구현은 애플리케이션이 핸드셰이크 완료 여부를 판단할 수 있는 방법을 반드시 제공해야 한다.¶
ClientHello를 재전송하면 동일한 초기 익스포터가 생성되므로 이러한 익스포터를 사용하는 애플리케이션은 추가로 주의해야 한다. 특히 이러한 익스포터를 인증 채널 바인딩으로 사용하는 경우(예: 익스포터 출력에 서명), PSK를 침해한 공격자는 인증 키를 침해하지 않고도 연결 간에 인증자를 옮길 수 있다.¶
또한 초기 익스포터는 키 재사용을 일으키므로 서버-클라이언트 암호화 키를 생성하는 데 사용하지 않는 것이 권고된다. 이는 초기 애플리케이션 트래픽 키를 클라이언트-서버 방향으로만 사용하는 것과 유사하다.¶
구현은 유효하지 않은 PSK 바인더에 핸드셰이크 중단으로 응답하므로 공격자가 특정 PSK 식별자가 유효한지 검증할 수 있다. 구체적으로 서버가 외부 PSK 및 인증서 기반 핸드셰이크를 모두 수락하는 경우 유효한 PSK 식별자는 핸드셰이크 실패를 일으키지만, 유효하지 않은 식별자는 건너뛰어 성공적인 인증서 핸드셰이크를 일으킨다. PSK 핸드셰이크만 지원하는 서버는 유효한 PSK 식별자가 없는 경우와 식별자는 있지만 바인더가 유효하지 않은 경우를 동일하게 처리하여 이러한 형태의 공격에 저항할 수 있다.¶
TLS의 외부 PSK는 정확히 하나의 클라이언트와 하나의 서버만 알도록 설계되었다. 그러나 [RFC9257]에서 언급한 것처럼 두 개보다 많은 엔터티가 PSK를 공유하는 사용 사례가 있다. 이러한 상황에서는 침해된 그룹 구성원이 다른 모든 구성원을 사칭할 수 있는 예상된 보안 약점 외에도, 악의적인 비구성원이 정직한 그룹 구성원 사이의 핸드셰이크 경로를 재지정하여 의도하지 않은 방식으로 연결할 수 있다 [Selfie]. [RFC9257]은 [RFC9258]에 정의된 외부 PSK 임포터 사용을 포함해 이러한 악의적인 메시지 경로 재지정을 방지하는 외부 PSK 사용 권고사항을 제공한다.¶
TLS 1.3을 유용한 신원이 없는 자체 서명 인증서 (DTLS-SRTP [RFC5763]의 경우)나 피어 인증용 원시 공개 키 [RFC7250]와 함께 사용하면 잘못된 바인딩 공격 [MM24]에 취약할 수 있다. 이 위험은 "external_id_hash" 확장 [RFC8844]을 사용하거나, 서버만 인증하는 경우 서버가 "server_name" 확장이 예상 신원과 일치하는지 검증하여 완화할 수 있다.¶
TLS 1.3은 RSA 키 전송을 사용하지 않으므로 Bleichenbacher 유형 공격 [Blei98]에 직접 취약하지 않지만, TLS 1.3 서버가 이전 TLS 버전의 콘텍스트에서 정적 RSA도 지원하면 TLS 1.3 연결에서 서버를 사칭할 수 있다 [JSS15]. TLS 1.3 구현은 모든 TLS 버전에서 정적 RSA 지원을 비활성화하여 이 공격을 방지할 수 있다. 원칙적으로 구현은 정적 RSA 복호화와 RSA 서명을 위해 서로 다른 keyUsage 비트를 가진 인증서를 분리할 수도 있지만, 이 기법은 digitalSignature 비트가 설정되지 않은 인증서의 키로 생성된 서명을 클라이언트가 거부하는 데 의존하며 많은 클라이언트가 이 제한을 강제하지 않는다.¶
Martin Abadi 캘리포니아 대학교 산타크루즈 abadi@cs.ucsc.edu Christopher Allen (TLS 1.0 공동 편집자) Alacrity Ventures ChristopherA@AlacrityManagement.com Nimrod Aviram 텔아비브 대학교 nimrod.aviram@gmail.com Richard Barnes Cisco rlb@ipv.sx Steven M. Bellovin 컬럼비아 대학교 smb@cs.columbia.edu David Benjamin Google davidben@google.com Benjamin Beurdouche INRIA & Microsoft Research benjamin.beurdouche@ens.fr Karthikeyan Bhargavan (RFC 7627 편집자) INRIA karthikeyan.bhargavan@inria.fr Simon Blake-Wilson (RFC 4492 공동 저자) BCI sblakewilson@bcisse.com Nelson Bolyard (RFC 4492 공동 저자) Sun Microsystems, Inc. nelson@bolyard.com Ran Canetti IBM canetti@watson.ibm.com Matt Caswell OpenSSL matt@openssl.org Stephen Checkoway 일리노이 대학교 시카고 sfc@uic.edu Pete Chown Skygate Technology Ltd pc@skygate.co.uk Katriel Cohn-Gordon 옥스퍼드 대학교 me@katriel.co.uk Cas Cremers 옥스퍼드 대학교 cas.cremers@cs.ox.ac.uk Antoine Delignat-Lavaud (RFC 7627 공동 저자) INRIA antdl@microsoft.com Tim Dierks (TLS 1.0 공동 저자, TLS 1.1 및 1.2 공동 편집자) 독립 tim@dierks.org Roelof DuToit Symantec Corporation roelof_dutoit@symantec.com Taher Elgamal Securify taher@securify.com Pasi Eronen Nokia pasi.eronen@nokia.com Cedric Fournet Microsoft fournet@microsoft.com Anil Gangolli anil@busybuddha.org David M. Garrett dave@nulldereference.com Illya Gerasymchuk 독립 illya@iluxonchik.me Alessandro Ghedini Cloudflare Inc. alessandro@cloudflare.com Daniel Kahn Gillmor ACLU dkg@fifthhorseman.net Matthew Green 존스 홉킨스 대학교 mgreen@cs.jhu.edu Jens Guballa ETAS jens.guballa@etas.com Felix Guenther TU Darmstadt mail@felixguenther.info Vipul Gupta (RFC 4492 공동 저자) Sun Microsystems Laboratories vipul.gupta@sun.com Chris Hawk (RFC 4492 공동 저자) Corriente Networks LLC chris@corriente.net Kipp Hickman Alfred Hoenes David Hopwood 독립 컨설턴트 david.hopwood@blueyonder.co.uk Marko Horvat MPI-SWS mhorvat@mpi-sws.org Jonathan Hoyland 런던 대학교 로열 홀러웨이 jonathan.hoyland@gmail.com Subodh Iyengar Facebook subodh@fb.com Benjamin Kaduk Akamai Technologies kaduk@mit.edu Hubert Kario Red Hat Inc. hkario@redhat.com Phil Karlton (SSL 3.0 공동 저자) Leon Klingele 독립 mail@leonklingele.de Paul Kocher (SSL 3.0 공동 저자) Cryptography Research paul@cryptography.com Hugo Krawczyk IBM hugokraw@us.ibm.com Adam Langley (RFC 7627 공동 저자) Google agl@google.com Olivier Levillain ANSSI olivier.levillain@ssi.gouv.fr Xiaoyin Liu 노스캐롤라이나 대학교 채플힐 xiaoyin.l@outlook.com Ilari Liusvaara 독립 ilariliusvaara@welho.com Atul Luykx K.U. Leuven atul.luykx@kuleuven.be Colm MacCarthaigh Amazon Web Services colm@allcosts.net Carl Mehner USAA carl.mehner@usaa.com Jan Mikkelsen Transactionware janm@transactionware.com Bodo Moeller (RFC 4492 공동 저자) Google bodo@acm.org Kyle Nekritz Facebook knekritz@fb.com Erik Nygren Akamai Technologies erik+ietf@nygren.org Magnus Nystrom Microsoft mnystrom@microsoft.com Kazuho Oku DeNA Co., Ltd. kazuhooku@gmail.com Kenny Paterson 런던 대학교 로열 홀러웨이 kenny.paterson@rhul.ac.uk Christopher Patton 플로리다 대학교 cjpatton@ufl.edu Alfredo Pironti (RFC 7627 공동 저자) INRIA alfredo.pironti@inria.fr Andrei Popov Microsoft andrei.popov@microsoft.com John Preuß Mattsson Ericsson john.mattsson@ericsson.com Marsh Ray (RFC 7627 공동 저자) Microsoft maray@microsoft.com Robert Relyea Netscape Communications relyea@netscape.com Kyle Rose Akamai Technologies krose@krose.org Jim Roskind Amazon jroskind@amazon.com Michael Sabin Joe Salowey Tableau Software joe@salowey.net Rich Salz Akamai rsalz@akamai.com David Schinazi Apple Inc. dschinazi@apple.com Sam Scott 런던 대학교 로열 홀러웨이 me@samjs.co.uk Mohit Sethi 알토 대학교 mohit@iki.fi Thomas Shrimpton 플로리다 대학교 teshrim@ufl.edu Dan Simon Microsoft, Inc. dansimon@microsoft.com Brian Smith 독립 brian@briansmith.org Ben Smyth Ampersand www.bensmyth.com Brian Sniffen Akamai Technologies ietf@bts.evenmere.org Nick Sullivan Cloudflare Inc. nick@cloudflare.com Bjoern Tackmann 캘리포니아 대학교 샌디에이고 btackmann@eng.ucsd.edu Tim Taubert Mozilla ttaubert@mozilla.com Martin Thomson Mozilla mt@mozilla.com Hannes Tschofenig Arm Limited Hannes.Tschofenig@arm.com Sean Turner sn3rd sean@sn3rd.com Steven Valdez Google svaldez@google.com Filippo Valsorda Cloudflare Inc. filippo@cloudflare.com Thyla van der Merwe 런던 대학교 로열 홀러웨이 tjvdmerwe@gmail.com Victor Vasiliev Google vasilvv@google.com Loganaden Velvindron cyberstorm.mu logan@cyberstorm.mu Hoeteck Wee 파리 고등사범학교 hoeteck@alum.mit.edu Tom Weinstein David Wong NCC Group david.wong@nccgroup.trust Christopher A. Wood Apple Inc. cawood@apple.com Tim Wright Vodafone timothy.wright@vodafone.com Peter Wu 독립 peter@lekensteyn.nl Kazu Yamamoto Internet Initiative Japan Inc. kazu@iij.ad.jp¶