TLS1.3中文版下(RC8446)

6. 警告协议(Alert Protocol)

TLS提供了一个Alert内容类型来指示关闭信息和错误。 和其他消息一样,警告消息也是根据当前连接状态进行加密的。
警告消息传达了警告的描述和一个遗留字段,在以前的TLS版本中传达了消息的严重程度。 警告分为两类:关闭警告和错误警告。 在TLS 1.3中,警告的严重性是隐含在警告的类型中的,"level"字段可以被忽略。 close_notify警报用于指示连接的一个方向的有序关闭。 当收到这样的告警时,TLS应该向应用程序发出数据结束的提示。
错误警报表示连接的中止关闭(见6.2节)。 当收到错误警报时,TLS应该向应用程序提示错误,并且不允许再在连接上发送或接收任何数据。 服务器和客户端必须丢弃在失败的连接中建立的秘密值和密钥,但与会话ticket相关联的PSK除外,如果可能的话,这些PSK应该被丢弃。
6.2节中列出的所有告警必须以AlertLevel=fatal的方式发送,并且不管消息中的AlertLevel是什么都必须作为错误告警处理。 未知的警报类型必须作为错误警报处理。
注意:TLS定义了两个通用的警报(见第6节),消息解析失败时使用。 当对端收到不能按照语法解析的消息时(例如,消息长度超出了消息边界或包含一个超出范围的长度),必须使用decode_error警报终止连接。如果接收到语法正确但语义无效的消息(例如,DHE share为p - 1,或无效的枚举),必须以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),
      no_application_protocol(120),
      (255)
  } AlertDescription;

  struct {
      AlertLevel level;
      AlertDescription description;
  } Alert;

6.1. 关闭警报(Closure Alerts)

客户端和服务器必须共享连接结束的信息,以避免截断攻击。
close_notify:这个警报通知接收者,发送者不会再在这个连接上发送任何消息。在收到关闭警报后收到任何数据都必须被忽略。
user_canceled:该警报通知接受者,发送者将不再在该连接上发送任何消息。该警报通知接收者,发送者因为某些与协议失败无关的原因取消了握手。如果用户在握手完成后取消了一个操作,那么通过发送close_notify来关闭连接更为合适。这个警报后面应该有一个 close_notify。这个告警一般AlertLevel=warning。
任何一方都可以通过发送close_notify警报来关闭其写侧的连接。 在收到关闭警报后收到的任何数据都必须忽略。 如果在close_notify之前收到了一个传输级的关闭,那么接收者就不能知道所有发送的数据都已经收到了。
每一方必须在关闭其连接的写侧之前发送close_notify警报,除非它已经发送了一些错误警报。这对它的读端连接没有任何影响。请注意,这是TLS 1.3对之前TLS版本的一个变化,在TLS 1.3之前的版本中,需要对close_notify做出反应,丢弃等待写的数据,并立即发送close_notify 警报。之前的要求可能会导致读端截断。双方不需要等待收到close_notify 警报后再关闭其读端连接,尽管这样做会带来截断的可能性。
如果使用TLS的应用协议在TLS连接关闭后仍然有数据想要通过底层传输,TLS实现必须收到一个close_notify警报再向应用层指示数据结束。 这个标准的任何部分都不应该被视为规定了TLS的使用配置文件管理数据传输的方式,包括何时打开或关闭连接。

注意:我们假设关闭连接的写端会在销毁传输之前可靠地传送待处理的数据。

6.2. 错误警报(Error Alert)

TLS中的错误处理非常简单。 当检测到错误时,检测方会向对端发送一条消息。 在发送或收到致命的警报消息后,双方必须立即关闭连接。
每当遇到致命的错误条件时,应该发送一个适当的致命警告,并且必须关闭连接,不能再发送或接收任何数据。 在本规范的其余部分,当使用"终止连接"(terminate the connection)和"中止握手"(abort the handshake)这两个短语时,如果没有特定的警报,则意味着应该发送下面描述的警报。 短语"终止连接并发出X警报"和"中止握手并发出X警报"意味着如果要发出警报必须发送警报X。 本节中定义的所有警报,以及所有未知的警报,从TLS 1.3开始被认为是致命的(见第6节)。实现应该提供一种方法来方便记录警报的发送和接收。
定义了以下错误提示:
unexpected_message:收到了不适当的信息(例如,错误的握手信息、过早的应用数据等)。在适当的实现之间的通信中绝对不应该观察到这个警报。
bad_record_mac:如果收到一个不能解密的记录,就会返回这个警报。 因为AEAD算法结合了解密和验证,同时也为了避免侧信道攻击,所以这个警报用于所有的解密失败。在适当的实现之间的通信中,除非消息在网络中被破坏,否则绝不应该观察到这个警报。
record_overflow:收到的TLSCiphertext记录长度超过214+256字节,或记录解密后的TLSPlaintext记录长度超过214字节(或其他协商的限制)。 在适当的实现之间的通信中,除非消息在网络中被破坏,否则绝不应该观察到这个警报。
handshake_failure: 收到handshake_failure的警报消息表明发送者无法在给定选项下协商出一套可接受的安全参数。
bad_certificate:证书损坏了,包含的签名不能通过验证等。
unsupported_certificate:证书的类型不受支持。
certificate_revoked:证书被签署者撤销。
certificate_expired:证书已过期。证书已过期或当前无效。
certificate_unknown:在处理证书的过程中出现了一些其他(未指明)问题,导致无法接受。
illegal_parameter:握手中的一个字段不正确或与其他字段不一致。该警报用于符合正式协议语法但其他方面不正确的错误。
unknown_ca:收到了有效的证书链或部分证书链,但证书没有被接受,因为无法找到CA证书或无法与已知的trust anchor匹配。
access_denied:收到了一个有效的证书或PSK,但当使用了访问控制,发送者决定不继续协商。
decode_error: 由于某些字段不在指定范围内或消息的长度不正确,消息不能被解码。 该警报用于消息不符合正式协议语法的错误。在适当的实现之间的通信中绝对不应该观察到这个警报,除非消息在网络中被破坏了。
decrypt_error:握手(不是记录层)解密操作失败,包括签名无法验证通过或无法验证Finished消息或PSK binder。
protocol_version: 对端想要协商的协议版本可以识别但不支持(见附录D)。
insufficient_security:当协商失败时,替代handshake_failure返回,具体原因是服务器要求的参数比客户端支持的更安全。
internal_error: 内部错误。 与对端或协议的正确性无关的内部错误(如内存分配失败)使其无法继续。
inappropriate_fallback:由服务器响应客户端无效连接重试而发送(见[RFC7507])。
missing_extension:收到不包含必要扩展的握手消息则发送。
unsupported_extension:接收到包含禁止在给定握手中使用的扩展则发送,或者ServerHello或Certificate中包含任何没有在相应的ClientHello或CertificateRequest中提供的扩展。
unrecognized_name:当客户端通过 server_name扩展提供的名称所标识的服务器不存在时,由服务器发送(见[RFC6066])。
bad_certificate_status_response:当服务器通过status_request扩展提供无效或不可接受的OCSP响应时,由客户端发送(见[RFC6066])。
unknown_psk_identity:当需要建立PSK密钥,但客户端没有提供可接受的PSK标识时,服务器会发送该警报。发送该警报是可选的;服务器可以选择发送decrypt_error警报,以表明无效的PSK标识。
certificate_required:当需要客户端证书,但客户没有提供证书时,由服务器发送。
no_application_protocol:当客户端的application_layer_protocol_negotiation扩展只提供了服务器不支持的协议时,由服务器发送(参见[RFC7301])。

新的警报值由IANA分配,如第11节所述。

7. 加密计算

TLS的握手建立了一个或多个输入secret,组合起来创建实际的工作密钥材料,详见下文。 密钥推导过程包含了输入secret和握手transcript。 需要注意的是,由于握手transcript包含了Hello消息中的随机值,因此任何一次握手都会有不同的流量secret,即使使用了相同的输入secret,就像在多个连接中使用相同的PSK一样。

7.1. Key Schedule

密钥推导过程使用了HKDF定义的HKDF-Extract和HKDF-Expand函数[RFC5869],以及下面定义的函数:

   HKDF-Expand-Label(Secret, Label, Context, Length) =
        HKDF-Expand(Secret, HkdfLabel, Length)

   Where HkdfLabel is specified as:
   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是指握手消息,包括握手消息类型和长度字段,但不包括记录层头。请注意,在某些情况下,一个零长度的Context(用""表示)被传递给HKDF-Expand-Label。本文中提到的标签都是ASCII字符串,不包括尾部的NUL字节。

注意: 对于普通的散列函数,任何超过12字符的标签都需要散列函数额外迭代计算。本规范中的标签都选在这个限制范围内。

密钥是使用HKDF-Extract和Derive-Secret函数从两个输入的secret中导出的。 一般来说,添加一个新secret的模式是使用HKDF-Extract,使用Salt作为当前的secret状态,输入密钥材料(IKM,Input Keying Material)作为新secret添加。在TLS 1.3这个版本中,这两个输入secret是:
 PSK (外部建立的预共享密钥,或从以前连接中的resumption_master_secret值导出)
 (EC)DHE共享secret (7.4节)

这产生了一个完整的密钥推导表,如下图所示。此图使用以下格式约定:
 HKDF-Extract从上取Salt参数,从左取IKM参数,其输出在底部,输出的名称在右侧。
 Derive-Secret的Secret参数用进位箭头表示。例如,Early Secret是生成client_early_traffic_secret的Secret。
 "0"表示一串Hash.length字节设置为0。

0
|
v PSK -> HKDF-Extract = Early Secret
|
±----> 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_master_secret
v
Derive-Secret(., “derived”, “”)
|
v (EC)DHE -> HKDF-Extract = Handshake Secret
|
±----> 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 = Master Secret
|
±----> Derive-Secret(., “c ap traffic”,
| ClientHello…server Finished)
| = client_application_traffic_secret_0
|
±----> Derive-Secret(., “s ap traffic”,
| ClientHello…server Finished)
| = server_application_traffic_secret_0
|
±----> Derive-Secret(., “exp master”,
| ClientHello…server Finished)
| = exporter_master_secret
|
±----> Derive-Secret(., “res master”,
ClientHello…client Finished)
= resumption_master_secret

这里的一般模式是,图中左边显示的secret只是没有上下文的原始熵,而右边显示的secret包括握手上下文,因此可以用来导出工作密钥,而不需要额外的上下文。请注意,对Derive-Secret的不同调用可能会使用不同的Messages参数,即使是相同的secret。 在0-RTT中,Derive-Secret被调用时有四个不同的transcript;在仅1-RTT中,它被调用时有三个不同的transcript。
如果给定的secret不可用,则使用Hash.length字节的0值。 请注意,这并不意味着跳过一轮,所以如果没有使用PSK,Early Secret仍将是HKDF-Extract(0,0)。 对于binder_key的计算,外部PSK(在TLS之外提供的PSK)的标签为ext binder,恢复PSK(作为之前握手的恢复主secret提供的PSK)的标签为res binder。 不同的标签防止了一种PSK被另一种PSK所替代。
有多个潜在的Early Secret值,取决于服务器最终选择的PSK。 客户端需要为每个潜在的PSK计算一个值;如果没有选择PSK,则需要计算对应于零PSK的Early Secret。
一旦计算出所有从某个secret中得出的值,该secret就应该被删除。

7.2. 更新流量Secret

一旦握手完成,任何一方都可以使用4.6.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及其相关的流量密钥。

7.3. 流量秘钥计算
流量密钥材料由以下输入值生成:

  • 一个secret值
  • 表示正在生成的具体值的purpose值
  • 正在生成的密钥的长度

流量密钥材料由输入的流量secret值生成,使用:

   [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
   [sender]_write_iv  = HKDF-Expand-Label(Secret, "iv", "", iv_length)

[sender] 表示发送方。 各记录类型的Secret值如下表所示:

   +-------------------+---------------------------------------+
   | Record Type       | Secret                                |
   +-------------------+---------------------------------------+
   | 0-RTT Application | client_early_traffic_secret           |
   |                   |                                       |
   | Handshake         | [sender]_handshake_traffic_secret     |
   |                   |                                       |
   | Application Data  | [sender]_application_traffic_secret_N |
   +-------------------+---------------------------------------+

All the traffic keying material is recomputed whenever the underlying Secret changes (e.g., when changing from the handshake to Application Data keys or upon a key update).
每当底层Secret变化时,所有的流量密钥材料都会被重新计算(例如,从握手密钥变为应用数据密钥或密钥更新时)。

7.4. (EC)DHE Shared Secret计算

7.4.1. 有限域DH(Finite Field Diffie-Hellman)

对于有限域组,进行传统的Diffie-Hellman[DH76]计算。 协商后的密钥(Z)以大字节序转为字节串,并以0左垫,直到质数的大小。 这个字节串在上面的密钥图中被用作shared secret。
请注意,这种结构与之前的TLS版本不同,后者去掉了前面的零。

7.4.2. 椭圆曲线DH(Elliptic Curve Diffie-Hellman)

对于secp256r1、secp384r1和secp521r1,ECDH计算(包括参数和密钥生成以及shared secret计算)按照[IEEE1363]使用ECKAS-D1H方案进行,以identity map作为密钥派生函数(KDF),因此shared secret是以八位数字符串表示的ECDH shared secret椭圆曲线点的x坐标。需要注意的是,FE2OSP(Field Element to Octet String Conversion Primitive)输出的这个八位数字串(IEEE1363术语中的"Z")对于任何给定字段来说都是恒定长度的;在这个八位数字串中前面的零一定不能被截断。
(请注意,这种身份KDF的使用是一个技术问题。 完整的情况是,ECDH采用了一个重要的KDF,因为TLS除了计算其他secret外,并不直接使用这个secret。)

对于X25519和X448,ECDH的计算方法如下:

  • 放入KeyShareEntry.key_exchange结构的公钥是将ECDH标量乘法函数应用于适当长度的秘钥(放入标量输入)和标准的公共基点(放入u坐标点输入)的结果。
  • ECDH shared secret是将ECDH标量乘法函数应用于秘钥(成标量输入)和对端的公钥(成u坐标点输入)的结果。 输出的结果是不经过任何处理直接使用的。

对于这些曲线,实现者应该使用[RFC7748]中指定的方法来计算Diffie-Hellman shared secret。实现者必须检查计算出的Diffie-Hellman shared secret是否是全零值,如果是,则按照[RFC7748]第6节的描述中止。 如果实现者使用这些椭圆曲线的替代实现,他们必须执行[RFC7748]第7节中规定的附加检查。

7.5. 导出器(Exporter)

[RFC5705]用TLS伪随机函数(PRF, pseudorandom function)定义了TLS的密钥材料导出器。 本文用HKDF替换了PRF,因此需要一个新的结构。 导出器接口保持不变。
导出器的值计算为:

TLS-Exporter(label, context_value, key_length) =
HKDF-Expand-Label(Derive-Secret(Secret, label, “”),
“exporter”, Hash(context_value), key_length)

其中Secret是early_exporter_master_secret或exporter_master_secret。 除非应用程序明确指定,否则必须使用exporter_master_secret。 early_exporter_master_secret定义为用于0-RTT数据需要exporter的设置。 建议为早期exporter定义一个单独的接口,这样可以避免exporter用户在需要常规exporter时意外地使用早期exporter,反之亦然。
如果没有提供上下文,context_value的长度为零。因此,不提供上下文和提供空上下文的计算结果是一样的。 这与以前的TLS版本不同,在以前的版本中,空上下文的输出与没有上下文的输出是不同的。 从本文发布之时起,无论是否有上下文,都不使用分配的exporter标签。 未来的规范不得定义导出器使用空上下文和无上下文使用相同的标签。 导出器的新用途应该在所有导出器计算中提供一个上下文,尽管这个值可以是空的。
对导出器标签格式的要求在[RFC5705]第4节中定义。

8. 0-RTT和抗重放

如2.3节和附录E.5所述,TLS没有为0-RTT数据提供内在重放保护。 有两种潜在的威胁需要关注:

  • 攻击者通过简单复制0-RTT数据进行重放攻击。
  • 攻击者利用客户端重试行为,使服务器接收到一个消息的多个副本。 这种威胁在一定程度上已经存在,因为重视健壮性的客户端会通过重试请求来应对网络错误。 然而,0-RTT为任何不保持全局一致的服务器系统增加了一个额外的维度。 具体来说,如果一个服务器系统有多个区, A区的ticket在B区不被接受,那么攻击者可以将打算用于A区的ClientHello和早期数据复制到A区和B区,在A区,数据将以0-RTT的方式被接受,但在B区,服务器将拒绝0-RTT数据,强制进行完全握手。 如果攻击者阻止了A的ServerHello,那么客户端将与B完成握手,并可能重试请求,导致整个服务器系统重复。

第一类攻击可以通过共享状态来防止,以保证0-RTT数据最多接受一次。 服务器应该通过实施本节所述的方法或通过同等手段提供该级别的重放安全。 然而,我们知道,由于操作上的考虑,并非所有的部署都会将状态保持在该级别。 因此,在正常操作中,客户端将不知道服务器实现了这些机制中的哪一种(如果有的话),因此必须只发送他们认为可以安全重放的早期数据。

除了重放的直接影响外,还有一类攻击,即使是通常被认为是幂等的操作也会被大量重放利用(定时攻击、资源限制耗尽和其他,如附录E.5所述)。 可以通过确保每个0-RTT有效载荷只能重放有限次数来缓解这些攻击。 服务器必须确保任何实例(无论是一台机器、一个线程或相关服务基础设施中的任何其他实体)最多接受一次同一0-RTT握手的0-RTT;这将重播次数限制在部署的服务器实例数量上。 这样的保证可以通过本地记录最近收到的ClientHello的数据并拒绝重复,或者通过任何其他能够提供相同或更强保证的方法来实现。 "每个服务器实例最多一次"的保证是最低要求,服务器应该在可行的情况下进一步限制0-RTT重放。

第二类攻击在TLS层无法防止,必须由应用程序来处理。 需要注意的是,任何应用程序的客户端实现任何类型的重试行为,都需要实现某种反重试防御。

8.1. 单次使用Ticket

最简单的防重放防御方式是服务器只允许每个会话令牌使用一次。 例如,服务器可以维护一个所有未使用的有效令牌的数据库,在使用时从数据库中删除每个令牌。 如果提供了未知的令牌,服务器就会回落到完全握手。

如果令牌不是自带的,而是数据库密钥,相应的PSK在使用时被删除,那么使用PSK建立的连接就享有前向保密性。 这样当只使用PSK不使用(EC)DHE时,就提高了所有0-RTT数据和PSK使用的安全性。

由于这种机制在有多个分布式服务器的环境中,需要在服务器节点之间共享会话数据库,因此与自加密令牌相比,可能很难实现PSK 0-RTT连接的高成功率。 与会话数据库不同的是,即使没有一致的存储,会话令牌也可以成功地进行基于PSK的会话建立,不过当允许0-RTT时,它们仍然需要一致的存储,以便对0-RTT数据进行反重放,详见下节。

8.2. 记录ClientHello

反重放的另一种形式是记录一个从ClientHello中派生出来的唯一值(一般是随机值或PSK binder),并拒绝重复。 记录所有的ClientHello会导致状态无限制地增长,但服务器也可以记录给定时间窗口内的ClientHello,并使用obfuscated_ticket_age来确保ticket在该窗口之外不会被重复使用。

为了实现这一点,当接收到ClientHello时,服务器首先验证PSK binder,如4.2.11所述。然后,按照下一节的描述计算预期的到达时间(expected_arrival_time),如果在记录窗口之外,则拒绝0-RTT,回退到1-RTT握手。

如果expected_arrival_time在窗口内,那么服务器会检查是否记录了一个匹配的ClientHello。 如果找到了,要么用illegal_parameter警告中止握手,要么接受PSK但拒绝0-RTT。 如果没有找到匹配的ClientHello,那么就接受0-RTT,然后将ClientHello存储至expected_arrival_time。 服务器也可以以false positive实现数据存储,例如Bloom过滤器,在这种情况下,必须通过拒绝0-RTT来响应明显的重放,但不得中止握手。

服务器必须只从ClientHello的验证部分导出存储密钥。 如果ClientHello包含多个PSK身份,那么攻击者可以在假设服务器不会验证的情况下,使用不同的binder值为less-preferred的身份创建多个ClientHello(如4.2.11所推荐的)。 比如,如果客户端发送PSK A和B,但服务器更喜欢A,那么攻击者可以改变B的binder,而不影响A的binder,如果B的binder是存储密钥的一部分,那么这个ClientHello不会被当作重复,这将导致ClientHello被接受,并可能导致重放缓存污染等副作用,尽管0-RTT数据无法解密,因为它将使用不同的密钥。 如果使用验证过的binder或ClientHello.random作为存储密钥,那么这种攻击是不可能的。

由于这种机制不需要存储所有未完成的ticket,因此在具有高恢复率和0-RTT的分布式系统中可能更容易实现,但代价是由于难以可靠地存储和检索接收到的ClientHello消息,因此反重放防御可能较弱。 在许多这样的系统中,对所有接收到的ClientHello进行全局一致的存储是不切实际的。 在这种情况下,最好的防重放保护方法是让一个存储区负责某一ticket,并拒绝该ticket在任何其他区的0-RTT。这种方法可以防止攻击者进行简单的重放,因为只有一个区会接受0-RTT数据。 一个较弱的设计是为每个区实现单独的存储,但允许任何区的0-RTT。 这种方法将重放的次数限制在每个区一次。 当然,无论哪种设计,应用消息复制仍然是可能的。
当刚启动时,只要其记录窗口的任何部分与启动时间重叠,就应该拒绝0-RTT。 否则,它们就有可能接受最初在该期间发送的重放。

注意:如果客户端的时钟运行速度比服务器快得多,那么未来可能会收到一个在窗口之外的ClientHello,在这种情况下,它可能会被接受为1-RTT,导致客户端重试,之后再成为0-RTT可接受状态。 这是第8章中描述的第二种攻击形式的另一种变体。

8.3. Freshness检查

因为ClientHello指示了客户端发送的时间,所以可以有效地判断一个ClientHello是否可能是最近合理发送的,对于这样的ClientHello只接受0-RTT,否则回落到1-RTT的握手。 这对于8.2描述的ClientHello存储机制来说是必要的,否则服务器需要存储无限的ClientHello,对于独立的单次使用的ticket来说,这是一个有用的优化,因为可以高效地拒绝不能用于0-RTT的ClientHello。
为了实现这一机制,服务器需要存储服务器生成会话ticket的时间,并加上客户端和服务器之间的往返时间估计。 即:

  adjusted_creation_time = creation_time + estimated_RTT

这个值可以在ticket中编码,从而避免为每个未完成的ticket保留状态。 服务器可以通过从客户端pre_shared_key扩展中的obfuscated_ticket_age参数中减去该ticket的 ticket_age_add值来确定客户端对ticket age的看法。 服务器可以确定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隐藏。 对于互联网上的客户端来说,这意味着十秒左右的窗口,以考虑时钟错误和测量不同;其他部署场景可能有不同的需求。 时钟偏移分布不是对称的,因此最佳的权衡可能涉及到一个不对称的允许偏差。

请注意,仅靠时新性检查不足以防止重放,因为它没有在错误窗口期间检测到重放,而这一窗口 – – 取决于带宽和系统容量 – – 在现实世界中可能包括数十亿次重放。 此外,这种时新性检查只在接收ClientHello时进行,而不是在接收后续早期应用数据记录时进行。 在接受早期数据后,记录可能会在较长的时间内继续流向服务器。

9. 合规性要求

9.1. 强制实现的密码套件

在没有应用配置标准规定的情况下:
符合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]的密钥交换。

9.2. 强制实现的扩展

在没有应用配置标准的情况下,符合TLS标准的应用必须实现以下TLS扩展:

  • 支持的版本 (supported_versions, 4.2.1)
  • Cookie (cookie,4.2.2)
  • 签名算法 (signature_algorithms,4.2.3)
  • 签名算法证书(signature_algorithms_cert,4.2.3)
  • 支持的组(supported_groups,4.2.7)。
  • 共享秘钥(key_share,4.2.8)。
  • 服务器名称(server_name,[RFC6066]第3节)

所有的实现在提供适用功能时必须发送和使用这些扩展:

  • supported_versions对于所有的ClientHello, ServerHello, 和HelloRetryRequest消息是必需的。
  • signature_algorithms对于证书认证来说是必须的。
  • supported_groups对于使用DHE或ECDHE密钥交换的ClientHello消息是必须的。
  • key_share对于使用DHE或ECDHE密钥交换的ClientHello消息是必须的。
  • 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警告,来终止连接。

9.3. 协议 Invariant

本节描述了TLS端点和中间件必须遵循的invariant。 它也适用于TLS的早期版本。

TLS被设计为安全和可兼容的扩展。 新的客户端或服务器,在与新对端通信时,应该协商最优先的通用参数。 TLS握手提供降级保护。中间件在没有终止TLS的情况下,在新的客户端和新的服务器之间传递流量,应该无法影响握手(见附录E.1)。 同时,部署以不同的速度更新,所以新的客户端或服务器可能会继续支持旧的参数,这将允许它与旧的端点互操作。

为了使之工作,实现必须正确处理可扩展字段:

  • 客户端发送的ClientHello必须支持其中所有的参数。 否则,服务器可能会因为选择了其中的一个参数而导致互操作失败。
  • 接收ClientHello的服务器必须正确地忽略所有不认识的密码套件、扩展和其他参数。否则,它可能无法与新的客户端进行互操作。 在TLS 1.3中,接收到CertificateRequest或NewSessionTicket的客户端也必须忽略所有不认识的扩展。
  • 终止TLS连接的中间件必须作为合规的TLS服务器(对原客户端),包括拥有客户端愿意接受的证书,同时也必须作为合规的TLS客户端(对原服务器),包括验证原服务器的证书。 特别是,它必须生成自己的ClientHello,只包含自己理解的参数,它必须生成更新的ServerHello随机值,而不是转发端点的值。

请注意,TLS的协议要求和安全分析只适用于两个单独的连接。 安全部署TLS终结器需要额外的安全考虑,这超出了本文的范围。

  • 转发不理解的ClientHello参数的中间件不得处理该ClientHello以外的任何消息。它必须不加修改地转发所有后续流量。 否则,可能无法与较新的客户端和服务器互通。

转发的ClientHello可能包含中间件不支持的功能通告,因此响应可能包含中间件不识别的未来TLS添加物。 这些附加功能可能会任意改变ClientHello之外的任何消息。 特别是,ServerHello中发送的值可能会改变,ServerHello格式可能会改变,TLSCiphertext格式可能会改变。

TLS 1.3的设计受到了广泛部署的不兼容的TLS中间件的限制(见附录D.4);但是,它并没有放松invariant。 这些中间件仍然是不合规的。

10. 安全考虑

本备忘录中讨论了安全问题,特别是附录C、D和E。

11. IANA考虑

本文使用了几个最初在[RFC4346]中创建并在[RFC8447]中更新的注册表。 IANA已经更新了这些注册表,以引用本文。 这些注册表及其分配策略如下:

  • TLS密码套件注册表:第一个字节在0-254(十进制)范围内的值通过规范要求[RFC8126]分配。 第一个字节为255(十进制)的值保留给私有使用[RFC8126]。

IANA已将附录B.4中所列的密码套件添加到注册表中。Value和Description列取自该表。 对于每个新的密码套件,DTLS-OK和Recommended列都标记为Y。

  • TLS ContentType注册表:未来的值将通过Standards Action [RFC8126]来分配。
  • TLS Alerts注册表:未来的值通过Standards Action [RFC8126]分配。IANA已经用附录B.2 中的值填充了这个注册表。 对于所有这些值,DTLS-OK 列都标记为Y。 标记为_RESERVED的值有描述其以前用途的注释。
  • TLS HandshakeType注册表:未来的值将通过Standards Action [RFC8126]分配。 IANA已经更新了这个注册表,将第4项从NewSessionTicket改名为new_session_ticket,并将附录B.3中的值填充到这个注册表中。 对于所有这些值,DTLS-OK列都标记为Y。 标有_RESERVED 的值有说明其以前或临时使用情况的注释。

本文还使用了最初在[RFC4366]中创建的TLS ExtensionType Values注册表。 IANA已经更新了它来引用这个文档。 该注册表的变化如下:

  • IANA更新了注册策略如下。
    第一个字节在0-254(十进制)范围内的值通过Specification Required [RFC8126]分配。 第一个字节为255(十进制)的值保留给私有使用[RFC8126]。

  • IANA已经更新了这个注册表,以包含key_share、pre_shared_key、psk_key_exchange_modes、early_data、cookie、supported_versions、certificate_authorities、oid_filters、post_handshake_auth 和 signature_algorithms_cert 扩展,其值在本文中定义,Recommended值为Y。

  • IANA已经更新了这个注册表,加入了TLS 1.3列,列出了扩展可能出现的消息。 这一栏最初是根据4.2的表格填写的,任何未列出的扩展名都标为"-",以表示该扩展名未被TLS 1.3使用。

本文更新了TLS Certificate Type注册表中的一个条目,该条目最初创建于[RFC6091],并在[RFC8447]中更新。 IANA更新了值1的条目,名称为OpenPGP_RESERVED,Recommended值为N,注释为"在1.3之前的TLS版本中使用"。

本文更新了最初在[RFC6961]中创建的TLS Certificate Status Type注册表中的一个条目。 IANA更新了值2的条目,名称为ocsp_multi_RESERVED,注释为"在1.3之前的TLS版本中使用"。

本文更新了TLS Supported Groups注册表中的两个条目(由[RFC4492]以不同的名称创建,现在由[RFC8422]维护),并由[RFC7919]和[RFC8447]更新。 值 29 和 30 (x25519 和 x448) 的条目已经更新,也参考了本文。

此外,本文件还定义了两个由IANA维护的新注册表:

  • TLS SignatureScheme注册表:第一个字节在0-253(十进制)范围内的值通过Specification Required [RFC8126]分配。 第一个字节为254或255(十进制)的值保留给私有使用[RFC8126]。 第一个字节在0-6范围内或第二个字节在0-3范围内的值目前没有分配,为向后兼容性而保留。该注册表有一个Recommended列。 该注册表最初是用4.2.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。 除非有明确的要求,否则Recommended列的值为N, Recommended值为Y需要Standards Action [RFC8126]。Y->N的转换需要IESG批准。
  • TLS PskKeyExchangeMode注册表: 0-253(十进制)范围内的值通过Specification Required [RFC8126]分配。254和255(十进制)的值是为私有使用[RFC8126]保留的。 该注册表有一个Recommended列。 该注册表最初被填充为psk_ke (0)和 psk_dhe_ke (1)。 两者都被标记为Recommended。 除非有明确的要求,否则Recommended列的值为N,使用Y的Recommended值需要Standards Action [RFC8126]。 Y->N的转换需要IESG批准。
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值