Zcash – 各种密钥和签名,你懂吗?

Zcash 的发展大体经过了 OverWinter (过冬) -> Sprout (发芽) -> Sapling (树苗) 这几个阶段,随着业务和功能的逐渐丰富,密钥系统也越来越复杂,刚开始接触时感觉一头雾水,但是静下心来仔细分析,就能逐渐领略其中的魅力。

Zcash 中的各种密钥,主要是为了实现下面 2 类功能:

  1. Signature
  2. In-band secret distribution

签名很好理解,主要是为了保证数据的不可篡改性。那么什么是 in-band secret distribution 呢?我们知道,Zcash 使用了零知识证明,因此交易内部的私密信息对外是隐藏的。但是,如果我想要消费某一笔交易的输出(Note),就必须提供这些私密信息(用于生成 Nullifier)。问题来了,交易发送方如何把这些私密信息传递给交易接收方呢?有 2 种方案:

  1. Out-band secret distribution:其实就是 “线下” 传递,通过一些加密的 P2P 即时通信工具,比如以太坊的 whisper。这种方案比较简单,但是也存在一些缺点,比如要求交易双方必须同时在线,否则就必须引入一些消息持久化方案比如 mail server 等等。
  2. In-band secret distribution:即 “线上” 传递,也是 Zcash 目前采用的方案。线上传递的意思是把这些私密信息加密后直接放到交易中上链,接收方再通过某种方式解密以获得私密信息。
    下面就逐一介绍一下 Sprout 和 Sapling 中的密钥系统,以及在签名及 In-band secret distribution 中的应用。

1. Sprout

1.1 Sprout 密钥系统

Sprout 的密钥系统比较简单,参见下图:

Sprout密钥系统

其中 Payment Address 是发送方需要用到的,Incoming viewing key 是给接收方用的。
下面会逐一介绍图上的这些密钥的含义,如果你觉得枯燥难懂,可以先跳过,等看完 “签名” 和 “带内秘密分发” 之后再回过头来看这段描述,相信会有不一样的体会。

1) askask:Spending Key,用于生成 Nullifier

2) apkapk:Paying Key,用于生成 Note

3) skencskenc:Receiving Key,和 epk 一起生成 sharedSecret,进而生成对称密钥,解密所有的 Note Plaintext

$$sharedSecret=Curve25519(sk_{enc},epk)=sk_{enc} \cdot epk=sk_{enc} \cdot esk \cdot G$$

4) pkencpkenc:Transmission Key,和 esk 一起生成 sharedSecret,进而生成对称密钥,加密所有的 Note Plaintext

sharedSecret=Curve25519(esk,pkenc)=eskpkenc=eskskencGsharedSecret=Curve25519(esk,pkenc)=esk⋅pkenc=esk⋅skenc⋅G
可以发现,和第3项生成的sharedSecret是完全相同的

5) esk:Ephemeral Secret Key,一个临时私钥,为了生成对称密钥
6) epk:Ephemeral Public Key,一个临时公钥,为了生成对称密钥,包含在 JoinSplit 中
7) KencKenc:用于加密 Note Plaintext 的对称密钥,通过 KDF 生成(5 个参数的 BLAKE2b-256 哈希)
8) JoinSplitPrivateKey:用于对 transaction 签名
9) JoinSplitPubKey:用于验证 transaction 签名,包含在 transaction 中

1.2 Sprout 中的 JoinSplit Signature

Sprout 中只有一种签名,叫做 JoinSplit Signature,不过不要被它的名字迷惑了,实际上它是整个 transaction 的签名:

Zcash - 各种密钥和签名,你懂吗?

首先看一下签名使用的密钥对:

joinSplitPrivKey 是一个随机生成的私钥,joinSplitPubKey 则是把 Curve25519 上对应的公钥,公钥是包含在 transaction 中的。之所以使用随机生成的密钥对,目的是为了隐藏用户的身份,因为每次付款都可以生成不同的公钥。

接下来看一下签名使用的输入数据:

输入数据是整个 transaction 的 SIGHASH_ALL 类型的哈希。接触过比特币的朋友可能会比较熟悉,比特币解锁脚本中的签名就使用了这种哈希函数。具体来说,就是去除每个 input 中解锁脚本字段,然后再计算整个交易的哈希。具体细节参考链接

Zcash 既支持类似比特币的透明交易(transparent transaction),又支持隐私交易(shielded transaction),因此它扩展了比特币原有的交易结构,增加了一些特有字段。在计算哈希时,需要把这些字段也包含进去。

有了密钥对和输入数据,就可以创建签名了,Sprout 中使用的是 Ed25519 签名算法(增加了 2 个限制条件)。

这里还有一个非常精妙的设计,不得不提一下:
不管是 Sprout 使用的 BCTV14 算法,还是 Sapling 使用的 Groth16 算法,都存在 “Malleability” 问题。Malleability 可以翻译成 “变形” 或者 “重塑”,指的是可以用已有的 proof 生成新的合法 proof。
因此,我们必须增加一个签名,用来保证数据不被篡改。但是,签名使用的密钥对是随机生成的,那么当别人收到你的交易后,是不是可以把签名和公钥字段去掉,然后自己生成新的密钥对重新签名?为了解决这个问题,我们把所有 Nullifier、joinSplitPubKey 还有一个 randomSeed 组合在一起,计算出一个哈希值 hSig,然后再把每个 Nullifier 的私钥 ask 跟 hSig 组合在一起,生成 N 个哈希值并放进 JoinSplit 中。经过这一波操作,输入数据就跟所有的私钥还有签名者的公钥绑定在了一起,第三方用户就无法替换签名了。用 Zcash 白皮书中的话来说,这样就保证了 “所有 ask 的持有者都授权了签名交易的人”。

1.3 Sprout in-band secret distribution

Zcash - 各种密钥和签名,你懂吗?

图中红色虚线框内是需要传输的秘密,包含 3 个数据。Note Plaintext 中包含这个秘密,同时还包含一个 memo 字符串。我们目标是安全地把 Note Plaintext 发送给交易接收方。

首先看蓝线部分:
1) 交易发送方拿到接收方的 Payment Address,其中包含 2 个密钥:apkpkencapk、pkenc
2) 然后,随机生成一个临时密钥对 esk/epk
3) 利用pkencpkenc和 esk 生成一个 sharedSecret,然后通过 KDF 生成对称密钥KencKenc
4) 利用对称密钥对 Note Plaintext 加密生成 CencCenc 数据,和 epk 一起放入交易的 JoinSplit 中
5) 接收方从交易的 JoinSplit 中拿到 epk,和skencskenc一起生成 sharedSecret,然后通过 KDF 生成对称密钥KencKenc(为什么可以生成相同的 sharedSecret?参见 1.1 节的证明)
6) 接收方用 KencKenc 解密 CencCenc,获得秘密

接下来看红线部分:
为了保证数据的正确性,发送方会把秘密和 apkapk 一起生成一个哈希值,称为 Note Commitment,放入交易的 JoinSplit 中,供接收方验证。

最后看紫线部分:
接收方解出秘密,和 apkapk 一起生成哈希值,比对 Note Commitment 看是否一致。

2.Sapling

2.1 Sapling 密钥系统

Sapling 的密钥系统采用了分层结构,比 Sprout 要复杂很多:
Zcash - 各种密钥和签名,你懂吗?

可以发现,更多地使用了随机化,从而减小了公钥暴露账户信息的风险。

首先,不是直接使用 Spending Key (sk),而是派生出了 3 个 Expended Spending Key。

Payment Address 也不再是固定值,而是通过一个随机数 d 和 Incoming Viewing Key 映射到椭圆曲线上,可以随意生成,从而实现了地址的 “多样化(Diversified)”。

另外,除了 Incoming Viewing Key,还增加一个 Full Viewing Key,不仅可以查看接收到的 “Incoming” 交易的信息,还可以查看对方发送的 “Outgoing” 交易的信息。

Proof Authorizing Key 会作为零知识证明的 auxiliary 输入,参与证明的生成。

下面会逐一介绍这些密钥的作用,同样,你也可以先跳过这些内容,等看完了后面的使用场景再来回顾:
1) sk:Spending Key
2) ask:Spend Authorizing Key,通过 randomize 生成 rsk
3) rsk:Randomized ask,由 ask 生成,用于给 SpendDescription 签名
4) rk:由 rsk 生成,用于验证 SpendDescription 签名
5) nsk:Spend Nullifier Key,作为 Proof Authorizing Key 的一部分,属于 prover 的一个 auxiliary 输入
6) nk:Nullifier Deriving Key,由 nsk 生成,用于生成 Nullifier
7) ak:由 ask 生成,用于和 nk 一起生成 ivk
8) ivk:Incomming Viewing Key,等于 CRHivk(reprJ(ak),nk)CRHivk(reprJ(ak),nk),可以生成多样化的密钥用于加密 Note Plaintext
9) d:一个用于生成 Diversified Base 的随机数
10) pkdpkd:Diversified Transmission Key,用于实现 In-band secret distribution。接收方可以利用 ivk 恢复出 Note Plaintext。和 esk 一起生成 sharedSecret,然后生成对称密钥,进而加密所有的 Note Plaintext

sharedSecret=Jubjub(hJesk,pkd)=hJeskpkd=hJeskivkgdsharedSecret=Jubjub(hJ⋅esk,pkd)=hJ⋅esk⋅pkd=hJ⋅esk⋅ivk⋅gd

11) esk/epk:临时密钥对,用于生成对称密钥
12) KencKenc:用于加密 Note Plaintext 的对称密钥,通过 KDF 生成(2 个参数的 BLAKE2b-256 哈希)
13) ovk:Outgoing Viewing Key,用于生成 ock
14) ock:Outgoing Cipher Key,由 ovk、epk 等参数生成,用于加密 pkdpkd 和 esk,生成 CoutCout

2.2 RedDSA 和 RedJubjub

Zcash 中定义了一种新的签名算法:RedDSA。RedDSA 类似于 Ed25519,但算法略有不同,具体流程参见下图:
Zcash - 各种密钥和签名,你懂吗?

和 Ed25519 一样,最终的输出是由 R 和 S 这 2 部分拼接起来的。
R 是通过把 Hash (T,vk,M) 的输出映射到椭圆曲线上得到的一个点,最后通过 representation function 转化为一个标量值。
S 则通过计算 r+sk・Hash (R,vk,M) 得到的的一个标量,结果需要对椭圆曲线的阶次取余。

RedJubjub 是 RedDSA 的一个特化,哈希函数选用 BLAKE2b-512,椭圆曲线选用 Jubjub。

2.3 Spend Authorization Signature

Sapling 中有 2 种签名,这里先介绍第一种:Spend Authorization Signature。
这个签名的作用是证明你知道 askask,因此你有权花费这边资金。
Zcash - 各种密钥和签名,你懂吗?

签名是在整个 transaction 的 SIGHASH_ALL 结果之上做的,签名结果放在 SpendDescription 中。

为了不暴露 askask 对应的公钥,采用了 re-randomized key 的方式(左侧虚线框),通过一个随机数 α 和一个 randomizer 生成一个新的私钥 rsk,然后再进行 RedJubjub 签名。为了保证签名和公钥的不可篡改性,α 和 rk 将作为 2 个 auxiluary 输入,参与 proof 的生成。

2.4 Binding Signature

这个签名是为了证明 2 件事情:

  1. shield inputs – shield outputs = transparent value change,也就是隐私交易和透明交易之间的金额必须平衡
  2. 保证签名者知道生成所有 Value Commitment(cv)的随机数(rcv)

Zcash - 各种密钥和签名,你懂吗?

签名同样也是在整个 transaction 的 SIGHASH_ALL 结果之上做的,签名结果直接放在 transaction 中。
这个签名有意思的地方在于,签名所使用的密钥对必须通过交易中的数据生成出来,而不是直接记录在交易中的。
我们先来看看私钥 bsk,bsk 是通过所有 SpendDescription 中的 cv 对应的 rcv 之和,减去所有 OutputDescription 中的 cv 对应的 rcv 之和生成的:

bsk=Σni=1rcvoldiΣmj=1rcvnewjbsk=Σi=1nrcviold−Σj=1mrcvjnew

而公钥 bvk 则是通过 SpendDescription 中所有 cv 之和,减去 OutputDescription 中所有 cv 之和以及透明交易余额 valueBalance 的 ValueCommit 生成的:

bvk=Σni=1cvoldiΣmj=1cvnewjValueCommit0(valueBalance)bvk=Σi=1ncviold−Σj=1mcvjnew−ValueCommit0(valueBalance)

需要注意的是,生成 valueBalance 的 ValueCommit 时,下标为 0,表示使用的随机数 rcv 为 0(因为 bsk 中其实省略了为 0 的最后一项)。

2.5 In-band Secret Distribution w/ Incoming Viewing Key

Sapling 中对密钥进行了更精细的划分,使用 Incoming Viewing Key 和 Full Viewing Key 都可以实现 In-band Secret Distribution。这里先介绍 Incoming View Key 的情况:
Zcash - 各种密钥和签名,你懂吗?

和 Sprout 相比,首先 Payment Address 发生了变化,采用了基于椭圆曲线的 “多样化” 地址。
蓝线部分和 Sprout 基本类似:结合临时密钥生成 sharedSecret,然后通过 KDF 生成对称密钥,完成数据传输。
红线部分和 Sprout 也是相同的,唯一的区别是把 SHA256 换成了 Windowed Pedersen Hash。
紫线部分验证 Note Commitment 时只需要使用到 Payment Address 中的公钥,而 Payment Address 是可以随机生成的,从而最大限度隐藏了用户信息。

2.6 In-band Secret Distribution w/ Full Viewing Key

Sapling 中定义了 Full Viewing Key 的概念,该密钥既可以查看接收方 “Incoming” 交易的信息,也可以查看发送方”Outgoing“交易的信息,这就是名字中 “Full” 的由来。
Zcash - 各种密钥和签名,你懂吗?

Full Viewing Key 包含 3 个密钥:ovk,ak,nk。
眼尖的朋友可能发现了,有了 ak 和 nk,不就可以生成 ivk 了吗?所以等价于 Full View Key 包含了 ivk 和 ovk 这 2 个密钥。这里重点分析一下 ovk 的作用。
观察上图可以发现,交易中除了包含 CencCenc 字段,还增加了一个 CoutCout 字段(绿色部分)。发送方使用 ovk 和临时密钥 epk 生成对称密钥 ock,然后把用于加密 Note Plaintext 的 2 个密钥 pkdpkd 和 esk 打包到交易中。Full Viewing Key 持有者可以以同样的方式生成对称密钥 ock,进而解密 CoutCout,获得这 2 个密钥。此时他已经获得了发送方生成 Outgoing 交易的所有信息,可以直接生成对称密钥解开 CencCenc,而不需要借助 Incoming Viewing Key 了。

3. Zcash 是如何实现隐私的

最后,写一点对 Zcash 实现隐私机制的理解。
所谓隐私,就是要隐藏一笔交易的发送方 (from)、接收方 (to)、金额 (amount)。
1)如何隐藏发送方?
发送方可以随机生成密钥对,然后对交易签名,这样就不会对外暴露固定的公钥。不管是 Sprout 中的 joinSplitSig 还是 Sapling 中的 spendAuthSig 和 bindingSig,都遵循这一原则。

2)如何隐藏接收方?
接收方的 Payment Address 只有发送方知道,JoinSplit 或者 OutputDescription 中只会提交 Note Commitment,因此矿工无法知晓地址该信息,并且也无法在区块链浏览器中查询到。当然,在 Sprout 中,由于 Payment Address 是固定的,如果多个发送方串谋,有可能会暴露接收方的信息。因此,在 Sapling 中引入了 “多样化” 地址,可以给每个发送方生成不同的地址,从而解决了这个问题。

3)如何隐藏金额?
矿工只需要通过零知识证明验证交易的 input 和 output 是平衡的,而不需要查询发送方的余额以及转账金额。

4. 总结

Zcash 通过引入精细的密钥机制,在实现了交易签名和私密信息分发的前提下,最大限度隐藏了用户信息。
在 Sprout 中,通过 joinSplitSig 完美解决了 malleability 问题,同时采用非对称双密钥的形式实现了 In-band secret distribution。
在 Sapling 中对密钥进一步分层,采用多样化的 Payment Address,同时将 Spend 和 Output 信息分离并分别签名,在计算能力比较弱的设备上也可以进行简单验证。通过引入 Outgoing Viewing Key 和 Full Viewing Key,更加精细地控制了交易的访问权限。

本文作者飞久,授权转发自公众号星想法

该内容来自于互联网公开内容,非区块链原创内容,如若转载,请注明出处:https://htzkw.com/archives/5829

联系我们

aliyinhang@gmail.com