0%

tls_dtls_connection_id_08

dtls connection id 翻译备忘

摘要

本文档定义了DTLS1.2的 Connection ID(CID)。

CID是记录层头给接收端定位相应加密上下文的一个额外信息标识。在”经典的”DTLS中,选择一个DTLS记录层入包的相应加密信息需要使用5元组,如果在session的生命周期内源IP或源端口改变了,接收端就无法定位相应的加密上下文了。

1. 介绍

DTLS(RFC6347)是为加密无连接状态传输通道而设计的,比如说UDP。跟TLS一样,DTLS一开始的握手非常耗费计算资源(特别是用到公钥加密时)。在成功握手后,会使用对称加密来对数据进行认证,以及完整性和私密性保护。这样的两步允许终端将开始的握手性能资源消耗平摊到后续的应用数据加密上去。理想状态下,加密应用数据的第二阶段应该维持尽可能长的时间,直到秘钥过期才去重新协商。

在RFC 6347中,对端的IP地址和端口用来标识跟DLTS连接的关联。但在有些情况下,比如NAT重绑定,会导致这些值不够用。这种情况在IoT环境中更普遍,比如设备进入睡眠状态以节省电量。NAT重绑定会导致连接失败,进而导致重新握手,消耗更多计算资源。

本文档定义了一个DTLS1.2的扩展项,在DTLS记录层加上一个CID。是否使用CID通过DTLS扩展项协商而来。

2. 惯例和术语

**MUST**...

3. “connection_id”扩展项

本文档定义”connection_id”扩展项,用于 ClientHelloServerHello 消息中。

扩展项类型如下:

1
2
3
enum {
connection_id(TBD1), (65535)
} ExtensionType;

ClientHello中该扩展项的extension_data 字段必须包含 ConnectionId 结构体。该结构体包含client希望server发送消息给client时候使用的CID的值。0长CID值表示client准备好发送带CID的消息,但不希望server发来的消息带CID。或者说,0长CID意味着client希望server使用0长CID,结果是一样的。

1
2
3
struct {
opaque cid<0..2^8-1>;
} ConnectionId;

server如果希望使用CID,会在 ServerHello 中回复一个”connection_id”扩展项,包含它希望client发送消息给自己的时候使用的CID的值。0长CID表示server会用client的CID发送消息,但不希望client发来的消息包含CID(或者说,使用0长CID)。

因为每个端点在”connection_id”扩展项中发送的值是它希望收到的加密记录数据包中的CID,因此端点有责任为这样的连接标识使用全局统一的长度值,比如在编译阶段确定CID长度的值,这样反过来还可以方便解析和连接查找。但同时,实现必须能发送不同长度值以适应不同的对端。如果实现希望使用可变长的CID值,则需要自己构造CID,以在接收的时候能确定CID的长度。要注意记录层本身是没有CID长度信息的。

在DTLS1.2中,CID只在session最开始进行交换。没有在session建立好之后专门用于”CID update”的消息,因为DTLS1.2通常没有类似TLS1.3那种握手后消息。当DTLS会话被复用或者重新写上,”connection_id”扩展项会重新协商。

如果DTLS终端没有协商使用CID,就必须用RFC6347定义的记录层格式和内容类型。

如果DTLS终端使用 ClientHello 和 ServerHello 协商出需要使用CID,接下来需要按照如下步骤进行。

DTLS终端决定出包或入包数据是否需要使用新的记录层格式,比如,新的记录层格式包含CID。一旦开始加密,就使用带 tls12_cid 内容类型的新记录层格式。明文负载从不使用新的记录层格式和CID内容类型。

发送时,如果协商使用了0长CID,就必须使用RFC6347定义的记录层格式和内容类型(见RFC6347 #4.1),否则必须使用下图3中定义的带 tls12_cid 内容类型的新数据格式。

当传输带 tls12_cid 内容类型的数据包时,必须使用 #5 中定义的新的MAC计算方式。

接收时,如果设置了 tls12_cid 内容类型,就需要使用 CID 去查找连接和加密上下文。如果没有设置 tls12_cid 类型,就使用5元组去查找连接和加密上下文,另外还必须检查期望的 CID 是否就是0长的;如果检查失败,必须丢弃数据包。

当接收一个带 tls12_cid 类型的数据包时,必须使用 #5 中定义的新的MAC计算方式。如果收到一个RFC6347定义的记录层格式数据包,必须使用RFC6347-#4.1.2中定义的MAC计算方式。

4. 记录层扩展

本段定义DTLS1.2记录层格式,[I-D.ietf-tls-dtls13]中定义如何在DTLS1.3中使用 CID。

为了让接收者确定一个记录包是否带有 CID,协商使用 CID 的连接使用一个单独的记录类型 tls12_cid(TBD2)。该内容类型的使用意味着:

  • CID 字段存在,并且包含1个或更多字节
  • MAC的计算方式按照 #5 所述
  • 真正的内容类型包含在加密信封中,如下所述

明文记录包不受该扩展影响,并且 DTLSPlaintext 结构体没有变动,如图1所示:

1
2
3
4
5
6
7
8
9
10
struct {
ContentType type;
ProtocolVersion version;
uint16 epoch;
uint48 sequence_number;
uint16 length;
opaque fragment[DTLSPlaintext.length];
} DTLSPlaintext;

Figure 1: DTLS 1.2 Plaintext Record Payload.

当使用 CID 时,先将待发送的内容用自己的内容类型和可选的padding封装进 DTLSInnerPlaintext 结构中。这个新引入的结构如图2所示。 然后再加密 DTLSInnerPlaintext,图3显示了加上 CID 之后组成的 DTLSCiphertext 结构。

1
2
3
4
5
6
7
struct {
opaque content[length];
ContentType real_type;
uint8 zeros[length_of_padding];
} DTLSInnerPlaintext;

Figure 2: New DTLSInnerPlaintext Payload Structure.

content: 对应给定长度的fragment
real_type: 负载的内容类型
zeros: 明文中在类型字段之后可以填充任意长度的0。这给发送者提供了一个可以将DTLS记录包填充到给定长度的机制,只要填充的长度在记录层大小限制内。更多细节见 RFC8446 #5.4。(注意 RFC8446 中 TLSInnerPlaintext 指的就是这里的 DTLSInnerPlaintext。)

1
2
3
4
5
6
7
8
9
10
11
struct {
ContentType outer_type = tls12_cid;
ProtocolVersion version;
uint16 epoch;
uint48 sequence_number;
opaque cid[cid_length]; // New field
uint16 length;
opaque enc_content[DTLSCiphertext.length];
} DTLSCiphertext;

Figure 3: DTLS 1.2 CID-enhanced Ciphertext Record.
  • outer_type: 带 CID 的 DTLSCiphertext 记录包的外层负载类型总是设置成 tls12_cid(TBD2)。真正的负载类型解密后在 DTLSInnerPlaintext.real_type 中存放。
  • cid: CID 的值,长度是扩展项协商时候定好的 cid_length。回忆一下(之前讨论过的),每个终端自己选择CID的值,用这个值来标识当前连接,因此实现可以选择总是接收一个固定长度的 CID 值。但是如果实现的时候选择可以接收不同长度的 CID,CID的值就必须能自己描述自己的长度,因为没有其他机制来决定选择哪条连接。
  • enc_content: 序列化后的 DTLSInnerPlaintext 结构的加密后数据。

其他字段在 RFC6347 中定义。

5. 记录层负载保护

TLS和DTLS已经定义了几种加密套件,套件中 MAC 的计算方法有些不一样。

针对带 tls12_cid 的记录层数据包,本文档调整了 RFC6347 和 RFC7366 中定义的 MAC 计算方法,也调整了 RFC6347 提供的用于 AEAD 套件的额外数据的定义方式。调整的算法不能用于不带CID的记录包,也就是负载类型不是 tls12_cid 的那些记录包。

接下来的字段在本文档定义,其他字段都在参考文档中定义。

  • cid: 协商好的CID的值(可变长度)
  • cid_length: 表示协商好的CID长度的1字节字段
  • length_of_DTLSInnerPlaintext: 序列化后的 DLTSInnerPlaintext 的长度(2字节字段,单位是bytes)。该长度不能超过 2^14。
  • “+” 表示连接符。

5.1. 块加密套件

下边这个 MAC 算法用于 RFC7366 中描述的不使用 Encrypt-then-MAC 过程的块加密套件。

1
2
3
4
5
6
7
8
9
10
MAC(MAC_write_key, seq_num +
tls12_cid +
DTLSCiphertext.version +
cid +
cid_length +
length_of_DTLSInnerPlaintext +
DTLSInnerPlaintext.content +
DTLSInnerPlaintext.real_type +
DTLSInnerPlaintext.zeros
)

5.2. 使用 Encrypt-then-MAC 处理的块加密套件

下边这个 MAC 算法用于 RFC7366 中描述的使用 Encrypt-then-MAC 过程的块加密套件。

1
2
3
4
5
6
7
8
MAC(MAC_write_key, seq_num +
tls12_cid +
DTLSCipherText.version +
cid +
cid_length +
length of (IV + DTLSCiphertext.enc_content) +
IV +
DTLSCiphertext.enc_content);

5.3. AEAD 加密套件

对利用附加数据进行认证的加密算法,下边是调整后的附加数据计算方法

1
2
3
4
5
6
additional_data = seq_num +
tls12_cid +
DTLSCipherText.version +
cid +
cid_length +
length_of_DTLSInnerPlaintext;

6. 对端地址更新

当收到一个带 CID 的记录包时,如果该数据包源地址跟当前使用的 DTLS 连接中设置的不同,那除非满足下边3个条件,否则接收者不会更新发送地址:

  • 数据包已经被 DTLS 记录层处理验证通过了
  • 接收到的记录包比已经收到的都要”新”(epoch 和 seq number 都是最新的)。地址更新之前已经被发出去的数据包经过重新排序,可能会将一个有效的地址更新重新变回去。这可以限制攻击者使用重放数据包伪造一个假的地址变动,假的地址变动可能会引起 DoS。要是攻击者能改变源地址并且让重放的数据包比源数据包更早到达,还是可以触发地址更新的。
  • 有其他方法能保证新的对端地址能接收和处理 DTLS 记录包。本文档没有定义相应的测试方法。

上边的是防止通过伪造地址和重放数据包触发攻击的必要条件。注意这里没有要求必须使用 DTLS1.2 #4.1.2.6 定义的防重放攻击窗口机制。”anti-replay window” 和 “newer” 算法这两种方法都能防止重放攻击时地址更新,后者只适用于对端地址更新,前者会使用于任何应用层流量。

注意通过了 DTLS 解密检查但并未触发对端地址更新的数据包也是有效的DTLS数据包,也要传递给应用。

应用协议可以依赖对端地址更新事件实现自己的防攻击机制。当传递了这个事件,就可以触发应用层对地址有效性检查的机制,例如依赖最少多少次来回通信后就更新地址的机制。或者应用 [I-D.tschofenig-tls-dtls-rrc] 描述的一个DTLS特定机制。

DTLS 的实现必须静默丢掉那些解密失败的记录包。

7. 例子

图4展示了一个从 client 到 server 单向使用 CID 的例子。我们使用 “connection_id=empty” 表示 “connection_id” 扩展项中出现了0长 CID。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Client                                             Server
------ ------

ClientHello -------->
(connection_id=empty)


<-------- HelloVerifyRequest
(cookie)

ClientHello -------->
(connection_id=empty)
(cookie)

ServerHello
(connection_id=100)
Certificate
ServerKeyExchange
CertificateRequest
<-------- ServerHelloDone

Certificate
ClientKeyExchange
CertificateVerify
[ChangeCipherSpec]
Finished -------->
<CID=100>

[ChangeCipherSpec]
<-------- Finished


Application Data ========>
<CID=100>

<======== Application Data

Legend:

<...> indicates that a connection id is used in the record layer
(...) indicates an extension
[...] indicates a payload other than a handshake message

Figure 4: Example DTLS 1.2 Exchange with CID

注意: 例子中一旦开始对记录层进行加密,记录包就加上了 CID。 在 DTLS1.2 握手中只有一个 Finished 消息被加密了。例子显示了从client发送到 server 的数据包如何使用 CID,所以只有负载包含 Finished 消息或应用数据的记录包才带 CID。

8. 机密性考虑

CID 替换了之前使用的5元组,但也引入了在 DTLS 连接生命期内都持续存在的标识。如 RFC6973 所述,任何标识都会增加连接性泄露的风险。

一个在 DTLS client 和 server 之间的线上攻击者能根据同一ID对儿来关联同一条DTLS流的所有数据包。抛开多宿主和移动性,使用 CID 会跟使用5元组暴露出的信息一样。

如果带上多宿主,被动攻击者可以区分两路以上的通信,通过CID的改变和seq number,可以关联某一路的数据包。DTLS1.2中 CID 更新机制的缺失会导致该扩展在必须考虑关联性的移动场景下不适用。在多宿主环境中部署DTLS的话,如果必须考虑这些方面,就不要在DTLS1.2中使用 CID,或者切换到 DTLS1.3,在DTLS1.3中会有 CID 更新机制,并且seq number 是被加密的。

本文档在带 CID 的记录层中引入了填充,该保密机制在原始的DTLS1.2中不存在。填充让基于长度的流量分析变得更困难。更多关于记录层填充的描述见 RFC8446 #5.4 和 附录 E.3。

最后,终端可以用 CID 去关联给定连接上的连接相关数据。这会给线上攻击者一种跟每连接信息通信的机制。除了含有任意 CID 值,也没有什么直接的方法能处理这种情况。顾虑这些的话,使用者应该拒绝使用 CID。

9. 安全性考虑

一个线上攻击者可以实施针对第三方的反射攻击,因为 DTLS 终端无法将正常的地址更新事件(因为NAT重新绑定)和有害的区分开。当请求/响应消息之间的大小非常不对称的时候,这种攻击就需要纳入考虑了。

另外,攻击者能够观察两个DTLS终端之间的数据流,然后能够修改IP/PORT来重放数据包。

对端地址更新在 #6 中有讨论。

10. IANA相关

11. 引用