赞
踩
TLS(传输层安全)协议是一种广泛使用的网络协议,它主要用来建立服务器和客户端之间的安全通道。本文对TLS 1.3的18草案就行详细解析,并用proverif对其进行建模已经进行安全性论证。本文是中国科学技术大学形式化方法的大作业,难免有些仓促,如有问题敬请指正。
在加密通信过程中,有时要用到共享密钥(shared key),这个密钥为通信双方所共享,但是要保证不可以被攻击者发现。Diffie-Hellman密钥交换协议确保即使攻击者截获通信信道上的所有信息,都无法在可以接受的时间内算出它们的共享密钥。
该协议用到了原根的一个性质:假设 q q q是 p p p的一个原根,则q的幂构成模p的简化剩余系,即q的幂模p可以取到1到p-1的任意一个值。具体证明可以参见https://www.cnblogs.com/philolif/p/number-theory-6.html。由求q的幂模p的值很容易,但是已知这个值,反过来求指数的过程计算周期却很长,基本只能用试错法,计算时间复杂度和p-1同量级。如果p非常大,那么逆向求解是无法有效做到的。
通信双方首先约定一个大素数p和它的原根g,这两个量是无需保护的。接下来A生产随机数a,将 g a ( m o d p ) g^a(mod p) ga(modp)通过公共信道发给B,接下来B生成随机数b,将 g a b ( m o d p ) g^{ab}(mod p) gab(modp)作为密钥,并将 g b g^b gb发给A,随后A将 ( g b ) a = g b a ( m o d p ) (g^b)^a=g^{ba}(mod p) (gb)a=gba(modp)作为密钥。由于 g a b = g b a g^{ab}=g^{ba} gab=gba,A和B便得到公共密钥。
攻击者从信道上可以截获 g a g^a ga和 g b g^b gb,然而从这两者还原 g a b g^{ab} gab是无法做到的,这就确保了协议的安全性。
然而,到此为止我们仅赋予了攻击者监听信息的权力。如果再考虑攻击者综合利用信息、伪造并重发送的情况,之前提到的通信方式仍然是不安全的:攻击者可以使用中间人攻击来获得共享密钥。为应对中间人攻击,可以使用数字签名。
接下来使用Proverif工具来对Diffie-Hellman密钥交换协议进行建模。这里不考虑数字签名,因为在最终的验证程序中数字签名额外生成并与通信消息绑在一起。定义dh_ideal(element,bitstring)为求幂函数,将element类型的参数求对另一个参数的幂,得到另一个element类型的值。这里的element可以理解成完全剩余系中的元素。除此之外,再考虑一种特殊情况: g a g^a ga模p只有极少数的可能值,以至于可以通过试错法破解共享密钥。出现这种情况的原因可能是g不是原根。不妨考虑极端情况: g a g^a ga模p只有一个可能值,记为BadElement。为了区分两种加密方式,我们再定义一种方法dh_exp,它接受一个参数标记使用正常的DH算法(StrongDH)或者使用上面提到的出现BadElement的方法(WeakDH)。如果是StrongDH,在底数是BadElement时返回BadElememt,否则按dh_ideal进行计算;如果是WeakDH,则返回BadElememt。此外为保证 g a b = g b a g^{ab}=g^{ba} gab=gba这一代数性质,需要定义一个方程确保dh_ideal(dh_ideal(G,x),y) = dh_ideal(dh_ideal(G,y),x)。
完整代码如下:
(********************************************************) (* Diffie-Hellman with small/bad subgroup attacks. See Logjam, Cross-Protocol *) (********************************************************) type group. const StrongDH: group [data]. const WeakDH: group [data]. type element. fun e2b(element): bitstring [data]. const BadElement: element [data]. const G: element [data]. fun dh_ideal(element,bitstring):element. equation forall x:bitstring, y:bitstring; dh_ideal(dh_ideal(G,x),y) = dh_ideal(dh_ideal(G,y),x). fun dh_exp(group,element,bitstring):element reduc forall g:group, e:element, x:bitstring; dh_exp(WeakDH,e,x) = BadElement otherwise forall g:group, e:element, x:bitstring; dh_exp(StrongDH,BadElement,x) = BadElement otherwise forall g:group, e:element, x:bitstring; dh_exp(StrongDH,e,x) = dh_ideal(e,x). letfun dh_keygen(g:group) = new x:bitstring; let gx = dh_exp(g,G,x) in (x,gx).
相比传统加密算法,AEAD加密算法支持解密前进行验证,以得知密钥正确性。为了便于理解,此处选一种AEAD加密分析进行说明。
E&M (Encryption and MAC):首先对密文进行加密,然后使用同一密钥进行MAC运算,将Concat(Ciphertext,MAC)发给对方。对方收到消息后先解密,然后将解密的信息重新做MAC运算,如果和收到的MAC值相同,则验证成功,说明密钥正确。(下图选自wiki)
有时,可以添加nonce(如序列号)唯一地标定每次加密过程。
在安全认证中,我们需要再定义一种弱AEAD加密算法,它允许对加密的数据进行修改,定义相应方法为aead_forged,接受两个参数,第一个为预期修改的信息p,第二个是加密后的信息。输出的信息经过解密后变成p。除此之外,攻击者可以使用aead_leak方法直接由加密过的数据得到被加密的值。
Proverif建模如下:
(********************************************************) (* Authenticated Encryption with Additional Data *) (* extended with with weak/strong algorithms: See Lucky13, Beast, RC4 *) (********************************************************) type ae_alg. const WeakAE, StrongAE: ae_alg. type ae_key. fun b2ae(bitstring):ae_key [data]. fun aead_enc(ae_alg, ae_key, bitstring, bitstring, bitstring): bitstring. (*编码*) fun aead_forged(bitstring,bitstring): bitstring. fun aead_dec(ae_alg, ae_key, bitstring, bitstring, bitstring): bitstring (*解码*) reduc forall a:ae_alg, k:ae_key, n:bitstring, p:bitstring, ad:bitstring; aead_dec(a, k, n, ad, aead_enc(a, k, n, ad, p)) = p otherwise forall a:ae_alg, k:ae_key, n:bitstring, p:bitstring, ad:bitstring,p':bitstring,ad':bitstring; aead_dec(WeakAE, k, n, ad, aead_forged(p,aead_enc(WeakAE, k, n, ad', p'))) = p. fun aead_leak(bitstring):bitstring reduc forall k:ae_key, n:bitstring, ad:bitstring, x:bitstring; aead_leak(aead_enc(WeakAE,k,n,ad,x)) = x.
可以将一个对象映射到某一个范围中的值。如果有两个不同对象映射到同一个值,则称为哈希冲突。一般来讲,可以通过检测两个值的Hash值是否相等,在不知道这两个具体值的情况下判断它们是否相等。和上面类似,我们可以定义一种“脆弱”的哈希算法,它很容易产生冲突。这样两个不相同的数可能被错误地被认证成相同。假设该算法下所有数据的哈希值全部被映射为collision。
哈希的proverif建模:
(********************************************************) (* Hash Functions, including those with collisions. See SLOTH *) (********************************************************) type hash_alg. const StrongHash: hash_alg [data]. const WeakHash: hash_alg [data]. const collision:bitstring [data]. fun hash_ideal(bitstring):bitstring. fun hash(hash_alg,bitstring): bitstring reduc forall x:bitstring; hash(WeakHash,x) = collision otherwise forall x:bitstring; hash(StrongHash,x) = hash_ideal(x).
Hash方法可以较好地用于验证,但生成的哈希值很容易伪造。HMAC可以理解为使用密钥进行哈希,具体定义为:
H
M
A
C
(
K
,
m
)
=
H
(
(
K
′
X
O
R
o
p
a
d
)
∣
∣
H
(
(
K
′
X
O
R
i
p
a
d
)
∣
∣
m
)
)
HMAC(K,m)=H((K'\ XOR\ opad)||H((K'\ XOR\ ipad)||m))
HMAC(K,m)=H((K′ XOR opad)∣∣H((K′ XOR ipad)∣∣m))
当K’大于某个长度时,K’=H(K),否则K’=K。双竖线表示连接。opad和ipad解释如下:
当然在Proverif中,可以隐藏很多实现的细节。定义一个名为mac_key的类型用于标记HMAC中的密钥hmac_ideal输入密钥和待编码数据,输出编码后的数据。hmac在mac_key的基础上添加哈希算法作为第一个参数。如果是WeakHash,输出的永远都是collision,否则按hmac_ideal就行正常编码。
(********************************************************)
(* HMAC *)
(********************************************************)
type mac_key.
fun b2mk(bitstring):mac_key [data,typeConverter].
fun hmac_ideal(mac_key,bitstring): bitstring.
fun hmac(hash_alg,mac_key,bitstring):bitstring
reduc forall k:mac_key, x:bitstring;
hmac(WeakHash,k, x) = collision
otherwise forall x:bitstring, k:mac_key;
hmac(StrongHash,k, x) = hmac_ideal(k,x).
签名方拥有一个私钥,将使用私钥对待签名数据进行加密,随后将加密后的签名和原数据发给检验方。检验方收到数据后,使用公钥对签名进行解密,如果解密后的数据和原数据相同,则说明数据为签名方所发。由于签名需要用到私钥,任何未得知私钥的攻击者都无法构造签名。
在Proverif中可以定义两个类型:公钥pubkey和私钥privkey。对于任意一个私钥,可以假设总可以找到对应的公钥,即定义pk(privkey),返回pubkey。但反过来,基于安全性,不存在对应的sk(pubkey),也就是说不能根据公钥就获取私钥。verify对使用私钥k对信息x的签名sign(k,x)进行验证。给定k对应的公钥pk(k)和x,如果签名信息是sign(k,x),则验证成功,应该返回true。整体代码如下:
(********************************************************) (* Public Key Signatures *) (********************************************************) type privkey. type pubkey. fun pk(privkey): pubkey. const NoPubKey:pubkey. (* RSA Signatures, typically the argument is a hash over some data *) fun sign(privkey,bitstring):bitstring. fun verify(pubkey,bitstring,bitstring): bool reduc forall k:privkey, x:bitstring; verify(pk(k),x,sign(k,x)) = true.
HKDF是基于HMAC的密钥派生算法,它可以将初始密钥扩展成更强大的密钥。
主要分为两部:提取和扩展。提取是把密钥转化为固定长度的伪随机数,扩展是将提取到的伪随机数扩展到指定长度。
提取的具体过程可以用如下公式表示:
H
K
D
F
−
E
x
t
r
a
c
t
(
s
a
l
t
,
I
K
M
)
=
H
M
A
C
(
s
a
l
t
,
I
K
M
)
HKDF-Extract(salt,IKM)=HMAC(salt,IKM)
HKDF−Extract(salt,IKM)=HMAC(salt,IKM)得到PRK,其中salt是盐,IKM是原始密钥。
扩展可以用如下公式表示:
H
K
D
F
−
E
x
p
a
n
d
(
P
R
K
,
i
n
f
o
,
L
)
=
O
K
M
HKDF-Expand(PRK,info,L)=OKM
HKDF−Expand(PRK,info,L)=OKM,OKM是长度为L的密钥输出,info是可选上下文和应用程序特定信息。
首先计算一系列哈希值,记为T(n),其中n从0到255。
T(0)=长度为0的空字符串
T
(
1
)
=
H
M
A
C
(
P
R
K
,
T
(
0
)
∣
i
n
f
o
∣
0
x
01
)
T(1)=HMAC(PRK,T(0)|info|0x01)
T(1)=HMAC(PRK,T(0)∣info∣0x01)
T
(
2
)
=
H
M
A
C
(
P
R
K
,
T
(
1
)
∣
i
n
f
o
∣
0
x
02
)
T(2)=HMAC(PRK,T(1)|info|0x02)
T(2)=HMAC(PRK,T(1)∣info∣0x02)
T
(
3
)
=
H
M
A
C
(
P
R
K
,
T
(
2
)
∣
i
n
f
o
∣
0
x
03
)
T(3)=HMAC(PRK,T(2)|info|0x03)
T(3)=HMAC(PRK,T(2)∣info∣0x03)
直到计算到T(255)。
我们将这些T(n)连接起来,记为
T
=
T
(
1
)
∣
T
(
2
)
∣
⋯
∣
T
(
N
)
T=T(1)|T(2)|\cdots|T(N)
T=T(1)∣T(2)∣⋯∣T(N),OKM即是T的前L个字节。
在TLS 1.3中,还需要用到HKDF-Expand-Label和Derive-Secret,它们的定义如下:
HKDF-Expand-Label(Secret, Label, HashValue, Length) =
HKDF-Expand(Secret, HkdfLabel, Length)
struct {
uint16 length = Length;
opaque label<9..255> = "TLS 1.3, " + Label;
opaque hash_value<0..255> = HashValue;
} HkdfLabel;
Derive-Secret(Secret, Label, Messages) =
HKDF-Expand-Label(Secret, Label,
Hash(Messages), Hash.Length)
HKDF的proverif实现:
(********************************************************) (* TLS 1.3 Key Schedule *) (********************************************************) type label. const client_finished, server_finished, master_secret, client_key_expansion, server_key_expansion: label. const tls13_client_handshake_traffic_secret, tls13_server_handshake_traffic_secret, tls13_client_early_traffic_secret, tls13_client_application_traffic_secret, tls13_server_application_traffic_secret, tls13_key, tls13_iv, tls13_early_exporter_master_secret, tls13_exporter_master_secret, tls13_resumption_master_secret, tls13_resumption_psk_binder_key, tls13_finished: label. fun tls12_prf(bitstring,label,bitstring): bitstring. letfun prf(k:bitstring,x:bitstring) = hmac(StrongHash,b2mk(k),x). letfun hkdf_extract(s:bitstring,k:bitstring) = prf(s,k). letfun hkdf_expand_label(k:bitstring,l:label,h:bitstring) = prf(k,(l,h)). letfun derive_secret(k:bitstring,l:label,m:bitstring) = hkdf_expand_label(k,l,hash(StrongHash,m)). letfun kdf_0() = hkdf_extract(zero,zero). letfun kdf_es(psk:preSharedKey) = let es = hkdf_extract(zero,psk2b(psk)) in let kb = derive_secret(es,tls13_resumption_psk_binder_key,zero) in (es,b2mk(kb)). letfun kdf_k0(es:bitstring,log:bitstring) = let atsc0 = derive_secret(es, tls13_client_early_traffic_secret, log) in let kc0 = hkdf_expand_label(atsc0,tls13_key,zero) in let ems0 = derive_secret(es,tls13_early_exporter_master_secret,log) in (b2ae(kc0),ems0). letfun kdf_hs(es:bitstring,e:bitstring) = hkdf_extract(es,e). letfun kdf_ms(hs:bitstring,log:bitstring) = let ms = hkdf_extract(hs , zero) in let htsc = derive_secret(hs, tls13_client_handshake_traffic_secret, log) in let htss = derive_secret(hs, tls13_server_handshake_traffic_secret, log) in let kch = hkdf_expand_label(htsc,tls13_key,zero) in let kcm = hkdf_expand_label(htsc,tls13_finished,zero) in let ksh = hkdf_expand_label(htss,tls13_key,zero) in let ksm = hkdf_expand_label(htss,tls13_finished,zero) in (ms,b2ae(kch),b2ae(ksh),b2mk(kcm),b2mk(ksm)). letfun kdf_k(ms:bitstring,log:bitstring) = let atsc = derive_secret(ms, tls13_client_application_traffic_secret, log) in let atss = derive_secret(ms, tls13_server_application_traffic_secret, log) in let ems = derive_secret(ms, tls13_exporter_master_secret, log) in let kc = hkdf_expand_label(atsc,tls13_key,zero) in let ks = hkdf_expand_label(atss,tls13_key,zero) in (b2ae(kc),b2ae(ks),ems). letfun kdf_psk(ms:bitstring, log:bitstring) = derive_secret(ms,tls13_resumption_master_secret,log).
包含一些未提到的方法,稍后将会讲到,这里仅做参考。
在通信协议中,可能出现第三者作为“中介”,伪造成对方和双方通信。在Diffie-Hellman密钥交换协议中,假设有一个攻击者Eve,他首先谎称自己是Bob和Alice建立通信,在得到Alice传过来的 A = g a A=g^a A=ga后,扮成Alice和Bob通信,将 C = g c C=g^c C=gc发给Bob,此时Bob生成共享密钥 C B = g b c C^B=g^{bc} CB=gbc,并将 B = g b B=g^b B=gb发给Eve,Eve收到B后,计算 C c = g b c C^c=g^{bc} Cc=gbc,作为其和Bob的共享密钥,同时选取随机数d,将 D = g d D=g^d D=gd发给Alice,并将 A d = g a d A^d=g^{ad} Ad=gad作为和Alice的共享密钥。这样Eve和Alice和Bob各有一对共享密钥。如果Alice通过共享密钥和Bob通信,则加密信息会先发给中间人Eve,Eve进行解密后使用和Bob的共享密钥再加密,将加密信息转发给Bob。这样Alice和Bob都认为消息是通过共享密钥直接发给对方的,而事实上它们的共享密钥并不相同,有一个中介Eve收取了它们所有的密钥并使消息都中中转给了Eve,Eve解密获得全部消息。
具体流程可参考下图:
该部分参考TLS 1.3文档。
TLS的主要目的是为两台通信计算机提供一个安全的交流通道,这个通道应该保持以下三条属性:
TLS协议包含一下两个主要部分:
Client Server Key ^ ClientHello Exch | + key_share* | + pre_shared_key_modes* v + pre_shared_key* ------> ServerHello ^ Key + key_share* | Exch + pre_shared_key* v {EncryptedExtensions} ^ Server {CertificateRequest*} v Params {Certificate*} ^ {CertificateVerify*} | Auth {Finished} v <-------- [Application Data*] ^ {Certificate*} Auth | {CertificateVerify*} v {Finished} --------> [Application Data] <-------> [Application Data] +表示在之前提到的消息 *表示可选项 {}表示被握手得到的traffic secret保护 []表示被对应于特定会话的traffic_secret_N保护
握手过程可以分为三步:
在密钥交换的过程中,客户端发送ClientHello消息,这个消息包含一个随机数(防止降级攻击,稍后会提到)、提供的协议版本、各种密钥。服务器处理ClientHello并决定合适的密码学参数。随后发出ServerHello作为响应,这个响应中包含协商的参数以及服务器决定使用的密钥。
随后,服务器发送如下两条信息:
经历以上三个过程后,握手完成,此时可以传输应用层的数据。注意在客户端发送Finished之前不允许传输应用数据,这也是安全性论证的一个点,即论证TLS 1.3协议可以防止传输应用数据给未Finish认证的对象。
当客户端在ClientHello中传输的key_share不被服务器支持,服务器将响应HelloRetryRequest,客户端需要使用一个正确的key_share扩展再次发出请求。如果没有协商到合适的密码学参数(比如客户端的supported_groups和服务器不重合),服务器必须中止握手过程并发出警告。
完整过程如下:
Client Server ClientHello + key_share --------> <-------- HelloRetryRequest + key_share ClientHello + key_share --------> ServerHello + key_share {EncryptedExtensions} {CertificateRequest*} {Certificate*} {CertificateVerify*} {Finished} <-------- [Application Data*] {Certificate*} {CertificateVerify*} {Finished} --------> [Application Data] <-------> [Application Data]
当握手结束时,服务器可以给客户端一个预共享密钥。在之后的握手过程中,客户端可以复用该密钥。由于服务器已被认证,在再次使用PSK时,服务器无需发送Certificate和CertificateVerify。当客户端提供PSK时,应该再提供一个key_share扩展给服务器,以允许服务器丢弃psk开始一个完整的握手(如果需要的话)。服务器会响应一个预共享密钥,同时可以响应key_share扩展来进行(EC)DHE密钥的建立,提供前向安全性(forward secrecy,即攻击者获取某一会话所有密钥数据后仍不能对之前的所有密钥解密)。
Part 1 密钥交换信息
从ClientHello可以得到以下四组参数:
服务器在ServerHello中指明如下的参数:
当服务器不能协商参数时,需要中止握手过程并发送handshake_failure或insufficient_security警告。
ClientHello数据块的C语言表示(选自TLS 1.3-18官方文档):
uint16 ProtocolVersion;
opaque Random[32];
uint8 CipherSuite[2]; /* Cryptographic suite selector */
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<0..2^16-1>;
} ClientHello;
以下是逐项解释:
cipher_suites具体结构:
Components | Contents |
---|---|
TLS | 字符串"TLS" |
AEAD | AEAD算法 |
HASH | HKDF中使用的HASH算法 |
VALUE | 两字节的ID |
Value的取值:
Description | Value |
---|---|
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} |
结构体的C语言表示:
struct {
ProtocolVersion version;
Random random;
CipherSuite cipher_suite;
Extension extensions<0..2^16-1>;
} ServerHello;
以下是逐项解释:
由于TLS 1.2及以下版本有较多的安全漏洞,攻击者可能会把协议版本设为1.2及以下来进行降级攻击(downgrade attack)。random域的作用便是防止这一攻击。事实上,不同版本的协议中random必须有不同的模式。支持TLS 1.2或更低协议版本的服务器仅支持最后8个字节为44 4F 59 4E 47 52 44 01的random,支持TLS 1.1或更低协议版本的服务器仅支持最后8个字节为44 4F 57 4E 47 52 44 00的random。如果服务器不支持降级使用,必须检查的random的后8位不能是上面两种情况中的一种,否则中止握手并发出illegal_parameter警告。
原文档中有HelloRetryRequest的讲解,但这部分对我们对协议的安全验证不重要,在此略过。
扩展的数据结构可以如下表示:
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
supported_groups(10),
signature_algorithms(13),
key_share(40),pre_shared_key(41),
early_data(42),
supported_versions(43),
cookie(44),
psk_key_exchange_modes(45),
ticket_early_data_info(46),
(65535)
} ExtensionType;
除了NewSessionTicket消息和HelloRetryRequest中的cookie扩展外,服务器发送的扩展只能是客户端扩展的子集,否则客户端会中止握手并发出unsupported_extension警告。客户端发送和服务器同类型但extension_data长度为0的扩展来表示对该扩展类型的支持。
和客户端相同。
struct {
Extension extensions<0..2^16-1>;
} EncryptedExtensions;
opaque DistinguishedName<1..2^16-1>;
struct {
opaque certificate_extension_oid<1..2^8-1>;
opaque certificate_extension_values<0..2^16-1>;} CertificateExtension;
struct {
opaque certificate_request_context<0..2^8-1>;
SignatureScheme
supported_signature_algorithms<2..2^16-2>;
DistinguishedName certificate_authorities<0..2^16-1>;
CertificateExtension certificate_extensions<0..2^16-1>;
} CertificateRequest;
其余字段可以从字面意思理解。
在最后的认证阶段,使用如下信息作为输入:
基于这些输入,认证信息将包含:
finished_key =
HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
struct {
opaque verify_data[Hash.length];
} Finished;
verify_data = HMAC(finished_key, Hash(
Handshake Context +
Certificate* +
CertificateVerify*
)
)
服务器收到客户端的结束信息后,可能会发送NewSessionTicket消息。这个消息创建了一个共享密钥。如果需要更改密钥,可以发送Key Update消息。记录层将传输的数据分块,对每一块进行AEAD加密。AEAD的输入由密钥、nonce(sequence number/client_write_iv/server_write_iv)、原始文本、附加数据组成。其中附加数据为空。
A
E
A
D
E
n
c
r
y
p
t
e
d
=
A
E
A
D
−
E
n
c
r
y
p
t
(
w
r
i
t
e
_
k
e
y
,
n
o
n
c
e
,
p
l
a
i
n
t
e
x
t
o
f
f
r
a
g
m
e
n
t
)
AEADEncrypted = AEAD-Encrypt(write\_key, nonce, plaintext\ of\ fragment)
AEADEncrypted=AEAD−Encrypt(write_key,nonce,plaintext of fragment)
p
l
a
i
n
t
e
x
t
o
f
f
r
a
g
m
e
n
t
=
A
E
A
D
−
D
e
c
r
y
p
t
(
w
r
i
t
e
_
k
e
y
,
n
o
n
c
e
,
A
E
A
D
E
n
c
r
y
p
t
e
d
)
plaintext\ of\ fragment = AEAD-Decrypt(write\_key, nonce, AEADEncrypted)
plaintext of fragment=AEAD−Decrypt(write_key,nonce,AEADEncrypted)
64位序列号一开始被初始化为0,每次读或写record都会增加。per-record nonce长度为64位,由以下步骤组成:
其中的key schedule由以下公式导出:
k
d
f
0
=
h
k
d
f
−
e
x
t
r
a
c
t
(
0
l
e
n
H
(
)
,
0
l
e
n
H
(
)
)
kdf_0 = hkdf-extract(0^{len_{H()}},0^{len_{H()}})
kdf0=hkdf−extract(0lenH(),0lenH())
k
d
f
h
s
(
e
s
,
e
)
=
h
k
d
f
−
e
x
t
r
a
c
t
(
e
s
,
e
)
kdf_{hs}(es,e) = hkdf-extract(es,e)
kdfhs(es,e)=hkdf−extract(es,e)
k
d
f
m
s
(
h
s
,
l
o
g
1
)
=
m
s
,
k
c
h
,
k
s
h
,
k
c
m
,
k
s
m
w
h
e
r
e
kdf_{ms}(hs,log_1) = ms,k_c^h,k_s^h,k_c^m,k_s^m\ where
kdfms(hs,log1)=ms,kch,ksh,kcm,ksm where
m
s
=
h
k
d
f
−
e
x
t
r
a
c
t
(
h
s
;
0
l
e
n
H
(
)
)
ms = hkdf-extract(hs; 0^{len_{H()}})
ms=hkdf−extract(hs;0lenH())
h
t
s
c
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
h
s
,
h
t
s
c
,
l
o
g
1
)
hts_c = derive-secret(hs,hts_c,log_1)
htsc=derive−secret(hs,htsc,log1)
h
t
s
s
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
h
s
,
h
t
s
s
,
l
o
g
1
)
hts_s = derive-secret(hs,hts_s,log_1)
htss=derive−secret(hs,htss,log1)
k
c
h
=
h
k
d
f
−
e
x
p
a
n
d
−
l
a
b
e
l
(
h
t
s
c
,
k
e
y
,
“
”
)
k^h_c = hkdf-expand-label(hts_c,key,“”)
kch=hkdf−expand−label(htsc,key,“”)
k
c
m
=
h
k
d
f
−
e
x
p
a
n
d
−
l
a
b
e
l
(
h
t
s
c
,
f
i
n
i
s
h
e
d
,
“
”
)
k^m_c = hkdf-expand-label(hts_c,finished,“”)
kcm=hkdf−expand−label(htsc,finished,“”)
k
s
h
=
h
k
d
f
−
e
x
p
a
n
d
−
l
a
b
e
l
(
h
t
s
s
,
k
e
y
,
“
”
)
k^h_s = hkdf-expand-label(hts_s,key,“”)
ksh=hkdf−expand−label(htss,key,“”)
k
s
m
=
h
k
d
f
−
e
x
p
a
n
d
−
l
a
b
e
l
(
h
t
s
s
,
f
i
n
i
s
h
e
d
,
“
”
)
k^m_s = hkdf-expand-label(hts_s,finished,“”)
ksm=hkdf−expand−label(htss,finished,“”)
k
d
f
k
(
m
s
,
l
o
g
4
)
=
k
c
,
k
s
,
e
m
s
w
h
e
r
e
kdf_k(ms,log_4) = k_c,k_s,ems\ where
kdfk(ms,log4)=kc,ks,ems where
a
t
s
c
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
m
s
,
a
t
s
c
,
l
o
g
4
)
ats_c = derive-secret(ms,ats_c,log_4)
atsc=derive−secret(ms,atsc,log4)
a
t
s
s
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
m
s
,
a
t
s
s
,
l
o
g
4
)
ats_s = derive-secret(ms,ats_s,log_4)
atss=derive−secret(ms,atss,log4)
e
m
s
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
m
s
,
e
m
s
,
l
o
g
4
)
ems = derive-secret(ms,ems,log_4)
ems=derive−secret(ms,ems,log4)
k
c
=
h
k
d
f
−
e
x
p
a
n
d
−
l
a
b
e
l
(
a
t
s
c
,
k
e
y
,
“
”
)
k_c = hkdf-expand-label(ats_c,key,“”)
kc=hkdf−expand−label(atsc,key,“”)
k
s
=
h
k
d
f
−
e
x
p
a
n
d
−
l
a
b
e
l
(
a
t
s
s
,
k
e
y
,
“
”
)
k_s = hkdf-expand-label(ats_s,key,“”)
ks=hkdf−expand−label(atss,key,“”)
k
d
f
p
s
k
(
m
s
,
l
o
g
7
)
=
p
s
k
′
w
h
e
r
e
kdf_{psk}(ms,log_7) = psk'\ where
kdfpsk(ms,log7)=psk′ where
p
s
k
′
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
m
s
,
r
m
s
,
l
o
g
7
)
psk' = derive-secret(ms,rms,log_7)
psk′=derive−secret(ms,rms,log7)
PSK-based Key Schedule:
k
d
f
e
s
(
p
s
k
)
=
e
s
,
k
b
w
h
e
r
e
kdf_{es}(psk) = es,k^b\ where
kdfes(psk)=es,kb where
e
s
=
h
k
d
f
−
e
x
t
r
a
c
t
(
0
l
e
n
H
(
)
,
p
s
k
)
es = hkdf-extract(0^{len_{H()}},psk)
es=hkdf−extract(0lenH(),psk)
k
b
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
e
s
,
p
b
k
,
“
”
)
k_b = derive-secret(es,pbk,“”)
kb=derive−secret(es,pbk,“”)
k
d
f
0
R
T
T
(
e
s
,
l
o
g
1
)
=
k
c
w
h
e
r
e
kdf_{0RTT}(es,log_1) = k_c\ where
kdf0RTT(es,log1)=kc where
e
t
s
c
=
d
e
r
i
v
e
−
s
e
c
r
e
t
(
e
s
,
e
t
s
c
,
l
o
g
1
)
ets_c = derive-secret(es,ets_c,log_1)
etsc=derive−secret(es,etsc,log1)
k
c
=
h
k
d
f
−
e
x
p
a
n
d
−
l
a
b
e
l
(
e
t
s
c
,
k
e
y
,
“
”
)
k_c = hkdf-expand-label(ets_c,key,“”)
kc=hkdf−expand−label(etsc,key,“”)
其中H()代表SHA-256,
l
e
n
H
(
)
=
32
b
y
t
e
s
len_{H()}=32\ bytes
lenH()=32 bytes。
以下代码部分新增注释。
(********************************************************) (* Message Formats, Session State *) (********************************************************) type random. type version. const TLS12, TLS13: version. type kex_alg. type rsa_alg. fun RSA(rsa_alg):kex_alg [data]. fun DHE(group):kex_alg [data]. fun DHE_13(group,element): kex_alg [data]. type psk_type. fun Binder(bitstring): psk_type [data]. fun NoBinder(): psk_type [data]. type params. fun nego(version,kex_alg,hash_alg,ae_alg,psk_type): params [data]. type msg. fun msg2bytes(msg):bitstring [data,typeConverter]. fun CH(random,params):msg [data]. (*Client Hello*) fun SH(random,params):msg [data]. (*Server Hello*) fun CRT(pubkey):msg [data]. fun SKE(group,element,bitstring):msg [data]. fun CKE(bitstring): msg [data]. fun CV(bitstring):msg [data]. fun FIN(bitstring):msg [data]. (*Finish message*) event ClientOffersVersion(random,version). event ClientOffersKEX(random,kex_alg). event ClientOffersAE(random,ae_alg). event ClientOffersHash(random,hash_alg). event ClientFinished(version,random,random, preSharedKey,pubkey, params,params, ae_key,ae_key,bitstring,bitstring). event ClientFinished0(version,random,preSharedKey, params,ae_key,bitstring). event ServerChoosesVersion(random,random,pubkey,version). event ServerChoosesKEX(random,random,pubkey,version,kex_alg). event ServerChoosesAE(random,random,pubkey,version,ae_alg). event ServerChoosesHash(random,random,pubkey,version,hash_alg). event ServerFinished0(version,random,preSharedKey, params,ae_key). event ServerFinished(version,random,random, preSharedKey,pubkey, params,params, ae_key,ae_key,bitstring,bitstring). event PreServerFinished(version,random,random, preSharedKey,pubkey, params,params, ae_key,ae_key,bitstring). table clientSession(random,random,preSharedKey,pubkey, params,params, ae_key,ae_key,bitstring,bitstring). table serverSession(random,random,preSharedKey,pubkey, params,params, ae_key,ae_key,bitstring,bitstring). table serverSession0_5(random,random,preSharedKey,pubkey, params,params, ae_key,ae_key,bitstring). table clientSession0(random,preSharedKey,params,ae_key,bitstring). table serverSession0(random,preSharedKey,params,ae_key,bitstring).
各部分含义较明显,其中clientSession0保存的是Hello问候结束后创建的会话,serverSession0同理。
首先客户端发出ClientHello(简记为CH):
let Client13() = (get preSharedKeys(a,b,psk) in in (io,ioffer:params); let nego(=TLS13,DHE_13(g,eee),hhh,aaa,pt) = ioffer in new cr:random; let (x:bitstring,gx:element) = dh_keygen(g) in let (early_secret:bitstring,kb:mac_key) = kdf_es(psk) in let zoffer = nego(TLS13,DHE_13(g,gx),hhh,aaa,Binder(zero)) in let pt = Binder(hmac(StrongHash,kb,msg2bytes(CH(cr,zoffer)))) in let offer = nego(TLS13,DHE_13(g,gx),hhh,aaa,pt) in let ch = CH(cr,offer) in event ClientOffersVersion(cr,TLS13); event ClientOffersKEX(cr,DHE_13(g,gx)); event ClientOffersAE(cr,aaa); event ClientOffersHash(cr,hhh); out(io,ch); let (kc0:ae_key,ems0:bitstring) = kdf_k0(early_secret,msg2bytes(ch)) in insert clientSession0(cr,psk,offer,kc0,ems0); ...(omitted)
ch包含了客户端生成的随机数、共享密钥的一部分等。
服务器收到ch,发送ServerHello(SH):
let Server13() = (get preSharedKeys(a,b,psk) in in(io,ch:msg); let CH(cr,offer) = ch in let nego(=TLS13,DHE_13(g,gx),hhh,aaa,Binder(m)) = offer in let (early_secret:bitstring,kb:mac_key) = kdf_es(psk) in let zoffer = nego(TLS13,DHE_13(g,gx),hhh,aaa,Binder(zero)) in if m = hmac(StrongHash,kb,msg2bytes(CH(cr,zoffer))) then let (kc0:ae_key,ems0:bitstring) = kdf_k0(early_secret,msg2bytes(ch)) in insert serverSession0(cr,psk,offer,kc0,ems0); new sr:random; in(io,SH(xxx,mode)); let nego(=TLS13,DHE_13(=g,eee),h,a,pt) = mode in let (y:bitstring,gy:element) = dh_keygen(g) in let mode = nego(TLS13,DHE_13(g,gy),h,a,pt) in out(io,SH(sr,mode)); ...omitted
客户端收到mode,将其解析,随后生成密钥信息发给服务器:
in(io,SH(sr,mode));
let nego(=TLS13,DHE_13(=g,gy),h,a,spt) = mode in
let log = (ch,SH(sr,mode)) in
let gxy = e2b(dh_exp(g,gy,x)) in
let handshake_secret = kdf_hs(early_secret,gxy) in
let (master_secret:bitstring,chk:ae_key,shk:ae_key,cfin:mac_key,sfin:mac_key) =
kdf_ms(handshake_secret,log) in
out(io,(chk,shk));
服务器也类似:
let log = (ch,SH(sr,mode)) in
get longTermKeys_tbl(sn,sk,p) in
event ServerChoosesVersion(cr,sr,p,TLS13);
event ServerChoosesKEX(cr,sr,p,TLS13,DHE_13(g,gy));
event ServerChoosesAE(cr,sr,p,TLS13,a);
event ServerChoosesHash(cr,sr,p,TLS13,h);
let gxy = e2b(dh_exp(g,gx,y)) in
let handshake_secret = kdf_hs(early_secret,gxy) in
let (master_secret:bitstring,chk:ae_key,shk:ae_key,cfin:mac_key,sfin:mac_key) =
kdf_ms(handshake_secret,log) in
out(io,(chk,shk));
服务器开始认证,认证结束后发送Finish:
out(io,CRT(p));
let log = (log,CRT(p)) in
let sg = sign(sk,hash(h,log)) in
out(io,CV(sg));
let log = (log,CV(sg)) in
let m1 = hmac(StrongHash,sfin,log) in
let log = (log,FIN(m1)) in
let (cak:ae_key,sak:ae_key,ems:bitstring) = kdf_k(master_secret,log) in
event PreServerFinished(TLS13,cr,sr,psk,p,offer,mode,cak,sak,ems);
out(io,FIN(m1));
客户端收到服务器的认证消息进行认证,最后也发送Finish:
in(io,CRT(p)); (*receive pks*) let log = (log,CRT(p)) in get longTermKeys_tbl(sn,xxx,=p) in in(io,CV(s)); (*s=sign(sk,hash(h,log))*) if verify(p,hash(h,log),s) = true then let log = (log,CV(s)) in in(io,FIN(m1)); if m1 = hmac(StrongHash,sfin,log) then ( let log = (log,FIN(m1)) in let (cak:ae_key,sak:ae_key,ems:bitstring) = kdf_k(master_secret,log) in let m2 = hmac(StrongHash,cfin,log) in let log = (log,FIN(m2)) in let rms = kdf_psk(master_secret,log) in event ClientFinished(TLS13,cr,sr,psk,p,offer,mode,cak,sak,ems,rms); insert clientSession(cr,sr,psk,p,offer,mode,cak,sak,ems,rms); out(io,FIN(m2)))).
服务器收到后发出结束事件并创建信的会话:
in(io,FIN(m2));
if m2 = hmac(StrongHash,cfin,log) then
let log = (log,FIN(m2)) in
let rms = kdf_psk(master_secret,log) in
event ServerFinished(TLS13,cr,sr,psk,p,offer,mode,cak,sak,ems,rms);
insert serverSession(cr,sr,psk,p,offer,mode,cak,sak,ems,rms);
phase 1;
event PostSessionCompromisedKey(pk(sk));
out(io,sk)).
appData模拟应用层数据的传输,传输方进行AEAD编码,接受方进行AEAD解码,随后响应信息给传输方。
(********************************************************) (* Application Data Client and Server (+Record Layer) *) (********************************************************) event ClientSends0(version,random,preSharedKey,bitstring,bitstring,bitstring). event ServerReceives0(version,random,preSharedKey,bitstring,bitstring,bitstring). event ClientSends(version,random,random,preSharedKey,pubkey,bitstring,bitstring,bitstring). event ServerSends(version,random,random,preSharedKey,pubkey,bitstring,bitstring,bitstring). event ClientReceives(version,random,random,preSharedKey,pubkey,bitstring,bitstring,bitstring). event ServerReceives(version,random,random,preSharedKey,pubkey,bitstring,bitstring,bitstring). fun m_c0(version,random,preSharedKey): bitstring [private]. fun m_s(version,random,random,pubkey,preSharedKey): bitstring [private]. fun m_c(version,random,random,pubkey,preSharedKey): bitstring [private]. let appData() = (get clientSession0(cr,psk,o,kc0,ems0) in let nego(v,kkk,hhh,a,pt) = o in in (io,(n:bitstring, ad:bitstring)); let mesg = m_c0(TLS13,cr,psk) in event ClientSends0(TLS13,cr,psk,n,ad,mesg); out (io,aead_enc(a,kc0,n,ad,mesg))) (*fun aead_enc(ae_alg, ae_key, bitstring, bitstring, bitstring): bitstring.*) | (get serverSession0(cr,psk,o,kc0,ems0) in let nego(v,kkk,hhh,a,pt) = o in in (io,(n:bitstring, ad:bitstring, c:bitstring)); let f = aead_dec(a,kc0,n,ad,c) in event ServerReceives0(TLS13,cr,psk,n,ad,f)) | (get serverSession0_5(cr,sr,psk,ps,o,m,kc,ks,ems) in let nego(v,kkk,hhh,a,pt) = m in in (io,(n:bitstring, ad:bitstring)); let mesg = m_s(v,cr,sr,ps,psk) in event ServerSends(v,cr,sr,psk,ps,n,ad,mesg); out (io,aead_enc(a,ks,n,ad,mesg))) | (get clientSession(cr,sr,psk,ps,o,m,kc,ks,ems,rms) in let nego(v,kkk,hhh,a,pt) = m in in (io,(n:bitstring, ad:bitstring)); let mesg = m_c(v,cr,sr,ps,psk) in event ClientSends(v,cr,sr,psk,ps,n,ad,mesg); out (io,aead_enc(a,kc,n,ad,mesg))) | (get serverSession(cr,sr,psk,ps,o,m,kc,ks,ems,rms) in let nego(v,kkk,hhh,a,pt) = m in in (io,(n:bitstring, ad:bitstring, c:bitstring)); let f = aead_dec(a,kc,n,ad,c) in event ServerReceives(v,cr,sr,psk,ps,n,ad,f)) | (get serverSession(cr,sr,psk,ps,o,m,kc,ks,ems,rms) in let nego(v,kkk,hhh,a,pt) = m in in (io,(n:bitstring, ad:bitstring)); let mesg = m_s(v,cr,sr,ps,psk) in event ServerSends(v,cr,sr,psk,ps,n,ad,mesg); out (io,aead_enc(a,ks,n,ad,mesg))) | (get clientSession(cr,sr,psk,ps,o,m,kc,ks,ems,rms) in let nego(v,kkk,hhh,a,pt) = m in in (io,(n:bitstring, ad:bitstring, c:bitstring)); let f = aead_dec(a,ks,n,ad,c) in event ClientReceives(v,cr,sr,psk,ps,n,ad,f)) .
event ClientAEKeyLeaked(version,random,random,preSharedKey,pubkey). event ServerAEKeyLeaked(version,random,random,preSharedKey,pubkey). event ClientAEKeyLeaked0(version,random,preSharedKey,params). event ServerAEKeyLeaked0(version,random,preSharedKey,params). let secrecyQuery() = (get clientSession(cr,sr,psk,p,o,m,ck,sk,cb,ms) in let nego(v,kkk,hhh,aaa,ppp) = m in in (io,=ck); event ClientAEKeyLeaked(v,cr,sr,psk,p)) | (get serverSession(cr,sr,psk,p,o,m,ck,sk,cb,ms) in let nego(v,kkk,hhh,aaa,ppp) = m in in (io,=sk); event ServerAEKeyLeaked(v,cr,sr,psk,p)) | (get clientSession0(cr,psk,o,ck,ems) in in (io,=ck); event ClientAEKeyLeaked0(TLS13,cr,psk,o)) | (get serverSession0(cr,psk,o,ck,ems) in in (io,=ck); event ServerAEKeyLeaked0(TLS13,cr,psk,o)).
有一些会话参数,一旦相等必须说明同属于一个会话。考虑它的反面,即这个参数相等但是其他参数不完全相同。测试结果应该全为False。
event MatchingChannelBinding(version,random,random,pubkey,version,random,random,pubkey). event MatchingResumptionSecret(version,random,random,pubkey,version,random,random,pubkey). event MatchingAEKey(version,random,random,pubkey,version,random,random,pubkey). event MatchingAEKey0(version,random,preSharedKey,params,random,preSharedKey,params). event MatchingEMS0(version,random,preSharedKey,params,random,preSharedKey,params). let channelBindingQuery() = (get clientSession0(cr,psk,o,ck,ems) in get serverSession0(cr',psk',o',=ck,ems) in if (cr <> cr' || psk <> psk' || o <> o') then event MatchingAEKey0(TLS13,cr,psk,o,cr',psk',o')) | (get clientSession0(cr,psk,o,ck,ems) in get serverSession0(cr',psk',o',ck,=ems) in if (cr <> cr' || psk <> psk' || o <> o') then event MatchingEMS0(TLS13,cr,psk,o,cr',psk',o')) | (get clientSession(cr,sr,psk,p,o,m,ck,sk,cb,ms) in get serverSession(cr',sr',psk',p',o',m',ck',sk',=cb,ms') in let nego(v,kkk,hhh,aaa,ppp) = m in let nego(v',kkk',hhh',aaa',ppp') = m' in if (cr <> cr' || sr <> sr' || p <> p') then event MatchingChannelBinding(v,cr,sr,p,v',cr',sr',p')) | (get clientSession(cr,sr,psk,p,o,m,ck,sk,cb,ms) in get serverSession(cr',sr',psk',p',o',m',ck',sk',cb',=ms) in let nego(v,kkk,hhh,aaa,ppp) = m in let nego(v',kkk',hhh',aaa',ppp') = m' in if (cr <> cr' || sr <> sr' || p <> p') then event MatchingResumptionSecret(v,cr,sr,p,v',cr',sr',p')) | (get clientSession(cr,sr,psk,p,o,m,ck,sk,cb,ms) in get serverSession(cr',sr',psk',p',o',m',=ck,=sk,cb',ms') in let nego(v,kkk,hhh,aaa,ppp) = m in let nego(v',kkk',hhh',aaa',ppp') = m' in if (cr <> cr' || sr <> sr' || p <> p') then event MatchingAEKey(v,cr,sr,p,v',cr',sr',p')).
应该是先服务器发出Finish再客户端Finish。除此之外还有一些其他结束顺序相关:
(********************************************************) (* Sanity Queries: should all be false *) (********************************************************) query cr:random, sr:random, psk:preSharedKey,p:pubkey,o:params, m:params, ck:ae_key, sk:ae_key, cb:bitstring, ms:bitstring; event(ClientFinished(TLS12,cr,sr,psk,p,m,o,ck,sk,cb,ms)) ==> event(ServerFinished(TLS12,cr,sr,psk,p,m,o,ck,sk,cb,ms)). query cr:random, sr:random, psk:preSharedKey,p:pubkey,o:params, m:params, ck:ae_key, sk:ae_key, cb:bitstring, ms:bitstring; event(ClientFinished(TLS13,cr,sr,psk,p,m,o,ck,sk,cb,ms)) ==> event(PreServerFinished(TLS13,cr,sr,psk,p,m,o,ck,sk,cb)). query cr:random, sr:random, psk:preSharedKey,p:pubkey,o:params, m:params, ck:ae_key, sk:ae_key, cb:bitstring, ms:bitstring; event(ServerFinished(TLS12,cr,sr,psk,p,m,o,ck,sk,cb,ms)). query cr:random, sr:random, psk:preSharedKey,p:pubkey,o:params, m:params, ck:ae_key, sk:ae_key, cb:bitstring, ms:bitstring; event(ClientFinished(TLS12,cr,sr,psk,p,m,o,ck,sk,cb,ms)). query cr:random, sr:random, psk:preSharedKey,p:pubkey,o:params, m:params, ck:ae_key, sk:ae_key, cb:bitstring, ms:bitstring; event(ServerFinished(TLS13,cr,sr,psk,p,m,o,ck,sk,cb,ms)). query cr:random, sr:random, psk:preSharedKey,p:pubkey,o:params, m:params, ck:ae_key, sk:ae_key, cb:bitstring, ms:bitstring; event(PreServerFinished(TLS13,cr,sr,psk,p,m,o,ck,sk,cb)). query cr:random, sr:random, psk:preSharedKey,p:pubkey,o:params, m:params, ck:ae_key, sk:ae_key, cb:bitstring, ms:bitstring; event(ClientFinished(TLS13,cr,sr,psk,p,m,o,ck,sk,cb,ms)).
(* Following secrecy queries should fail *)
query cr:random, sr:random, psk:preSharedKey, p:pubkey, ms:bitstring, aek:ae_key, cb:bitstring, cr':random, sr':random, v:version, e:element;
event(ClientAEKeyLeaked(TLS13,cr,sr,psk,p)) ==>
(event(WeakOrCompromisedKey(p)) && (psk = NoPSK || event(CompromisedPreSharedKey(psk)))) ||
event(ServerChoosesKEX(cr,sr,p,TLS13,DHE_13(WeakDH,e))).
query cr:random, sr:random, psk:preSharedKey, p:pubkey, ms:bitstring, aek:ae_key, cb:bitstring, cr':random, sr':random, v:version, e:element;
event(ClientAEKeyLeaked(TLS13,cr,sr,psk,p)) ==>
(event(WeakOrCompromisedKey(p)) && (psk = NoPSK || event(CompromisedPreSharedKey(psk)))) ||
event(ServerChoosesHash(cr',sr',p,TLS13,WeakHash)).
query cr:random, sr:random, psk:preSharedKey, p:pubkey, ms:bitstring, aek:ae_key, cb:bitstring, cr':random, sr':random, v:version, e:element;
event(ClientAEKeyLeaked(TLS13,cr,sr,psk,p)) ==>
event(ServerChoosesKEX(cr,sr,p,TLS13,DHE_13(WeakDH,e))) ||
event(ServerChoosesHash(cr',sr',p,TLS13,WeakHash)).
其余验证点从略(可以从稍后的验证结果中找出)。
Verification summary: Query event(ClientFinished(TLS12,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5,ms_7)) ==> event(ServerFinished(TLS12,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5,ms_7)) is true. Query event(ClientFinished(TLS13,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5,ms_7)) ==> event(PreServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5)) is false. Query not event(ServerFinished(TLS12,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5,ms_7)) is true. Query not event(ClientFinished(TLS12,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5,ms_7)) is true. Query not event(ServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5,ms_7)) is false. Query not event(PreServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5)) is false. Query not event(ClientFinished(TLS13,cr_18,sr_12,psk_18,p_7,m_13,o_16,ck_10,sk_6,cb_5,ms_7)) is false. Query event(ClientAEKeyLeaked(TLS13,cr_18,sr_12,psk_18,p_7)) ==> (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) is false. Query event(ClientAEKeyLeaked(TLS13,cr_18,sr_12,psk_18,p_7)) ==> (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is false. Query event(ClientAEKeyLeaked(TLS13,cr_18,sr_12,psk_18,p_7)) ==> event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is false. Query inj-event(ClientFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5,ms_7)) ==> inj-event(PreServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5)) || (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) is false. Query inj-event(ClientFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5,ms_7)) ==> inj-event(PreServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5)) || (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) is false. Query inj-event(ClientFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5,ms_7)) ==> inj-event(PreServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5)) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is false. Query inj-event(ClientFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5,ms_7)) ==> inj-event(PreServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5)) is false. Query not event(MatchingChannelBinding(TLS13,cr_18,sr_12,p_7,TLS13,cr'_5,sr'_3,p'_3)) is true. Query not event(MatchingResumptionSecret(TLS13,cr_18,sr_12,p_7,TLS13,cr'_5,sr'_3,p'_3)) is true. Query not event(MatchingAEKey(TLS13,cr_18,sr_12,p_7,TLS13,cr'_5,sr'_3,p'_3)) is true. Query event(ClientAEKeyLeaked(TLS13,cr_18,sr_12,psk_18,p_7)) ==> (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is true. Query inj-event(ClientFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5,ms_7)) ==> inj-event(PreServerFinished(TLS13,cr_18,sr_12,psk_18,p_7,o_16,m_13,ck_10,sk_6,cb_5)) || (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is true. Query attacker_p1(m_c(TLS13,cr_18,sr_12,p_7,psk_18)) ==> (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesAE(cr_18,sr_12,p_7,TLS13,WeakAE)) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is true. Query event(ClientReceives(TLS13,cr_18,sr_12,psk_18,p_7,n_7,ad_7,m_13)) ==> event(ServerSends(TLS13,cr_18,sr_12,psk_18,p_7,n_7,ad_7,m_13)) || (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesAE(cr_18,sr_12,p_7,TLS13,WeakAE)) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is true. Query event(ServerReceives(TLS13,cr_18,sr_12,psk_18,p_7,n_7,ad_7,m_13)) ==> event(ClientSends(TLS13,cr_18,sr_12,psk_18,p_7,n_7,ad_7,m_13)) || psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)) || event(ServerChoosesAE(cr_18,sr_12,p_7,TLS13,WeakAE)) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is true. Query event(ServerReceives0(TLS13,cr_18,psk_18,n_7,ad_7,m_13)) ==> event(ClientSends0(TLS13,cr_18,psk_18,n_7,ad_7,m_13)) || psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)) || event(ClientOffersAE(cr_18,WeakAE)) is true.
通过前面几次查询对TLS 1.2和TLS 1.3的对比,发现TLS 1.3的安全性确实更强。除此之外,举一点进行详细说明:
Query event(ClientAEKeyLeaked(TLS13,cr_18,sr_12,psk_18,p_7)) ==> event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is false.
说明在选择WeakDH和WeakHash的情况下并不会造成AE密钥泄露。当然,在多种配置不当的条件下,还是会导致数据被窃取:
Query attacker_p1(m_c(TLS13,cr_18,sr_12,p_7,psk_18)) ==> (event(WeakOrCompromisedKey(p_7)) && (psk_18 = NoPSK || event(CompromisedPreSharedKey(psk_18)))) || event(ServerChoosesAE(cr_18,sr_12,p_7,TLS13,WeakAE)) || event(ServerChoosesKEX(cr_18,sr_12,p_7,TLS13,DHE_13(WeakDH,e))) || event(ServerChoosesHash(cr'_5,sr'_3,p_7,TLS13,WeakHash)) is true.
其余结果可以类似分析。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。