数据报传输层安全(DTLS)1.2版

1. 介绍

   TLS [TLS]是使用最广泛的网络流量安全协议。它被广泛用于保护网页流量和电子邮件协议如IMAP [IMAP]和POP [POP]。TLS的主要优势是它提供了一个透明的有向通道。因此,它容易通过在应用侧鞥和传输层插入TLS来保护应用层协议。然而,TLS必须运行与一个可靠的传输信道 -- 典型的是TCP [TCP]。因此,它不能被用于保护不可靠的数据报流量。

   使用UDP传输的应用层协议的数量在增加。特别是像会话发起协议(SIP)[SIP]和电子游戏协议这样的协议越发流行。(需要注意的是SIP能既能运行于TCP也能运行于UDP之上,但有一些情况用UDP更合适)当前,这些应用的设计者面临着大量的不能令人满意的选择。首先,他们可以使用IPsec [RFC4301]。然而,由于在[WHYIPSEC]中详细列出的原因,这个方案只能对一些应用适用。第二,他们可以设计一个定制的应用层安全协议。不幸的是,虽然应用层安全协议通常提供较好的安全特性(例如,在S/MIME应用场景中的端到端安全),他们通常需要做大量的工作进行设计 -- 相对比将协议运行在TLS上需要相对小的工作量。

   在很多情况下,保证client/server应用安全的最好方法是使用TLS;然而,数据报语义的需求自动禁止了使用TLS。本文描述了一种协议来实现这种给你:数据报传输层安全(DTLS)。DTLS是特意设计成尽可能与TLS相似,这样既能最小化新的安全发明也能最大化重用代码和基础设施。

   DTLS 1.0 [DTLS1]最初被定义为[TLS11]的一个增量。本文介绍了DTLS的一个新版本,DTLS 1.2,它被定义为TLS 1.2 [TLS12]的一个增量系列。没有DTLS 1.1;这个版本被跳过以便与TLS的版本号相协调。这个版本也会澄清一些与DTLS 1.0的规范相混淆的点。

   同时支持DTLS 1.2和DTLS的实现能与声明支持只支持DTLS 1.0的协议互操作 (当然要使用DTLS 1.0), 正如TLS 1.2的实现能与之前的TLS版本(详见[TLS12]的Appendix E.1)互操作一样。例外的是没有SSLv2和SSLv3的DTLS,所以对这些协议并没有应用后向兼容。

1.1. 需求术语

   略。

2. 使用模式

   DTLS协议被设计用来保证应用通信数据的安全。它被设计在应用层运行,不需要对内核做任何修改。

   数据报传输不要求或提供可靠的或有序的数据传输。DTLS协议为负载数据保留了这特性。诸如流媒体,网络电话和在线游戏这样的应用使用数据报进行通信本质上是由于对传输数据的延迟敏感。当使用DTLS进行安全通信时这类应用的行为不会改变,因为DTLS协议不会补偿丢失或重排序数据流量。

3. DTLS概览

   设计DTLS的基本思想是构建“数据报传输之上的TLS”。TLS不能直接用于数据报环境的原因只是包可能会丢失或重排序。TLS没有内部机制能处理这种不可靠性;因此,当移植到数据报传输上之后TLS实现就会崩溃。DTLS的目的是对TLS做最小的变更来解决这个问题。最大程度的可能是DTLS与TLS相同。无论何时我们需要发明新的机制时,我们都会试图保留TLS的风格。

   不可靠性对TLS产生了两个方面的问题:

      1. TLS不运行对单个记录进行独立解密。因为完整性检查依赖于序列号,如果记录N没有收到,对记录N+1的完整性检查会基于错误的序列号进行从而会失败。(需要注意的是对于TLS 1.1之前的版本没有显式IV,而且这样解密也会错误。——

      2. TLS握手层假定握手消息是可靠投递的,如果消息丢失则会中断。

   本节的其余部分描述了DTLS用来解决这些问题的方法。

3.1. 丢失不敏感报文发送

   在TLS的流量加密层(被称为TLS记录层),记录并不是独立的。有两种记录间依赖:
      1. 密码学上下文(流密码密钥流)在记录间保留。
      2. 抗重放和消息乱序保护由一个包含了序列号的MAC来提供,但序列号在记录中是隐藏的。

   DTLS通过禁止流密码解决了第一个问题。DTLS通过增加显式序列号解决了第二个问题。

3.2. 为握手提供可靠性

   TLS握手是一个古板的密码学握手。消息必须以确定的顺序被传输和接收;任何其它的顺序都是错误的。很明显,这与重排序和消息丢失不兼容。此外,TLS握手消息潜在地比任何给定的数据报大,因此会产生IP分片问题。DTLS必须解决这些问题。

3.2.1. 包丢失

   DTLS使用了一个简单的重传定时器来处理包丢失。下图使用了DTLS握手的第一阶段表明基本流程:

         Client                                   Server
         ------                                   ------
         ClientHello           ------>

                                 X<-- HelloVerifyRequest
                                                  (lost)

         [Timer Expires]

         ClientHello           ------>
         (retransmit)

   一旦client传输了ClientHello消息,它会期望看到从server发来的一个 HelloVerifyRequest。然而,如果这个server的消息丢失了,client就会知道ClientHello或者HelloVerifyRequest已经丢失,然后重传。当server收到重传包时,它就会知道重传发生了。Server也维持了一个重传定时器,当其超时后会重传。
   需要注意的是超时和重传并不应用于HelloVerifyRequest,因为这需要在server上创建状态。HelloVerifyRequest被设计的足够小以避免自身被分片,因此可以不用考虑多个HelloVerifyRequest交叉的情况。

3.2.2. 重排序

   在DTLS中,每个握手消息会在握手中被分配一个明确的序列号。当一个对端收到一个握手消息时,它能快速确定这个消息是否是它所期望接收的下一个消息。如果是,它会进行处理。如果不是,它会将其放入队列中,在将来一旦所有缺失的消息都收到后再进行处理。

3.2.3. 消息大小

   TLS和DTLS握手消息可能会相当大(理论上可以达到的上限是2^24-1字节,实际上是很多个K字节);相比值之下,UDP报文通常被限制在小于1500字节,如果不想要
分片的话。为了补偿这个限制,每个DTLS握手消息可以在几个DTLS记录中进行分片,每个DTLS记录都会试图适应一个单个IP数据报。每个DTLS握手消息包含一个分片偏移和分片长度。因此,一个拥有一个握手消息所有字节的接受者能够重组原始的未分片消息。

3.3. 重放保护

   DTLS选择性地支持记录重放探测。所使用的技术与IPsec AH/ESP相同,是通过维护接收记录的一个位图窗口实现的。因为太老而不适合窗口的记录和以前接收过的记录都会被悄悄地丢弃。重放探测特性是可选的,因为包的重复并不总是恶意的,可能会因为路由错误而出现。应用可以可信地探测重复报文并相应地修改它们的数据重传策略。

4. 与TLS的差异

    正如第3节所说,DTLS本质上与TLS十分相似。因此,我们把DTLS作为TLS 1.2 [TLS12]的一个补充,而非作为一个新的协议提出。在我们没有显式指出差异的地方,DTLS与[TLS12]相同。

4.1 记录层  

DTLS记录层与TLS 1.2及其相似。唯一的变化是在记录中包含了一个显式序列号。这个序列号允许接收者正确地验证TLS MAC。DTLS记录格式显示如下:

      struct {
           ContentType type;
           ProtocolVersion version;
           uint16 epoch;                                    // New field
           uint48 sequence_number;                          // New field
           uint16 length;
           opaque fragment[DTLSPlaintext.length];
         } DTLSPlaintext;

   类型
      与TLS 1.2记录层的type域相同。

   version
      协议所使用的版本。本文描述了DTLS 1.2版本,其使用的版本是{ 254, 253 }。254.253的版本值是DTLS版本1.2的第一个补码。TLS和DTLS版本号之间的最大化空间保证了两个协议的记录能够很容易地区分。需要注意的是将来DTLS发送到网络上的版本在数值上是递减的(而其真实的值是递增的)。

   epoch
      一个计数值,每次密码状态变化都会增加。

   sequence_number
      本记录的序列号。

   length
      与TLS 1.2记录层的length域相同。如TLS 1.2中一样,这个长度不超过2^14

   fragment
      与TLS 1.2记录层的fragment域相同。

   DTLS使用了一个显式序列号,而不是一个隐式的,这个序列号位于记录中的sequence_number域。对于每个epoch序列号都单独维护,对于每个epoch每个sequence_number都会被初始化为0.例如,如果一个epoch为0的握手消息被重传了,它可能会拥有一个在一个epoch 1的消息之后的序列号,即使epoch 1的消息被首先重传。需要注意的是在握手阶段需要多加关注以确保重传消息使用了正确的epoch和密钥材料。

   如果多次握手是密切连续进行,可能会在线路上产生多个相同序列号但不同密码状态的记录。Epoch域允许接收者区别这样的包。Epoch号初始化为0且在每次发送一个ChangeCipherSpec时增长。为了确保任何给定的序列号/epoch对是独特的,实现上必须不能允许相同的epoch值在两个TCP最大报文段生命周期被重用。实际上,TLS实现很少重新握手;因此我们不期望这会成为一个问题。


   需要注意的是由于DTLS记录可以被重排序,一个来自epoch 1的记录可能会在eopch 2开始后被收到。通常,实现上应该丢弃更早epoch的包,但如果包丢失会导致需要引起注意的问题,实现上可以选择保留以前epoch中的密钥材料直到TCP [TCP]所指定的默认最大报文段生存时间(MSL),以允许包的重新排序。(需要注意的是此处的意图是实现者使用IETF当前关于MSL的指导,而不是让实现者尝试获取系统TCP协议栈正在使用的数值)。握手完成后,实现必须接受来自旧的epoch的包。

   相反,对于由最新协商的上下文保护的记录,在一个握手完成之前被接受是可能的。例如,server可以发送它的Finished消息,然后开始传输数据。实现上可以缓存或者丢弃这样的包,虽然当DTLS运行于可靠传输协议(如:SCTP)之上时,这些消息应该被缓存,一旦握手完成就应该被处理。需要注意的是TLS对何时发送数据包的限制仍然有效,接收者会将它们做为按照正确顺序发送的包一样对待。特别地,在完成第一个模式之前发送数据仍然是不允许的。

   需要注意的是在一个现存连接的重握手的特殊情况下,立即处理数据包会是安全的,即使ChangeCipherSpec或Finished消息没有被收到。这种情况是或者重握手消息恢复现存会话或使用与现存连接完全相同的安全参数。在任意其它情况下,实现上必须等待接收Finished消息以阻止降级攻击。

   在TLS中,实现上必须抛弃一个连接或在允许序列号回绕之前重握手。相似地,实现上必须不能允许eopch回绕,但作为替代必须建立一个新的连接,像4.2.8节描述的那样终止旧的连接。在实践上,实现上很少在一条通道上重复进行重握手,所以这不太可能会成为一个问题。

4.1.1. 传输层映射

   每个DTLS记录必须能够放入单个数据报内。为了避免IP分片,DTLS记录层的client应该尝试改变记录的大小以使其能适应从记录层获得的任何路径MTU的估算值。
   需要注意的是与IPsec不同,DTLS记录不包含任何连接标识符。应用必须在多个连接之间实现标志多路复用。在使用UDP时,这个功能很可能由host/port号来实现。

   多个DTLS记录可以被放入单个数据报内。它们会被简单地进行连续编码。DTLS记录框架足以确定边界。需要注意的是,数据报负载的第一个字节必须是一个记录的开头。记录可能不会跨越数据报。

   一些传输层协议,如DCCP [DCCP]会提供他们自己的序列号。当运行在这些传输层协议之上时,DTLS和传输层序列号都会出现。虽然这样导致了少量的效率损失,但传输层和DTLS序列号的作用是不同的。因此,为了概念的简单性,DTLS会优先使用这两种序列号。在将来,DTLS的扩展可能会确定只允许使用一种序列号用于在受限的环境中部署。

   一些传输层协议,如DCCP,为它们所承载的流量提供了拥塞控制。如果拥塞窗口足够小,DTLS握手的重传可能会阻塞而不是立即传输,潜在地导致超时和假重传。当DTLS被用于这类传输层协议时,必须多加小心不要超过可能的拥塞窗口。[DCCPDTLS]定义了一个DTLS到DCCP的映射,已将这些问题考虑在内。

4.1.1.1. 路径MTU问题

  通常,DTLS的哲学是将PMTU发现留给应用。然而,基于以下原因DTLS不能完全忽略PMTU:
   -  DTLS记录结构扩展了数据报大小,因此从应用的角度看会降低PMTU的效率。
   -  在一些实现中,应用可能不会直接与网络进行交互,这种情况下DTLS协议栈可以In some implementations, the application may not directly talk to
      the network, in which case the DTLS stack may 使用ICMP [RFC1191] "数据报太大" 标识或ICMPv6 [RFC4443] "包太大"标识。
   -  DTLS握手消息可以超过PMTU。

   为了处理前两个问题,DTSL记录层应该按照如下描述进行处理。

   如果PMTU估计值能从其使用的传输层协议获取,他们也从能被上层协议处理。特别地:

   -  对于DTLS over UDP, 上层协议应该被允许获取IP层维护的PMTU估计值。

   -  对于DTLS over DCCP, 上层协议应该被允许获取当前的的PMTU估计值。

   -  对于DTLS over TCP或SCTP, 它们会自动分配和重组报文,没有PMTU限制。然而,上层协议写任何记录都不能超过最大记录大小2^14字节。

   DTLS记录层应允许上层协议发现DTLS处理所期望的记录扩展的数量。需要注意的是这个数量仅仅是一个估计,因为块填充或可能使用DTLS压缩。

   如果有一个传输层协议表明(或者通过ICMP协议或者通过一个拒绝消息;按照第14章中的[DCCP]),然后DTLS记录层必须告知处理错误的上层协议。

   DTLS记录层不应该与上层协议进行交互来实现PMTU发现(通过RFC1191]或[RFC4821])。特别地:

   -  在下面的传输层协议中允许这样做, 上层协议应该被允许设置DF位的状态(在IPv4中) 或者禁止本地分配(在IPv6中)。

   -  如果下面的传输层协议允许应用层请求PMTU探测(例如,DCCP),DTLS记录层应该尊重这种请求。

   最后的问题是DTLS握手协议。从DTLS记录层的角度考虑,这仅仅是另外一种上层协议。然而,DTLS握手并不频繁发生而且仅产生了一点往返时延;因此,握手协议对PMTU的处理是在精确的PMTU发现之上设置了一个快速完成的保险机制。为了允许在这些条件下建立连接,DTLS实现应该遵循以下规则:
   
   -  如果DTLS记录层告知DTLS握手层一个消息太大了,它应该立即尝试使用任何关于PMTU的信息对其进行分片。

   -  如果重复的重传没有得到响应,且PMTU未知,随后的重传应该回退到一个更小的记录尺寸,合适地对握手消息进行分片。这个标准没有指定一个精确的回退之前尝试重传的次数,但2-3次看起来合适。

4.1.2. 记录负载保护

   像TLS一样,Like TLS, DTLS将数据作为被保护记录的一个序列进行传输。本节的其余部分会描述这个格式的细节。

4.1.2.1. MAC

    DTLS MAC与TLS1.2的相同。然而,不同于TLS使用隐式序列号,用于计算MAC的序列号是64位的值,是由epoch和数据出现在线路上的序列号级联而成。需要注意的是DTLS epoch + 序列号与TLS序列号的长度相同。

    TLS MAC计算在协议版本号上是参数化的。对于DTLS,是线路上的版本,如:对于TLS 1.2是{254,253}。

    需要注意的是DTLS与TLS MAC处理方法的一个重要差别是在TLS中,MAC错误必须导致连接中断。对于DTLS,接收的实现可以简单地丢弃不想要的记录然后继续维持连接。这个变化可能是由于DTLS记录并不像TLS的记录那样彼此依赖。
    通常,DTLS实现上应该默默丢弃MAC错误或其它部分无效的记录。它可以将错误信息记录成一条日志。如果一个DTLS实现在收到一个MAC无效的消息时选择生成一个警报,它必须生成一个bad_record_mac alert。

    需要注意的是DTLS与TLS MAC处理方法的一个重要差别是在TLS中,MAC错误必须导致连接中断。对于DTLS,接收的实现可以简单地丢弃不想要的记录然后继续维持连接。这个变化可能是由于DTLS记录并不像TLS的记录那样彼此依赖。
    通常,DTLS实现上应该默默丢弃MAC错误或其它部分无效的记录。它可以将错误记录一条日志。如果一个DTLS实现在收到一个MAC无效的消息时选择生成一个警报,它必须生成一个fatal级别的bad_record_mac alert并且中止它的连接。需要注意的是由于错误并不会导致连接中止,DTLS栈是比TLS栈更高效的错误类型显示(oracles)。因此,尤其重要的是TLS12 6.2.3.2节的建议应该被遵循。

4.1.2.2. 空或标准流密码

   DTLS空密码的作用与TLS 1.2的空密码完全相同
   在TLS 1.2中描述的唯一一个流密码是RC4, 它不能被随机访问。RC4不能用于DTLS。

4.1.2.3.  块密码

   DTLS块密码加密和解密的操作与TLS 1.2完全相同。

4.1.2.4.  带额外数据的认证加密

   TLS 1.2介绍了带额外数据的认证加密(AEAD)密码族。现存的AEAD密码族(在[ECCGCM]和[RSAGCM]中定义,能够像TLS 1.2中的那样被用于DTLS。

4.1.2.5. 新密码族

   在注册时,新的密码族必须标明它们是否适用于DTLS,并且应当做何种适配(如果有)。见第7节 IANA的考虑。

4.1.2.6. 抗重放

    DTLS记录包含一个序列号以提供重放保护。序列号的校验应遵循下面的滑动窗口流程,借鉴自3.4.3节。
会话的接收报文的计数必须在会话建立时被初始化为0.对于每个已接收到的记录,接收者必须验证记录中包含的序列号没有与在这个会话的生命周期内接收到的任何其它记录的序列号重复。这应该是在与会话匹配后应用于数据包的第一个检查,以加快对重复记录的拒绝。

    丢弃重复数据通过一个滑动接收窗口来实现(这个窗口如何实现是一个局部的事情,但下面的文字描述了实现上必须展示的功能)。必须支持最小为32的窗口大小,但大小是64的窗口更被推荐且应该被设置为默认值。其它的窗口大小(大于最小值)可以由接收者选择。(接收者不能告知发送者窗口的大小)

    窗口的右边缘代表着当前会话接收到的最高有效序列号的值。序列号比窗口左边缘低的记录会被丢弃。落入窗口范围内的包会依据在窗口中接收到的包的列表进行检查。执行这种检查的一个有效的方法(基于使用位掩码)在3.4.3节中描述。

    如果接收到的记录落入窗口中且是新的,或者包处于窗口的右边缘,接收者会进行MAC验证。如果MAC验证失败,接收者必须丢弃接收到的非法记录。仅当MAC验证成功时接收窗口才能更新。

4.1.2.7. 处理非法记录

    与TLS不同,在面对非法记录时DTLS更容易恢复(例如:非法的格式,长度,MAC等等)。通常,非法的记录应该被静悄悄地丢弃,因此保持了当前连接。然而,一个错误可以被记录日志用于诊断。实现上如果选择了生成一个警报来替代日志,则必须产生fatal级别的警报以避免攻击者重复探测实现来观察其对各种错误类型的反应。需要注意的是如果DTLS运行在UDP之上,任何执行这个操作(生成警报)的实现都极易遭受拒绝服务(DoS)攻击,因为伪造UDP报文十分容易。因此,这个功能并不推荐应用于这样的传输层协议。

    如果DTLS是被一个抗伪造的传输层协议(例如,带SCTP-AUTH的SCTP)所承载,则发送警报会更安全因为一个攻击者很难伪造一个不会被传输层丢弃的报文。

4.2. DTLS握手协议

    DTLS使用的所有握手消息与TLS相同,但有3个主要的区别:
1.增加了一个无状态的cookie交换以防止拒绝服务攻击。
2.修改了握手报文头以处理消息丢失、乱序和DTLS消息分片(为了避免IP分片)
3.设置了重传定时器以处理消息丢失。

    除了上述区别,DLS消息格式,流,和逻辑与TLS 1.2一样。

4.2.1.拒绝服务的对策

    数据报安全协议极容易遭受各种DoS攻击。有两种攻击尤其需要考虑:
    1. 一个攻击者可以传送一系列握手初始化请求给server,导致server分配资源且有可能执行高资源消耗的密码学操作,从而导致server消耗大量的资源。
    2. 一个攻击者可以发送使用受害者源地址伪造的连接初始化消息给server,这样就能将server用作放大器。Server随后会发送下一条消息(在DTLS中,一个证书消息可能相当大)给受害者的机器,从而导致泛洪攻击。

    为了应对者两种攻击,TLS借鉴了由Photuris [PHOTURIS] 和IKE[IKEv2]所使用的无状态cookie技术。当client发送它的ClientHello消息给server时,server可以用HelloVerifyRequest消息来响应。这个消息包含了一个由[PHOTURIS]技术生成的无状态cookie。Client必须重传添加这个cookie的ClientHello消息。然后server验证cookie,仅当cookie有效时才会继续进行握手处理。这个机制强迫攻击者/client能够接收cookie,这样会使得使用虚假IP地址的DoS攻击难以进行。这个机制不能防御从用合法IP地址进行的DoS攻击。

    交互流程显示如下:
      Client                                   Server
      ------                                   ------
      ClientHello           ------>

                            <----- HelloVerifyRequest
                                   (contains cookie)

      ClientHello           ------>
      (with cookie)

      [握手的其余流程]

    由此DTLS修改了ClientHello消息来添加cookie。
struct {
     ProtocolVersion client_version;
     Random random;
     SessionID session_id;
     opaque cookie<0..2^8-1>;                             // New field
     CipherSuite cipher_suites<2..2^16-1>;
           CompressionMethod compression_methods<1..2^8-1>;
   } ClientHello;

    当我们发送第一个ClientHello消息时,client还没有cookie;这种情况下,cookie域留空(0长度)。

    HelloVerifyRequest的定义如下:
struct {
     ProtocolVersion server_version;
     opaque cookie<0..2^8-1>;
   } HelloVerifyRequest;

    HelloVerifyRequest消息的类型是hello_verify_request(3)。

    Server_version域与TLS中的语法结构一样。然而,为了避免在初始握手过程中进行版本号的协商,DTLS 1.2 server实现应当使用DTLS 1.0版本而不理会TLS期望协商的版本号是什么。DTLS 1.2和1.0 client必须仅能将版本号用于显示包的格式(这对于DTLS 1.2和1.0是相同的),不能作为版本协商的一部分。特别地,DTLS 1.2 client必须不能假定由于server在HelloVerifyRequest中使用了版本1则server不会是DTLS 1.2或最终一定协商出 DTLS 1.0而不是DTLS 1.2。

    当响应一个HelloVerifyRequest时,client必须使用与原始的ClientHello相同的参数值(版本,随机数,会话ID,密码族,压缩算法)。Server应该使用这些值来生成它的cookie且使用收到的cookie来验证这些值是否正确。Server必须使用与HelloVerifyRequest中使用的相同的版本号来发送一个ServerHello。在收到ServerHello时,client必须验证server版本号是否匹配。为了避免序列号在多个HelloVerifyRequest中重复,server必须将ClientHello中的记录序列号用作HelloVerifyRequest中的记录序列号。
注:本规范将cookie的大小增加到255字节以增加将来的灵活性。对于以前版本的DTLS仍限制在32.

    DTLS Server应该以一种方式来生成cookie,以至于它们可以不在server端保存任何每client的状态的情况下也可以被验证。一种技术是用一个随机产生的机密数据并用如下的方式产生cookie:
    Cookie = HMAC(Secret, Client-IP, Client-Parameters)

    当收到第二个ClientHello时,server可以验证Cookie是否合法,client能够基于设定的IP接收数据包。为了避免序列号在交换多个cookie时重复,server必须将ClientHello中的记录序列号用作它发起的ServerHello中的记录序列号。随后ServerHello仅在sever产生了状态之后才能发生,且必须正常递增。

    对于这个方案的一个潜在的攻击是攻击者收集大量来自不同地址的cookie然后重新使用它们来攻击server。Server可以通过频繁改变cookie的值从而使这些cookie失效来防御这种攻击。如果server希望合法的client能通过这个改变过程实现握手(例如,它们接收到了一个带有机密1的cookie然后在server改变为机密2之后发送第二个ClientHello),server可以限制它接受这两个机密的窗口大小。[IKEv2]建议添加一个版本号到cookie中以检测这种情况。一个另外的方法是简单地带着这两个机密进行验证。

    DTLS Server应该在任何时候执行握手时进行cookie的交互。如果server在一个扩大不成问题的环境中被操作,server可以被配置为不执行cookie交换。然而默认是执行交换。此外,server可以选择当一个会话被恢复后不执行cookie交换。Client必须准备好在任何握手中执行cookie交换。

    如果使用了HelloVerifyRequest,初始的ClientHello和HelloVerifyRequest不会被包含在handshake_message(包含CertificateVerify消息)和verify_data(对应Finished消息)的计算中。

    如果一个server收到了一个带有无效cookie的ClientHello消息,它应当将其当做无cookie的ClientHello消息处理。这样避免了竞争/死锁的条件,如果client某种程度上获得了一个坏的cookie(例如,由于server改变了cookie的签名密钥)。

    实现者需要注意:这可能会导致client接收到多个带有不同cookie的HelloVerifyRequest消息。Client应当这样处理:发送一个带有一个cookie新的ClientHello以响应新的HelloVerifyRequest。

4.2.2. 握手消息格式

    为了支持消息丢失,乱序和消息分片,DTLS修改了TLS 1.2握手头:
   struct {
     HandshakeType msg_type;
     uint24 length;
     uint16 message_seq;                               // New field
     uint24 fragment_offset;                           // New field
     uint24 fragment_length;                           // New field
     select (HandshakeType) {
       case hello_request: HelloRequest;
       case client_hello:  ClientHello;
       case hello_verify_request: HelloVerifyRequest;  // New type
       case server_hello:  ServerHello;
       case certificate:Certificate;
       case server_key_exchange: ServerKeyExchange;
       case certificate_request: CertificateRequest;
       case server_hello_done:ServerHelloDone;
       case certificate_verify:  CertificateVerify;
       case client_key_exchange: ClientKeyExchange;
       case finished: Finished;
     } body;
   } Handshake;

    两边在每次握手时传输的第一个消息message_seq一直是0.无论何时每个新消息被生成时,message_seq的值都会加1.需要注意的是在重握手的情况,这意味着HelloRequest将会设置message_seq为0,ServerHello的message_seq为1.当一个消息被重传时,会使用相同的message_seq。例如:
         Client                             Server
         ------                             ------
         ClientHello (seq=0)  ------>

                                 X<-- HelloVerifyRequest (seq=0)
                                                 (lost)

         [Timer Expires]

         ClientHello (seq=0)  ------>
         (retransmit)

                              <------ HelloVerifyRequest (seq=0)

         ClientHello (seq=1)  ------>
         (with cookie)

                              <------        ServerHello (seq=1)
                              <------        Certificate (seq=2)
                              <------    ServerHelloDone (seq=3)

         [Rest of handshake]

    注意,然而,从DTLS记录层的角度来看,重传是一个新的记录。这个记录将会有一个新的DTLSPlaintext.sequence_number值。

    DTLS实现维持(至少是想象的)一个next_receive_seq计数器。这个计数器初始化为0.当接收到一个消息时,如果序列号与next_receive_seq匹配,则next_receive_seq递增并且消息被处理。如果序列号小于next_receive_seq,消息必须被丢弃。如果序列号大于next_receive_seq,实现上应当将这个消息放入队列但可以丢弃。(这是一个简单的空间/带宽折衷)。

4.2.3. 握手消息分片与重组

    正如4.1.1节中所记,每个DTLS消息必须在单个传输层报文内。然而,握手消息极可能比最大记录大小大。因此,DTLS提供另一种机制用于将一个握手消息分片为多个记录,每个记录可以被分别传送,从而避免了IP分片。

    当传输握手消息时,发送者将消息分为N个连续的数据序列。这些序列不能大于最大握手分片大小且必须共同地包含整个握手消息。这些序列不应重叠。发送者随后产生了N个握手消息,它们都拥有与原始的握手消息相同的message_seq。每个新消息都由fragment_offset(前一个分片包含的字节数)和fragment_length(这个分片的长度)来标识。所有消息的length域都与原始的消息相同。一个未分片的消息是一种退化的情况:fragment_offst=0且fragment_length=length。

    当一个DTLS实现收到一个握手分片时,它必须缓存它直到它收到整个握手消息。DTLS实现必须能够处理重叠的分片序列。这允许发送者在PMTU估计值发生变化时使用更小的分片大小重传握手消息。

    需要注意的是与TLS相同,多个握手消息可以被放置于相同的DTLS记录中,条件是有空间且它们是一次传送的一部分。因此,有两种方式可以将两个DTLS消息打包到相同的报文中:在相同的记录中或在分开的记录中。

4.2.4. 超时和重传

    DTLS消息可以组入到一系列消息传送(Flight)中,如下图所显示的。虽然每次消息的传送可能有多个消息组成,但从超时和重传的角度它们应当被看做是整体的。
Client                                          Server
   ------                                          ------

   ClientHello             -------->                           Flight 1

                           <-------    HelloVerifyRequest      Flight 2

   ClientHello             -------->                           Flight 3

                                              ServerHello    \
                                             Certificate*     \
                                       ServerKeyExchange*      Flight 4
                                      CertificateRequest*     /
                           <--------      ServerHelloDone    /

   Certificate*                                              \
   ClientKeyExchange                                          \
   CertificateVerify*                                          Flight 5
   [ChangeCipherSpec]                                         /
   Finished                -------->                         /

                                       [ChangeCipherSpec]    \ Flight 6
                           <--------             Finished    /

               Figure 1. 整个握手的消息传送


   Client                                           Server
   ------                                           ------

   ClientHello             -------->                          Flight 1

                                              ServerHello    \
                                       [ChangeCipherSpec]     Flight 2
                            <--------             Finished    /

   [ChangeCipherSpec]                                         \Flight 3
   Finished                 -------->                         /

         Figure 2. 会话恢复握手的消息传送(无Cookie交换)

    DTLS使用了基于下面的状态机的一个简单的超时和重传机制。由于DTLS client发送第一个消息(ClientHello),它们从PREPARING状态开始。DTLS Server在WAITING状态开始,但缓冲区为空且无重传定时器。


          Figure 3. DTLS超时和重传状态机

    这个状态机有三个基本状态。

    在PREPARING状态,实现上会做所有备消息的内容所必须的计算。然后它会将它们缓存起来用于传输(首先将缓存清空)并进入SENDING状态。

    在SENDING状态,实现会传输缓存的消息。一旦消息发送出去了,如果是握手的最后一个消息实现就会进入FINISHED状态。或者,如果实现希望收到更多消息,它会设置一个重传定时器然后进入WAITING状态。

    有三种方式退出WAITING状态:

    1.重传定时器超时:实现转换到SENDING状态,在那里它重传已发送的包,重置重传定时器,然后回到WAITING状态。
    2.实现从对端读取了一个重传的包:实现转换到SENDING状态,在那里它重传已发送的包,重置重传定时器,然后回到WAITING状态。这里的理论是收到一个重复的包可能是对端的定时器超时所导致,因此标明以前发送的包的一部分已经丢失。
    3.实现收到了下一个发送的消息:如果这是最后一个消息,实现转换到FINISHED。如果实现需要发送一个新的包,它转换到PREPARING状态。部分读取(或者是部分消息或者只是已发送消息中的一些)不好导致状态机转换或定时器重置。

    由于DTLS client发送了第一个消息(ClientHello),它们开始于PREPARING状态。DTLS Server开始于WAITING状态,但缓存为空且没有重传定时器。

    当server需要重新握手时,它从FINISHED状态转换到PREPARING状态以传送HelloRequest。当client收到一个HelloRequest时,它从FINISHED状态转换到PREPARING状态以传送ClientHello。

    此外,在FINISHED状态达到至少两个默认的TCP MSL时间时,传输最后一个报文的节点(处于普通握手过程中的server或处于握手恢复过程中的client)必须重传最后一个报文来响应对端最后一个报文的重传。这样可以避免当最后一个报文丢失时产生死锁。这个要求也适用于DTLS 1.0,虽然在[DTLS1]中没有明确要求,但对状态机能正常工作的要求一直都有。如果要了解为什么这样是必要的,考虑下在一个通常的握手过程中如果server的结束消息丢失:server相信握手完成了但实际上没有。在client等待结束消息时,client的重传定时器会启动并且会重传client的结束消息。这将会导致server用自己的结束消息来响应,完成握手。同样的逻辑也适用于恢复握手过程中的server端。

    需要注意的是由于报文丢失,一端可能会发送应用数据即使对端并没有收到它的结束消息。实现上必须或者丢弃或者缓存所有用于新epoch的应用数据报文直到它们收到这个epoch的结束消息。实现上可以将收到应用数据的epoch在其对应的结束消息的epoch之前看做是乱序或包丢失的证据,立即重传它们的最后一个报文,从而简化重传定时器。

4.2.4.1. 定时器数值

    虽然定时器的数值是实现上的选择,但定时器的错误处理能导致严重的拥塞问题;例如,如果一个DTLS的很多实例过早超时并在一条拥塞的链路上过快重传。实现上应该使用1秒作为定时器初始值(最小值定义于RFC 6298[RFC6298]中)每次重传使该值加倍,直到不少于RFC 6298的最大60秒。需要注意的是我们推荐1秒的定时器而不是3秒的RFC 6298默认值以提升对时间敏感应用的延迟性能。由于DTLS只对握手使用重传而对数据流不使用,对拥塞的影响应该会很小。

    实现应该维持当前定时器的值直到出现没有丢失的传输,这时这个值应当重置为初始值。在经过一个长的空闲周期(不少于10倍的当前定时器数值)后,实现可以将定时器重置为初始值。可能发生这种情况的一种场景是在重要的数据传输之后产生了一次重握手。

4.2.5. ChangeCipherSpec

    在TLS中,ChangeCipherSpec消息从技术上并非是一个握手消息,但出于超时和重传考虑,必须将其被作为相关Finished消息的一部分来进行处理。这样会产生一个潜在的模糊性,是因为ChangeCipherSpec不能明确地建立与在消息丢失的情况下的握手消息之间的顺序。

    这对于当前的TLS模式并不是问题,因为逻辑上在ChangeCipherSpec之前的期望的握手消息集从握手的其余状态中就能预测出来。然而,将来的模式必须小心以避免产生模糊性。

4.2.6 CertificateVerify和Finished消息

    CertificateVerify和Finished消息的格式与TLS中的相同。Hash计算包含了整个握手消息,包括DTLS-specific域:message_seq,fragment_offset和fragment_length。然而,为了消除对握手消息分片的敏感,Finished MAC必须在将每个握手消息当做都在一个分片中发送出去的情况下进行计算。需要注意的是当使用了cookie交换时,初始的ClientHello和HelloVerifyRequest必须不能被包含在CertificateVerify或Finished的MAC计算中。

4.2.7 警报消息

    需要注意的是警报消息完全不能重传,即使它们在握手的过程中出现。然而,一个能够发起一个警报消息的DTLS实现在再次收到不合法的记录(例如,一个重传的握手消息)时应该产生一个新的警报消息。实现应该探测到一个对端持续发送非法消息并且在这种非法行为被探测到之后中止本地连接。

4.2.8.用现存参数建立新的关联

    如果一个DTLS client-server对被配置为重复的连接发生在相同的主机/端口四元组时,client可能会静静地丢弃连接然后以相同参数(例如,重启之后)发起另一个连接。当一个新的握手中的epoch为0时对于server可能会出现这种情况。如果一个server确信在一个给定的主机/端口四元组上存在着一个关联且它收到了一个epoch为0的ClientHello,它应该继续新的握手但不能销毁这个存在的关联,直到client通过完成了一个cookie交换或通过完成一个包括传输一个可验证的Finished消息在内的一个完整的握手来表明了可达性。在收到一个正确的Finished消息后,server必须废弃之前的关联以避免在两个有效的且epoch重叠的联之间产生冲突。可达性的要求阻止了off-path/blind攻击者仅仅通过发送伪造的ClientHello就能够摧毁关联。

4.3.新语法的总结

    本节包含了在TLS1.2和DTLS1.2之间产生了变化的数据结构的规范。语法的定义见[TLS12]。

4.3.1.记录层

struct {
        ContentType type;
        ProtocolVersion version;
        uint16 epoch;                                     // New field
        uint48 sequence_number;                           // New field
        uint16 length;
        opaque fragment[DTLSPlaintext.length];
      } DTLSPlaintext;

      struct {
        ContentType type;
        ProtocolVersion version;
        uint16 epoch;                                     // New field
        uint48 sequence_number;                           // New field
        uint16 length;
        opaque fragment[DTLSCompressed.length];
      } DTLSCompressed;

      struct {
        ContentType type;
        ProtocolVersion version;
        uint16 epoch;                                     // New field
        uint48 sequence_number;                           // New field
        uint16 length;
        select (CipherSpec.cipher_type) {
          case block:  GenericBlockCipher;
          case aead:   GenericAEADCipher;                 // New field
        } fragment;
      } DTLSCiphertext;

4.3.2.握手协议

enum {
     hello_request(0), client_hello(1), server_hello(2),
     hello_verify_request(3),                          // New field
     certificate(11), server_key_exchange (12),
     certificate_request(13), server_hello_done(14),
     certificate_verify(15), client_key_exchange(16),
     finished(20), (255) } HandshakeType;

   struct {
     HandshakeType msg_type;
     uint24 length;
     uint16 message_seq;                               // New field
     uint24 fragment_offset;                           // New field
     uint24 fragment_length;                           // New field
     select (HandshakeType) {
       case hello_request: HelloRequest;
       case client_hello:  ClientHello;
       case server_hello:  ServerHello;
       case hello_verify_request: HelloVerifyRequest;  // New field
       case certificate:Certificate;
       case server_key_exchange: ServerKeyExchange;
       case certificate_request: CertificateRequest;
       case server_hello_done:ServerHelloDone;
       case certificate_verify:  CertificateVerify;
       case client_key_exchange: ClientKeyExchange;
       case finished: Finished;
     } body; } Handshake;

   struct {
     ProtocolVersion client_version;
     Random random;
     SessionID session_id;
     opaque cookie<0..2^8-1>;                             // New field
     CipherSuite cipher_suites<2..2^16-1>;
     CompressionMethod compression_methods<1..2^8-1>; } ClientHello;

   struct {
     ProtocolVersion server_version;
     opaque cookie<0..2^8-1>; } HelloVerifyRequest;

5.安全考虑

    本文描述了一个TLS 1.2的变体;因此,大多数安全考虑与TLS 1.2[TLS12]中的一致,附录D, E, F中有描述。

    由DTLS引发的主要的额外安全考虑是拒绝服务。DTLS包含了一个交换cookie其设计用来应对拒绝服务。然而,不使用cookie交换的实现对于DoS仍然是脆弱的。尤其是,不能使用这个cookie交换的DTLS server可能会被用作攻击放大器,即使它们自身并不经历DoS。因此,DTLS server应该使用cookie交换除非有好的理由相信放大器对于它们的环境并不是威胁。Client必须准备好对每个握手进行cookie交换。

    与TLS实现不同,DTLS实现不应该对非法记录用终结连接的方式进行响应。细节见4.1.2.7节。

6.致谢

(略)

7.IANA考虑

    本文使用了与TLS [TLS12]相同的标识符空间,所以不需要新的IANA记录。当新的标识符分配给TLS时,作者必须明确它们是否适用于DTLS。IANA已经修改了所有的TLS参数记录来增加DTLS-OK标识,表明规范是否可以被用于DTLS。在发布时,所有的[TLS12]记录除了下面的都适用于DTLS。记录的全部表格可以在[IANA]获得。

    来自TLS 密码套件记录:
      0x00,0x03 TLS_RSA_EXPORT_WITH_RC4_40_MD5        [RFC4346]
      0x00,0x04 TLS_RSA_WITH_RC4_128_MD5              [RFC5246]
      0x00,0x05 TLS_RSA_WITH_RC4_128_SHA              [RFC5246]
      0x00,0x17 TLS_DH_anon_EXPORT_WITH_RC4_40_MD5    [RFC4346]
      0x00,0x18 TLS_DH_anon_WITH_RC4_128_MD5          [RFC5246]
      0x00,0x20 TLS_KRB5_WITH_RC4_128_SHA             [RFC2712]
      0x00,0x24 TLS_KRB5_WITH_RC4_128_MD5             [RFC2712]
      0x00,0x28 TLS_KRB5_EXPORT_WITH_RC4_40_SHA       [RFC2712]
      0x00,0x2B TLS_KRB5_EXPORT_WITH_RC4_40_MD5       [RFC2712]
      0x00,0x8A TLS_PSK_WITH_RC4_128_SHA              [RFC4279]
      0x00,0x8E TLS_DHE_PSK_WITH_RC4_128_SHA          [RFC4279]
      0x00,0x92 TLS_RSA_PSK_WITH_RC4_128_SHA          [RFC4279]
      0xC0,0x02 TLS_ECDH_ECDSA_WITH_RC4_128_SHA       [RFC4492]
      0xC0,0x07 TLS_ECDHE_ECDSA_WITH_RC4_128_SHA      [RFC4492]
      0xC0,0x0C TLS_ECDH_RSA_WITH_RC4_128_SHA         [RFC4492]
      0xC0,0x11 TLS_ECDHE_RSA_WITH_RC4_128_SHA        [RFC4492]
      0xC0,0x16 TLS_ECDH_anon_WITH_RC4_128_SHA        [RFC4492]
      0xC0,0x33 TLS_ECDHE_PSK_WITH_RC4_128_SHA        [RFC5489]

    来自TLS输出者标识记录:
      client EAP encryption       [RFC5216]
      ttls   keying material      [RFC5281]
      ttls   challenge            [RFC5281]

    本文定义了一个新的握手消息,hello_verify_request,它的值已经从定义在[TLS12]的TLS HandshakeType记录中分配出来。IANA已经指定的值是3.

8.从DTLS1.0开始的变化

    本文反映了从DTLS1.0开始的如下变化:
- 更新以适应TLS 1.2 [TLS12];
- 在4.1.2.3节(跟随TLS1.2中的变化)中增加了AEAD密码;
- 关于4.1节中的序列号和epoch的澄清,以及为4.2.8节中处理状态丢失给出了一个明确的步骤;
- 澄清和关于4.1.1.1节中关于路径MTU问题的更详细的规则。分片文本的输出的澄清;
- 对4.1.2.7节中处理非法记录的澄清;
- 在4.2.1节的末尾增加了一个新的段落描述对非法cookie的处理;
- 在4.2.4节的末尾增加了一些新的文字来描述如何避免握手死锁条件;
- 在4.2.6节增加了一些新的关于CertificateVerify消息的文字;
- 4.1节防止epoch回绕的方法;
- IANA需求的澄清和对每个参数的一个新的IANA记录标志的明确需求;
- 增加了一个记录序列号镜像技术来处理重复的ClientHello消息;
- 为HelloVerifyRequest推荐了一个固定的版本号;
- 大量编辑上的修改。

9.参考文献

9.1 标准参考文献


   [REQ]       Bradner, S., "Key words for use in RFCs to Indicate
               Requirement Levels", BCP 14, RFC 2119, March 1997.

   [RFC1191]   Mogul, J. and S. Deering, "Path MTU discovery", RFC 1191,
               November 1990.

   [RFC4301]   Kent, S. and K. Seo, "Security Architecture for the
               Internet Protocol", RFC 4301, December 2005.

   [RFC4443]   Conta, A., Deering, S., and M. Gupta, Ed., "Internet
               Control Message Protocol (ICMPv6) for the Internet
               Protocol Version 6 (IPv6) Specification", RFC 4443, March
               2006.

   [RFC4821]   Mathis, M. and J. Heffner, "Packetization Layer Path MTU
               Discovery", RFC 4821, March 2007.

   [RFC6298]   Paxson, V., Allman, M., Chu, J., and M. Sargent,
               "Computing TCP's Retransmission Timer", RFC 6298, June
               2011.

   [RSAGCM]    Salowey, J., Choudhury, A., and D. McGrew, "AES Galois
               Counter Mode (GCM) Cipher Suites for TLS", RFC 5288,
               August 2008.

   [TCP]       Postel, J., "Transmission Control Protocol", STD 7, RFC
               793, September 1981.

   [TLS12]     Dierks, T. and E. Rescorla, "The Transport Layer Security
               (TLS) Protocol Version 1.2", RFC 5246, August 2008.

9.2 信息性参考文献

   [DCCP]      Kohler, E., Handley, M., and S. Floyd, "Datagram
               Congestion Control Protocol (DCCP)", RFC 4340, March
               2006.

   [DCCPDTLS]  Phelan, T., "Datagram Transport Layer Security (DTLS)
               over the Datagram Congestion Control Protocol (DCCP)",
               RFC 5238, May 2008.

   [DTLS]      Modadugu, N. and E. Rescorla, "The Design and
               Implementation of Datagram TLS", Proceedings of ISOC NDSS
               2004, February 2004.

   [DTLS1]     Rescorla, E. and N. Modadugu, "Datagram Transport Layer
               Security", RFC 4347, April 2006.

   [ECCGCM]    Rescorla, E., "TLS Elliptic Curve Cipher Suites with
               SHA-256/384 and AES Galois Counter Mode (GCM)", RFC 5289,
               August 2008.

   [ESP]       Kent, S., "IP Encapsulating Security Payload (ESP)", RFC
               4303, December 2005.

   [IANA]      IANA, "Transport Layer Security (TLS) Parameters",
               http://www.iana.org/assignments/tls-parameters.

   [IKEv2]     Kaufman, C., Hoffman, P., Nir, Y., and P. Eronen,
               "Internet Key Exchange Protocol Version 2 (IKEv2)", RFC
               5996, September 2010.

   [IMAP]      Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
               4rev1", RFC 3501, March 2003.

   [PHOTURIS]  Karn, P. and W. Simpson, "Photuris: Session-Key
               Management Protocol", RFC 2522, March 1999.

   [POP]       Myers, J. and M. Rose, "Post Office Protocol - Version
               3", STD 53, RFC 1939, May 1996.

   [SIP]       Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston,
               A., Peterson, J., Sparks, R., Handley, M., and E.
               Schooler, "SIP: Session Initiation Protocol", RFC 3261,
               June 2002.

   [TLS]       Dierks, T. and C. Allen, "The TLS Protocol Version 1.0",
               RFC 2246, January 1999.

   [TLS11]     Dierks, T. and E. Rescorla, "The Transport Layer Security
               (TLS) Protocol Version 1.1", RFC 4346, April 2006.

   [WHYIPSEC]  Bellovin, S., "Guidelines for Specifying the Use of IPsec
               Version 2", BCP 146, RFC 5406, February 2009.


  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值