BOLT 8:加密和认证传输
本文档是闪电网络 BOLT 8 规范,详细描述了闪电网络节点之间通信的加密和认证传输协议。该协议基于 Noise Protocol Framework,采用 Noise_XK 握手方式进行密钥交换和身份验证,所有后续消息通过 ChaChaPoly-1305 进行加密和认证,确保通信的机密性和完整性。
BOLT #8:加密和认证传输
Lightning 节点之间的所有通信都经过加密,以便为节点之间的所有记录提供保密性,并且经过身份验证以避免恶意干扰。每个节点都有一个已知的长期标识符,它是 Bitcoin 的 secp256k1 曲线上的公钥。这个长期公钥在协议中使用,以建立与对等节点的加密和认证连接,并验证代表节点发布的任何信息。
目录
密码消息传递概述
在发送任何 Lightning 消息之前,节点必须首先启动加密会话状态,该状态用于加密和认证节点之间发送的所有消息。此加密会话状态的初始化与任何内部协议消息头或约定完全不同。
两个节点之间的记录分为两个不同的部分:
- 在任何实际数据传输之前,两个节点都参与加认证密钥协商握手,该握手基于 Noise 协议框架<sup>2</sup>。
- 如果初始握手成功,则节点进入 Lightning 消息交换阶段。在 Lightning 消息交换阶段,所有消息都是带有相关数据的认证加密 (AEAD) 密文。
认证密钥协商握手
为认证密钥交换选择的握手是 Noise_XK。作为预消息,发起方必须知道响应方的身份公钥。这为响应方提供了一定程度的身份隐藏,因为其静态公钥在握手期间从不传输。相反,通过一系列椭圆曲线 Diffie-Hellman (ECDH) 操作,然后进行 MAC 检查,隐式地实现身份验证。
认证密钥协商 (Noise_XK) 分为三个不同的步骤(行为)。在握手的每个行为中,都会发生以下情况:一些(可能已加密的)密钥材料被发送到另一方;根据正在执行的行为,执行 ECDH,结果混合到当前的一组加密密钥中(ck 是链接密钥,k 是加密密钥);并发送一个带有零长度密文的 AEAD 负载。由于此负载没有长度,因此仅通过 MAC 发送。将 ECDH 输出混合到哈希摘要中会形成一个增量 TripleDH 握手。
使用 Noise 协议的语言,e 和 s(都是公钥,其中 e 是临时密钥,s 是静态密钥,在我们的例子中通常是 nodeid)表示可能已加密的密钥材料,es、ee 和 se 各自表示两个密钥之间的 ECDH 操作。握手布局如下:
Noise_XK(s, rs):
<- s
...
-> e, es
<- e, ee
-> s, se
所有通过线路发送的握手数据,包括密钥材料,都会增量地哈希到会话范围的“握手摘要”h 中。请注意,握手状态 h 永远不会在握手期间传输;相反,摘要用作零长度 AEAD 消息中的相关数据。
对发送的每个消息进行身份验证可确保中间人 (MITM) 没有修改或替换作为握手一部分发送的任何数据,因为如果这样做,另一侧的 MAC 检查将会失败。
接收方成功检查 MAC 意味着所有身份验证到目前为止已成功。如果在握手过程中 MAC 检查失败,则应立即终止连接。
握手版本控制
在初始握手期间发送的每个消息都以单个前导字节开始,该字节指示当前握手使用的版本。版本 0 表示不需要更改,而非零版本表示客户端已偏离本文档中最初指定的协议。
客户端必须拒绝使用未知版本发起的握手尝试。
Noise 协议实例化
Noise 协议的具体实例化需要定义三个抽象密码对象:哈希函数、椭圆曲线和 AEAD 密码方案。对于 Lightning,选择 SHA-256 作为哈希函数,secp256k1 作为椭圆曲线,ChaChaPoly-1305 作为 AEAD 结构。
使用的 ChaCha20 和 Poly1305 的组合必须符合 RFC 8439<sup>1</sup>。
Noise 的 Lightning 变体的官方协议名称是 Noise_XK_secp256k1_ChaChaPoly_SHA256。此值的 ASCII 字符串表示形式被哈希到摘要中,用于初始化起始握手状态。如果两个端点的协议名称不同,则握手过程会立即失败。
认证密钥交换握手规范
握手过程分为三个步骤(Act),需要 1.5 个往返。每个握手都是加固定大小的负载,没有任何标头或附加的元数据。每个 Act 的确切大小如下:
- Act One:50 字节
- Act Two:50 字节
- Act Three:66 字节
握手状态
在整个握手过程中,每一方都维护这些变量:
-
ck:链接密钥。此值是所有先前 ECDH 输出的累积哈希。在握手结束时,ck用于导出 Lightning 消息的加密密钥。 -
h:握手哈希。此值是在握手过程中到目前为止已发送和接收的加所有握手数据的累积哈希。 -
temp_k1、temp_k2、temp_k3:中间密钥。这些密钥用于加密和解密每个握手消息末尾的零长度 AEAD 负载。 -
e:一方的临时密钥对。对于每个会话,节点必须使用强大的加密随机性生成一个新的临时密钥。 -
s:一方的静态密钥对(ls代表本地,rs代表远程)
还将引用以下函数:
-
ECDH(k, rk):使用k(它是有效的secp256k1私钥)和rk(它是有效的公钥)执行椭圆曲线 Diffie-Hellman 操作- 返回值是生成的点的压缩格式的 SHA256。
-
HKDF(salt,ikm):RFC 5869<sup>3</sup> 中定义的函数,使用零长度info字段进行评估HKDF的所有调用都隐式地返回 64 字节的加密随机性,使用HKDF的提取和扩展组件。
-
encryptWithAD(k, n, ad, plaintext):输出encrypt(k, n, ad, plaintext)- 其中
encrypt是使用传递的参数对ChaCha20-Poly1305(IETF 变体)的评估,其中 noncen编码为 32 个零位,后跟一个小端 64 位值。注意:这遵循 Noise 协议的约定,而不是我们通常的端。
- 其中
-
decryptWithAD(k, n, ad, ciphertext):输出decrypt(k, n, ad, ciphertext)- 其中
decrypt是使用传递的参数对ChaCha20-Poly1305(IETF 变体)的评估,其中 noncen编码为 32 个零位,后跟一个小端 64 位值。
- 其中
-
generateKey():生成并返回一个新的secp256k1密钥对- 其中
generateKey返回的对象有两个属性:.pub,它返回一个表示公钥的抽象对象.priv,它表示用于生成公钥的私钥
- 其中该对象还具有一个方法:
.serializeCompressed()
- 其中
-
a || b表示两个字节字符串a和b的串联
握手状态初始化
在 Act One 开始之前,双方按如下方式初始化其每个会话的状态:
-
h = SHA-256(protocolName)- 其中
protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256"编码为 ASCII 字符串
- 其中
-
ck = h -
h = SHA-256(h || prologue)- 其中
prologue是 ASCII 字符串:lightning
- 其中
作为最后一步,双方将响应方的公钥混合到握手摘要中:
-
发起节点混合到响应节点的静态公钥中,该公钥以比特币的压缩格式序列化:
h = SHA-256(h || rs.pub.serializeCompressed())
-
响应节点混合到其本地静态公钥中,该公钥以比特币的压缩格式序列化:
h = SHA-256(h || ls.pub.serializeCompressed())
握手交换
Act One
-> e, es
Act One 从发起方发送到响应方。在 Act One 期间,发起方尝试满足响应方的隐式挑战。为了完成此挑战,发起方必须知道响应方的静态公钥。
握手消息正好是 50 个字节:1 个字节用于握手版本,33 个字节用于发起方的压缩临时公钥,16 个字节用于 poly1305 标签。
发送方操作:
e = generateKey()h = SHA-256(h || e.pub.serializeCompressed())- 新生成的临时密钥被累积到正在运行的握手摘要中。
es = ECDH(e.priv, rs)- 发起方在其新生成的临时密钥和远程节点的静态公钥之间执行 ECDH。
ck, temp_k1 = HKDF(ck, es)- 生成一个新的临时加密密钥,该密钥用于生成身份验证 MAC。
c = encryptWithAD(temp_k1, 0, h, zero)- 其中
zero是零长度明文
- 其中
h = SHA-256(h || c)- 最后,生成的密文被累积到身份验证握手摘要中。
- 通过网络缓冲区将
m = 0 || e.pub.serializeCompressed() || c发送到响应方。
接收方操作:
- 从网络缓冲区读取_正好_ 50 个字节。
- 将读取的消息 (
m) 解析为v、re和c:- 其中
v是m的_第一个_字节,re是m的下 33 个字节,c是m的最后 16 个字节 - 远程方的临时公钥 (
re) 的原始字节应使用仿射坐标反序列化为曲线上的一个点,如密钥的序列化组合格式编码的那样。
- 其中
- 如果
v是无法识别的握手版本,则响应方_必须_中止连接尝试。 h = SHA-256(h || re.serializeCompressed())- 响应方将发起方的临时密钥累积到身份验证握手摘要中。
es = ECDH(s.priv, re)- 响应方在其静态私钥和发起方的临时公钥之间执行 ECDH。
ck, temp_k1 = HKDF(ck, es)- 生成一个新的临时加密密钥,该密钥将很快用于检查身份验证 MAC。
p = decryptWithAD(temp_k1, 0, h, c)- 如果此操作中的 MAC 检查失败,则发起方_不_知道响应方的静态公钥。如果发生这种情况,则响应方_必须_终止连接,而不发送任何进一步的消息。
h = SHA-256(h || c)- 接收到的密文混合到握手摘要中。此步骤用于确保有效负载未被 MITM 修改。
Act Two
<- e, ee
Act Two 从响应方发送到发起方。Act Two 仅在 Act One 成功时才会发生。如果响应方能够正确解密和检查 Act One 末尾发送的标签的 MAC,则 Act One 成功。
握手消息_正好_是 50 个字节:1 个字节用于握手版本,33 个字节用于响应方的压缩临时公钥,16 个字节用于 poly1305 标签。
发送方操作:
e = generateKey()h = SHA-256(h || e.pub.serializeCompressed())- 新生成的临时密钥被累积到正在运行的握手摘要中。
ee = ECDH(e.priv, re)- 其中
re是发起方的临时密钥,该密钥在 Act One 期间收到
- 其中
ck, temp_k2 = HKDF(ck, ee)- 生成一个新的临时加密密钥,该密钥用于生成身份验证 MAC。
c = encryptWithAD(temp_k2, 0, h, zero)- 其中
zero是零长度明文
- 其中
h = SHA-256(h || c)- 最后,生成的密文被累积到身份验证握手摘要中。
- 通过网络缓冲区将
m = 0 || e.pub.serializeCompressed() || c发送到发起方。
接收方操作:
- 从网络缓冲区读取_正好_ 50 个字节。
- 将读取的消息 (
m) 解析为v、re和c:- 其中
v是m的_第一个_字节,re是m的下 33 个字节,c是m的最后 16 个字节。
- 其中
- 如果
v是无法识别的握手版本,则响应方_必须_中止连接尝试。 h = SHA-256(h || re.serializeCompressed())ee = ECDH(e.priv, re)- 其中
re是响应方的临时公钥 - 远程方的临时公钥 (
re) 的原始字节应使用仿射坐标反序列化为曲线上的一个点,如密钥的序列化组合格式编码的那样。
- 其中
ck, temp_k2 = HKDF(ck, ee)- 生成一个新的临时加密密钥,该密钥用于生成身份验证 MAC。
p = decryptWithAD(temp_k2, 0, h, c)- 如果此操作中的 MAC 检查失败,则发起方_必须_终止连接,而不发送任何进一步的消息。
h = SHA-256(h || c)- 接收到的密文混合到握手摘要中。此步骤用于确保有效负载未被 MITM 修改。
Act Three
-> s, se
Act Three 是本节中描述的认证密钥协商的最后阶段。此 Act 作为结论步骤从发起方发送到响应方。只有在 Act Two 成功时,才会执行 Act Three。在 Act Three 期间,发起方使用累积的 HKDF 派生密钥,通过_强_前向保密性加密将其静态公钥传输到响应方。
握手消息_正好_是 66 个字节:1 个字节用于握手版本,33 个字节用于使用 ChaCha20 流密码加密的静态公钥,16 个字节用于通过 AEAD 结构生成的加密后公钥的标签,以及 16 个字节用于最终身份验证标签。
发送方操作:
c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())- 其中
s是发起方的静态公钥
- 其中
h = SHA-256(h || c)se = ECDH(s.priv, re)- 其中
re是响应方的临时公钥
- 其中
ck, temp_k3 = HKDF(ck, se)- 最终的中间共享密钥混合到正在运行的链接密钥中。
t = encryptWithAD(temp_k3, 0, h, zero)- 其中
zero是零长度明文
- 其中
sk, rk = HKDF(ck, zero)- 其中
zero是零长度明文,sk是发起方用于加密发送给响应方的消息的密钥,rk是发起方用于解密响应方发送的消息的密钥 - 生成最终的加密密钥,该密钥将在会话期间用于发送和接收消息。
- 其中
rn = 0, sn = 0- 发送和接收 nonce 初始化为 0。
- 通过网络缓冲区发送
m = 0 || c || t。
接收方操作:
- 从网络缓冲区读取_正好_ 66 个字节。
- 将读取的消息 (
m) 解析为v、c和t:- 其中
v是m的_第一个_字节,c是m的下 49 个字节,t是m的最后 16 个字节
- 其中
- 如果
v是无法识别的握手版本,则响应方_必须_中止连接尝试。 rs = decryptWithAD(temp_k2, 1, h, c)- 此时,响应方已恢复发起方的静态公钥。
- 如果此操作中的 MAC 检查失败,则响应方_必须_终止连接,而不发送任何进一步的消息。
h = SHA-256(h || c)se = ECDH(e.priv, rs)- 其中
e是响应方原始的临时密钥
- 其中
ck, temp_k3 = HKDF(ck, se)p = decryptWithAD(temp_k3, 0, h, t)- 如果此操作中的 MAC 检查失败,则响应方_必须_终止连接,而不发送任何进一步的消息。
rk, sk = HKDF(ck, zero)- 其中
zero是零长度明文,rk是响应方用于解密发起方发送的消息的密钥,sk是响应方用于加密发送给发起方的消息的密钥 - 生成最终的加密密钥,该密钥将在会话期间用于发送和接收消息。
- 其中
rn = 0, sn = 0- 发送和接收 nonce 初始化为 0。
闪电消息规范
在 Act Three 结束时,双方都已导出加密密钥,该密钥将用于加密和解密会话剩余时间内的消息。
实际的 Lightning 协议消息封装在 AEAD 密文中。每个消息都以另一个 AEAD 密文为前缀,该密文编码了以下 Lightning 消息的总长度(不包括其 MAC)。
任何 Lightning 消息的_最大_大小都_不能_超过 65535 字节。65535 的最大大小简化了测试,使内存管理更加容易,并有助于缓解内存耗尽攻击。
为了使流量分析更加困难,所有加密的 Lightning 消息的长度前缀也被加密。此外,将 16 字节的 Poly-1305 标签添加到加密的长度前缀,以确保数据包长度在传输过程中未被修改,并避免创建解密 oracle。
线路上的数据包结构类似于以下内容:
+-------------------------------
|2 字节加密消息长度|
+-------------------------------
| 加密的 16 字节 MAC |
| 消息长度 |
+-------------------------------
| |
| |
| 加密的 Lightning |
| 消息 |
| |
+-------------------------------
| Lightning 消息的 |
| 16 字节 MAC |
+-------------------------------
前缀消息长度编码为 2 字节大端整数,总最大数据包长度为 2 + 16 + 65535 + 16 = 65569 字节。
加密和发送消息
为了加密并将 Lightning 消息 (m) 发送到网络流,给定一个发送密钥 (sk) 和一个 nonce (sn),完成以下步骤:
- 令
l = len(m)。- 其中
len获取 Lightning 消息的字节长度
- 其中
- 将
l序列化为 2 个字节,编码为大端整数。 - 加密
l(使用ChaChaPoly-1305、sn和sk),以获得lc(18 个字节)- nonce
sn编码为 96 位小端数。由于解码后的 nonce 为 64 位,因此 96 位 nonce 编码为:32 位前导 0,后跟一个 64 位值。- 在此步骤之后,_必须_递增 nonce
sn。
- 在此步骤之后,_必须_递增 nonce
- 零长度字节片应作为 AD(关联数据)传递。
- nonce
- 最后,使用与加密长度前缀相同的过程加密消息本身 (
m)。令加密的密文称为c。- 在此步骤之后,_必须_递增 nonce
sn。
- 在此步骤之后,_必须_递增 nonce
- 通过网络缓冲区发送
lc || c。
接收和解密消息
为了解密网络流中的_下一个_消息,完成以下步骤:
- 从网络缓冲区读取_正好_ 18 个字节。
- 令加密的长度前缀称为
lc。 - 解密
lc(使用ChaCha20-Poly1305、rn和rk),以获得加密数据包l的大小。- 零长度字节片应作为 AD(关联数据)传递。
- 在此步骤之后,_必须_递增 nonce
rn。
- 从网络缓冲区读取_正好_
l+16个字节,并将这些字节称为c。 - 解密
c(使用ChaCha20-Poly1305、rn和rk),以获得解密的明文数据包p。- 在此步骤之后,_必须_递增 nonce
rn。
- 在此步骤之后,_必须_递增 nonce
闪电消息密钥轮换
定期更改密钥和忘记以前的密钥对于防止在以后发生密钥泄漏(即后向保密)的情况下解密旧消息很有用。
密钥轮换是为_每个_密钥(sk 和 rk)_单独_执行的。在一方使用密钥加密或解密 1000 次后(即每 500 条消息),将轮换密钥。可以通过在专用于密钥的 nonce 超过 1000 后轮换密钥来正确说明这一点。
密钥 k 的密钥轮换按照以下步骤执行:
- 令
ck为在 Act Three 结束时获得的链接密钥。 ck', k' = HKDF(ck, k)- 将密钥的 nonce 重置为
n = 0。 k = k'ck = ck'
安全考虑
强烈建议使用现有的、常用的、经过验证的库进行加密和解密,以避免许多可能的实现陷阱。
附录 A:传输测试向量
为了进行可重复的测试握手,以下指定了 generateKey() 对于每一方将返回的值(即 e.priv 的值)。请注意,这违反了规范,该规范要求随机性。
发起方测试
当输入此输入时,发起方应生成给定的输出。注释反映了内部状态,用于调试目的。
name: transport-initiator successful handshake
rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111
ls.pub: 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
e.priv: 0x1212121212121212121212121212121212121212121212121212121212121212
e.pub: 0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7
# Act One
# h=0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c
# ss=0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3
# HKDF(0x2640f52eebcd9e882958951c794250eedb28002c05d7dc2ea0f195406042caf1,0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3)
# ck,temp_k1=0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f
# encryptWithAD(0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f, 0x000000000000000000000000, 0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c, <empty>)
# c=0df6086551151f58b8afe6c195782c6a
# h=0x9d1ffbb639e7e20021d9259491dc7b160aab270fb1339ef135053f6f2cebe9ce
output: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
input: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae
# re=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27
# h=0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf
# ss=0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47
# HKDF(0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47)
# ck,temp_k2=0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc
# decryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000000000000000000, 0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf, 0x6e2470b93aac583c9ef6eafca3f730ae)
# h=0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72
# Act Three
# encryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000100000000000000, 0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72, 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa)
# c=0xb9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c3822
# h=0x5dcb5ea9b4ccc755e0e3456af3990641276e1d5dc9afd82f974d90a47c918660
# ss=0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766
# HKDF(0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766)
# ck,temp_k3=0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01,0x981a46c820fb7a241bc8184ba4bb1f01bcdfafb00dde80098cb8c38db9141520
# encryptWithAD(0x981a46c820fb7a241bc8184ba4bb1f01bcdfafb00dde80098cb8c38db9141520, 0x000000000000000000000000, 0x5dcb5ea9b4ccc755e0e3456af3990641276e1d5dc9afd82f974d90a47c918660, <empty>)
# t=0x8dc68b1c466263b47fdf31e560e139ba
output: 0x00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba
# HKDF(0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01,zero)
output: sk,rk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9,0xbb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442
name: transport-initiator act2 short read test
rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111
ls.pub: 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
e.priv: 0x1212121212121212121212121212121212121212121212121212121212121212
e.pub: 0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7
output: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
input: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730
output: ERROR (ACT2_READ_FAILED)
name: transport-initiator act2 bad version test
rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111
ls.pub: 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
e.priv: 0x1212121212121212121212121212121212121212121212121212121212121212
e.pub: 0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7
output: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
input: 0x0102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae
output: ERROR (ACT2_BAD_VERSION 1)
name: transport-initiator act2 bad key serialization test
rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111
ls.pub: 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
e.priv: 0x1212121212121212121212121212121212121212121212121212121212121212
e.pub: 0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7
output: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
input: 0x0004466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae
output: ERROR (ACT2_BAD_PUBKEY)
name: transport-initiator act2 bad MAC test
rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111
ls.pub: 0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
e.priv: 0x1212121212121212121212121212121212121212121212121212121212121212
e.pub: 0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7
output: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
input: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730af
output: ERROR (ACT2_BAD_TAG)
Responder Tests
响应者在接收到此输入时,应该 生成给定的输出。
name: transport-responder successful handshake
ls.priv=2121212121212121212121212121212121212121212121212121212121212121
ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
e.priv=0x2222222222222222222222222222222222222222222222222222222222222222
e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27
# Act One
input: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# re=0x036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7
# h=0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c
# ss=0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3
# HKDF(0x2640f52eebcd9e882958951c794250eedb28002c05d7dc2ea0f195406042caf1,0x1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3)
# ck,temp_k1=0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f
# decryptWithAD(0xe68f69b7f096d7917245f5e5cf8ae1595febe4d4644333c99f9c4a1282031c9f, 0x000000000000000000000000, 0x9e0e7de8bb75554f21db034633de04be41a2b8a18da7a319a03c803bf02b396c, 0x0df6086551151f58b8afe6c195782c6a)
# h=0x9d1ffbb639e7e20021d9259491dc7b160aab270fb1339ef135053f6f2cebe9ce
# Act Two
# e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27 e.priv=0x2222222222222222222222222222222222222222222222222222222222222222
# h=0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf
# ss=0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47
# HKDF(0xb61ec1191326fa240decc9564369dbb3ae2b34341d1e11ad64ed89f89180582f,0xc06363d6cc549bcb7913dbb9ac1c33fc1158680c89e972000ecd06b36c472e47)
# ck,temp_k2=0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc
# encryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000000000000000000, 0x38122f669819f906000621a14071802f93f2ef97df100097bcac3ae76c6dc0bf, <empty>)
# c=0x6e2470b93aac583c9ef6eafca3f730ae
# h=0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72
output: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae
# Act Three
input: 0x00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba
# decryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000100000000000000, 0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72, 0xb9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c3822)
# rs=0x034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
# h=0x5dcb5ea9b4ccc755e0e3456af3990641276e1d5dc9afd82f974d90a47c918660
# ss=0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766
# HKDF(0xe89d31033a1b6bf68c07d22e08ea4d7884646c4b60a9528598ccb4ee2c8f56ba,0xb36b6d195982c5be874d6d542dc268234379e1ae4ff1709402135b7de5cf0766)
# ck,temp_k3=0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01,0x981a46c820fb7a241bc8184ba4bb1f01bcdfafb00dde80098cb8c38db9141520
# decryptWithAD(0x9```markdown
name: transport-responder act3 bad MAC for ciphertext test
ls.priv=2121212121212121212121212121212121212121212121212121212121212121
ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
e.priv=0x2222222222222222222222222222222222222222222222222222222222222222
e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27
# Act One
input: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
output: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae
# Act Three
input: 0x00c9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba
output: ERROR (ACT3_BAD_CIPHERTEXT)
name: transport-responder act3 bad rs test
ls.priv=2121212121212121212121212121212121212121212121212121212121212121
ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
e.priv=0x2222222222222222222222222222222222222222222222222222222222222222
e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27
# Act One
input: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
output: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae
# Act Three
input: 0x00bfe3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa2235536ad09a8ee351870c2bb7f78b754a26c6cef79a98d25139c856d7efd252c2ae73c
# decryptWithAD(0x908b166535c01a935cf1e130a5fe895ab4e6f3ef8855d87e9b7581c4ab663ddc, 0x000000000000000000000001, 0x90578e247e98674e661013da3c5c1ca6a8c8f48c90b485c0dfa1494e23d56d72, 0xd7fedc211450dd9602b41081c9bd05328b8bf8c0238880f7b7cb8a34bb6d8354081e8d4b81887fae47a74fe8aab3008653)
# rs=0x044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
output: ERROR (ACT3_BAD_PUBKEY)
name: transport-responder act3 bad MAC test
ls.priv=2121212121212121212121212121212121212121212121212121212121212121
ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
e.priv=0x2222222222222222222222222222222222222222222222222222222222222222
e.pub=0x02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27
# Act One
input: 0x00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a
# Act Two
output: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae
# Act Three
input: 0x00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139bb
output: ERROR (ACT3_BAD_TAG)
消息加密测试
在这个测试中,发起者发送长度为 5 的消息,其中包含 "hello" 1001 次。为了简洁和测试两次密钥轮换,仅显示六个示例输出:
name: transport-message test
ck=0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01
sk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9
rk=0xbb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442
# encrypt l: cleartext=0x0005, AD=NULL, sn=0x000000000000000000000000, sk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9 => 0xcf2b30ddf0cf3f80e7c35a6e6730b59fe802
# encrypt m: cleartext=0x68656c6c6f, AD=NULL, sn=0x000000000100000000000000, sk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9 => 0x473180f396d88a8fb0db8cbcf25d2f214cf9ea1d95
output 0: 0xcf2b30ddf0cf3f80e7c35a6e6730b59fe802473180f396d88a8fb0db8cbcf25d2f214cf9ea1d95
# encrypt l: cleartext=0x0005, AD=NULL, sn=0x000000000200000000000000, sk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9 => 0x72887022101f0b6753e0c7de21657d35a4cb
# encrypt m: cleartext=0x68656c6c6f, AD=NULL, sn=0x000000000300000000000000, sk=0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9 => 0x2a1f5cde2650528bbc8f837d0f0d7ad833b1a256a1
output 1: 0x72887022101f0b6753e0c7de21657d35a4cb2a1f5cde2650528bbc8f837d0f0d7ad833b1a256a1
# 0xcc2c6e467efc8067720c2d09c139d1f77731893aad1defa14f9bf3c48d3f1d31, 0x3fbdc101abd1132ca3a0ae34a669d8d9ba69a587e0bb4ddd59524541cf4813d8 = HKDF(0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01, 0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9)
# 0xcc2c6e467efc8067720c2d09c139d1f77731893aad1defa14f9bf3c48d3f1d31, 0x3fbdc101abd1132ca3a0ae34a669d8d9ba69a587e0bb4ddd59524541cf4813d8 = HKDF(0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01, 0x969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9)
output 500: 0x178cb9d7387190fa34db9c2d50027d21793c9bc2d40b1e14dcf30ebeeeb220f48364f7a4c68bf8
output 501: 0x1b186c57d44eb6de4c057c49940d79bb838a145cb528d6e8fd26dbe50a60ca2c104b56b60e45bd
# 0x728366ed68565dc17cf6dd97330a859a6a56e87e2beef3bd828a4c4a54d8df06, 0x9e0477f9850dca41e42db0e4d154e3a098e5a000d995e421849fcd5df27882bd = HKDF(0xcc2c6e467efc8067720c2d09c139d1f77731893aad1defa14f9bf3c48d3f1d31, 0x3fbdc101abd1132ca3a0ae34a669d8d9ba69a587e0bb4ddd59524541cf4813d8)
# 0x728366ed68565dc17cf6dd97330a859a6a56e87e2beef3bd828a4c4a54d8df06, 0x9e0477f9850dca41e42db0e4d154e3a098e5a000d995e421849fcd5df27882bd = HKDF(0xcc2c6e467efc8067720c2d09c139d1f77731893aad1defa14f9bf3c48d3f1d31, 0x3fbdc101abd1132ca3a0ae34a669d8d9ba69a587e0bb4ddd59524541cf4813d8)
output 1000: 0x4a2f3cc3b5e78ddb83dcb426d9863d9d9a723b0337c89dd0b005d89f8d3c05c52b76b29b740f09
output 1001: 0x2ecd8c8a5629d0d02ab457a0fdd0f7b90a192cd46be5ecb6ca570bfc5e268338b1a16cf4ef2d36
致谢
TODO(roasbeef); fin
参考文献
- <a id="reference-1">https://tools.ietf.org/html/rfc8439</a>
- <a id="reference-2">http://noiseprotocol.org/noise.html</a>
- <a id="reference-3">https://tools.ietf.org/html/rfc5869</a>
作者
FIXME
<br>
本作品采用 知识共享署名 4.0 国际许可协议 授权。
- 原文链接: github.com/lightning/bol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~