TLS1.3 密钥衍生计算方法和功能 - V2EX
hxndg
V2EX    SSL

TLS1.3 密钥衍生计算方法和功能

  •  
  •   hxndg 2020-08-27 15:12:48 +08:00 2281 次点击
    这是一个创建于 1871 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    Tls1.3 的 RFC ( RFC8446 )中规定了各种密钥的算法和用途,虽然我打算在不久的将来翻译 TLS1.3 的 RFC,出于方便的考虑,还是简单讲讲 TLS1.3 中的各种密钥计算方法。因为 RFC 的密钥衍生流程中没有画出 PSK 的计算过程,我也会在本文附加 PSK 的计算流程如果文章帮到了你 /或者依然存在一些问题,欢迎给我一些反馈。

    我本来是写到了博客里的把这文章,想了想还是在 V 站再发一遍吧,我平时有 V 站检索资料的习惯。就顺便发出来了。

    密钥衍生流程

    密钥衍生流程中一些计算方法

    密钥衍生的流程依赖于 RFC5869 定义的 HKDF-Extract 和 HKDF-Expand 操作流程,这两个流程结合到一起才算是从密钥原始材料( key material)衍生出安全的符合长度需求的密钥。TLS1.3 定义了一个 HKDF-Expand-Label 即带衍生标签的 HKDF-Expand 操作,这个操作的流程是:

    Derive-Secret(Secret, Label, Messages) = HKDF-Expand-Label(Secret, Label,Transcript-Hash(Messages), Hash.length) = HKDF-Expand(Secret, HkdfLabel, Hash.length)

    如果你知道 HKDF-Expand 的操作(我会不久翻译 RFC5869 并且讲解 HKDF 的两个操作),问题就变成了 HkdfLabel 是如何拼接的。按照 RFC,HkdfLabel 的拼接方式如下:

    struct { uint16 length = Length; opaque label<7..255> = "tls13 " + Label; opaque context<0..255> = Context; } HkdfLabel;

    首先拼接一个 uint16_t 的“长度”,也就是两个字节,该“长度”的数值等于当前算法使用的 hash 算法的长度,比方说你使用 ciphersuite 是 TLS_AES_256_GCM_SHA384,所以该“长度的数值”等于 SHA384 计算结果的长度,也就是 384/8=48 字节。之后先拼接上一个 uint8_t 的“字符串长度”,这个长度用来标识整个字符串的长度,包括"tls1.3"+Label 的长度,然后附加上字符串"tls13"(我这里附加的时候可不包括两个双引号啊),再拼接上 Label,比方说你此时在计算复用主密钥(resumption_master_secret),那么这时候的 Label 就是"res master"。最后拼接一个 uint8_t 的 Context 长度,再拼接 Context 。Context 一般是对消息做 hash 计算得出的。

    密钥衍生流程及每个密钥的作用

    这部分我是直接粘贴的 TLS1.3 RFC 的内容,见下,由于翻译可能产生争议,这部分我用的是原图。 key schedule process.png 密钥衍生流程中有两个用于计算的基础密钥,分别是 PSK 和 ECDHE secret 。PSK 通过上次连接由复用主密钥计算得出 /带外数据传输建立,(EC)DHE 密钥则是完整握手时协商产生。这两个密钥并不一定每次都存在,比方说完整握手的情况下,计算初期密钥( Early Secret )的时候可能不存在 PSK,那么 PSK 就由一串长度等同于当前 ciphersuite 使用的 hash 算法结果的长度的 0x00 代替,比方说 ciphersuite 是 TLS_AES_256_GCM_SHA384,那么 PSK 就用长度等于 48 的 0x00 内容替代。我在写 TLS1.3 自动机的时候就直接写了个

    static const unsigned char default_zeros[EVP_MAX_MD_SIZE];

    来替代。

    流程当中 HKDF-Extract 顶部的数据充当“盐”的角色,左侧是原始密钥材料( IKM ),其输出结果为衍生出来的密钥。我们熟悉了 HKDF-Extract 和 Derive-Secret 之后,就会发现 TLS1.3 中是结合了 HKDF-Extract 和 HKDF-Expand 一起计算出来各种密钥的,这点比 TLS1.2 更符合 RFC 的要求,更安全。

    还有一点值得注意,整个密钥衍生流程是不可以跳过任何一步的,每一步都必须进行(这里的必须进行只针对需要的数据,比方说你不开复用,那么就没必要计算 PSK,复用主密钥之类的)。举个简单的例子,完整握手的情况下,仍然需要计算初期密钥,计算的方式就是 HKDF-Extract(0, 0)。

    最后一个需要注意的是,我们计算出来的密钥并不直接参与到会话 /握手当中去,是要经过一论演算的。也就是说从 secret 到 key 是有一个过程的,举个例子说,客户端握手密钥( client_handshake_traffic_secret )计算到客户端会话 key 是有个过程的,如下:

    client_handshake_write_key = HKDF-Expand-Label(client_handshake_traffic_secret, "key", "", key_length) client_handshake_write_iv = HKDF-Expand-Label(client_handshake_traffic_secret, "iv", "", iv_length)

    我这里之所以写 client_handshake_write_key 的时候加了 write 是因为客户端发送握手消息使用的 key 和客户端接受握手消息(服务端发送握手消息)的 key 是不一样的。这一点在 TLS1.2 中也是如此。

    PSK 的计算过程

    这次只简单写写 PSK 的计算过程,复用时如何计算 binder value,恢复会话的操作以后单独写。 PSK 由复用主密钥( resumption_master_secret )计算得出,公式如下:

    PSK = HKDF-Expand-Label(resumption_master_secret,"resumption", ticket_nonce, Hash.length)

    这里复用主密钥不需要多说,而 ticket_nonce 需要简单说说:ticket_nonce 每个连接只有一个,其值应当每个都不一样。ticket_nonce 需要明文发给客户端,让客户端计算出来 PSK 。包含 ticket_nonce 的报文就是 new session ticket 报文,简写为 NST 报文。

    直接对照“密钥衍生流程中一些计算方法”就可以清晰的计算出来 PSK,实际上从 PSK 到 HMAC key,再到 binder key 的计算过程非常简单,完全可以一次算完。为了节省复用时的时间,可以直接计算存储 binder key 。但是由于涉及到操作都是 hmac/hash,所以本质上时间减少的很少。这东西我第一个想出来以后被我司拿去申请了专利,还给了点奖金。

    密钥衍生中的一些细节

    这里写的东西是我当初踩到的一些坑,有的我注意到避开了,有的没注意到踩了进去。 1 计算 HKDF-Extract 和 HKDF-Extract 时,需要注意,对于空消息的 hash 结果并不为空。其结果时有值的。因为忘了 hash 计算的流程,就容易踩这个坑。目前 tls1.3 的 cipher 只有两种 hash 长度一个是 sha384 的 48,另一个时 sha256 的 32 。直接把当时写的结果贴上来了,各位也不用自己算了。

    uint8_t zero_sha384_hash[TLSV13_SHA384_HASH_LEN] = {0x38, 0xb0, 0x60, 0xa7,0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e,0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11,0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf,0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf,0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1,0x48, 0x98, 0xb9, 0x5b}; uint8_t zero_sha256_hash[TLSV13_SHA256_HASH_LEN] = {0xe3, 0xb0, 0xc4, 0x42,0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8,0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4,0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,0x78, 0x52, 0xb8, 0x55};

    2 对于每个 TLS1.3 的连接,需要自己保存 ticket_nonce 。踩这个坑是因为写的自动机时异步,计算 PSK 时候用的 ticket_nonce 和写 NST 报文的时候的 ticket_nonce 可能会变化。因此需要每个连接自己保存自己的 ticket_nonce 。

    结尾

    写到这里差不多就可以结束了,TLS 这块还有啥不明白的直接告诉我就成了 狗头的赞赏码.jpg

    2 条回复    2021-01-23 20:40:36 +08:00
    hxndg
        1
    hxndg  
    OP
       2020-08-28 18:50:32 +08:00
    HKDF-Expand 和 HKDF-Extract 的流程和代码已经写了。
    那么密钥衍生的流程可以说是彻底没啥难度了
    hxndg
        2
    hxndg  
    OP
       2021-01-23 20:40:36 +08:00
    回来看了一下 v 站,发现格式可以说是很难看了。。。贴个博客的连接,格式好看很多
    https://hxndg.github.io/2020/08/27/TLS1.3%E5%AF%86%E9%92%A5%E8%A1%8D%E7%94%9F%E8%AE%A1%E7%AE%97%E6%96%B9%E6%B3%95%E5%92%8C%E5%8A%9F%E8%83%BD/
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2734 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 11:50 PVG 19:50 LAX 04:50 JFK 07:50
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86