今夜冥王星有雪

导航

从 croc 库中学到一个新名词 Pake

这些天看了一下 croc 库的实现。这是一个用来传输文件的工具,用处和 sftp, sz/rz 这些东西类似。但是不同之处在于这个工具文件的收发需要依赖于外部的一个 relay server。

  1. sender 先与 relay server 建立连接,生成一个随机的 secret,注册房间信息 room。
  2. sender 通过某种方式将 secret 发送给 receiver。(邮件,微信,😓)
  3. receiver 带上这个 secret 连接 relay server。找到对应的房间信息。
  4. relay Server 将这两个 TCP 连接通过管道 pipe 连接,让他们相互通信。
  5. sender 将文件发送给 receiver。

当然这种信息传递方式没有加密的话是非常危险的,尤其是在还有一个 relay server 存在的情况下。

  1. 在 sender 与 relay server 建立连接注册 room 之前。
  2. 在 receiver 与 relay server 建立连接获取 romm 之前。
  3. sender 和 receiver 连接 pipe 之后,传递文件数据之前。

在上述 3 个时间点连接的双方会使用一种叫做 Pake (Password-Based Authenticated Key Establishment Protocol) 的协议协商加解密的密钥。中文叫做 口令认证密钥交换协议

Pake

具体的原理可以参看 一个酷炫却不普及的协议——PAKE,我对于这种数学算法比较头疼,就不解释了。

下面写个代码例子看一下。

// 这既是公钥。必须双方一开始约定好。
weakPassword := "foo-bar"

// P 为 sender,Q 为 receiver。
// P 和 Q 都使用同样的 weakPassword 初始化。
P, err := pake.InitCurve([]byte(weakPassword), 0, "siec")
if err != nil {
    panic(err)
}

Q, err := pake.InitCurve([]byte(weakPassword), 1, "siec")
if err != nil {
    panic(err)
}

// P -> Q
// P 将自身信息发送给 Q,所以 Q 更新。
if err := Q.Update(P.Bytes()); err != nil {
    panic(err)
}
// Q -> P
// Q 再将自身信息发送给 P,P 更新。
if err := P.Update(Q.Bytes()); err != nil {
    panic(err)
}

// 得到双方的 sessionKey,即协商密钥。
KeyP, err := P.SessionKey()
if err != nil {
    panic(err)
}
KeyQ, err := Q.SessionKey()
if err != nil {
    panic(err)
}

// 可以发现是相同的。
fmt.Printf("Key of P: %s\n", hex.EncodeToString(KeyP))
fmt.Printf("Key of Q: %s\n", hex.EncodeToString(KeyQ))

得到打印:

15:26 test ➜  go run main.go
Key of P: cebe3dbfc2afb1579e4649726e8ce352de3dc59ce5ca7ab31bebaf5b0aa36a9c
Key of Q: cebe3dbfc2afb1579e4649726e8ce352de3dc59ce5ca7ab31bebaf5b0aa36a9c

你可能得到的打印与我不一样,很正常。每次打印都是有随机数影响的,都是不一样的。

这样双方就能用这个密钥进行对称加密通信传输数据了。中间人只能截获到中间数据,无法推算出最后的密钥。即使知道了一开始的公钥,也没用。

如果双方一开始使用的 weakPassword 就不相同的话。在 P.Update(Q.Bytes()) 处就会报错了。

posted on 2021-03-25 15:44  alfred_zhong  阅读(443)  评论(0编辑  收藏  举报