1. 引言
接前序博客 Zcash中的Notes。
Sapling note的组成为tuple n = ( d , p k d , v , r c m ) \mathbf{n}=(d, pk_d, v, rcm) n=(d,pkd,v,rcm),其中:
- d d d:为接收方的shielded payment address对应的diversifier值。【为88bit, B [ l d ] \mathbb{B}^{[l_d]} B[ld]】
- p k d pk_d pkd:为接收方的shielded payment address对应的diversified transmission key。【为Jubjub point, K A S a p l i n g . P u b l i c P r i m e S u b g r o u p KA^{Sapling}.PublicPrimeSubgroup KASapling.PublicPrimeSubgroup】
- v v v:取值范围为 MAX_MONEY \text{MAX\_MONEY} MAX_MONEY,代表note中的value值,单位为zatoshi。
- r c m rcm rcm:为random commitment trapdoor。【 N o t e C o m m i t S a p l i n g . T r a p d o o r NoteCommit^{Sapling}.Trapdoor NoteCommitSapling.Trapdoor】
m e m o memo memo为与该note关联的512字节的memo field。
1.1 相关约定
- l o v k l_{ovk} lovk:为256。
- S y m Sym Sym:为Symmetric Encryption scheme。
- K A KA KA:为Sapling key agreement scheme K A S a p l i n g KA^{Sapling} KASapling。
-
K
D
F
KDF
KDF:为Sapling key derivation function
K
D
F
S
a
p
l
i
n
g
KDF^{Sapling}
KDFSapling。
void KDF_Sapling(
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
const uint256 &dhsecret,
const uint256 &epk
)
{
unsigned char block[64] = {};
memcpy(block+0, dhsecret.begin(), 32);
memcpy(block+32, epk.begin(), 32);
unsigned char personalization[BLAKE2bPersonalBytes] = {};
memcpy(personalization, "Zcash_SaplingKDF", 16);
auto state = blake2b_init(NOTEENCRYPTION_CIPHER_KEYSIZE, personalization);
blake2b_update(state, block, 64);
blake2b_finalize(state, K, NOTEENCRYPTION_CIPHER_KEYSIZE);
blake2b_free(state);
}
-
G \mathbb{G} G:为Jubjub中的subgroup J \mathbb{J} J。 J \mathbb{J} J group具有的order为 h J ⋅ r J h_{\mathbb{J}}\cdot r_{\mathbb{J}} hJ⋅rJ,其中 r J r_{\mathbb{J}} rJ为prime。
-
l G l_{\mathbb{G}} lG:为Jubjub中的 l J = 256 l_{\mathbb{J}}=256 lJ=256。
-
r e p r J repr_{\mathbb{J}} reprJ:为Jubjub中的 r e p r J repr_{\mathbb{J}} reprJ。即将group point以256-bit表示。
-
a b s t J abst_{\mathbb{J}} abstJ:为将256-bit转换为Jubjub上的group point。
-
E x t r a c t G ( r ) Extract_{\mathbb{G}^{(r)}} ExtractG(r):为Jubjub中的 E x t r a c t J ( r ) Extract_{\mathbb{J}^{(r)}} ExtractJ(r)——Coordinate Extractor for Jubjub。实际即为提取Jubjub point的 u u u坐标值, E x t r a c t J ( r ) ( P ) = l 2 L E B S P l M e r k l e S a p l i n g ( U ( P ) ) Extract_{\mathbb{J}^{(r)}}(P)=l2LEBSP_{l_{Merkle}^{Sapling}}(\mathcal{U}(P)) ExtractJ(r)(P)=l2LEBSPlMerkleSapling(U(P)),其中 U ( P ) = U ( ( u , v ) ) = u \mathcal{U}(P)=\mathcal{U}((u,v))=u U(P)=U((u,v))=u,同时约定,若 P = ( u , v ) ∈ J ( r ) P=(u,v)\in\mathbb{J}^{(r)} P=(u,v)∈J(r),则 ( u , − v ) ∉ J ( r ) (u,-v)\notin \mathbb{J}^{(r)} (u,−v)∈/J(r)。 J ( r ) \mathbb{J}^{(r)} J(r)为prime order group。
-
P R F o c k PRF^{ock} PRFock:为Pseudo Random Function P R F o c k S a p l i n g PRF^{ockSapling} PRFockSapling。 P R F o c k S a p l i n g PRF^{ockSapling} PRFockSapling用于派生the outgoing cipher key o c k ock ock used to encrypt an outgoing ciphertext。其采用BLAKE2b hash函数来实例化:
P R F o v k o c k S a p l i n g ( c v , c m u , e p h e m e r a l K e y ) = B L A K E 2 b − 256 ( " Z c a s h D e r i v e o c k " , o c k I n p u t ) PRF_{ovk}^{ockSapling}(cv,cmu,ephemeralKey)=BLAKE2b-256("Zcash_Derive_ock", ockInput) PRFovkockSapling(cv,cmu,ephemeralKey)=BLAKE2b−256("ZcashDeriveock",ockInput),其中 o c k I n p u t ockInput ockInput为:
B L A K E 2 b − 256 ( " Z c a s h D e r i v e o c k " , o c k I n p u t ) BLAKE2b-256("Zcash_Derive_ock", ockInput) BLAKE2b−256("ZcashDeriveock",ockInput)必须为PRF,其输出为对称加密中的秘钥 S y m . K = B [ 256 ] Sym.K=\mathbb{B}^{[256]} Sym.K=B[256](为256-bit key)。 -
D i v e r s i f y H a s h DiversifyHash DiversifyHash:为 D i v e r s i f y H a s h S a p l i n g DiversifyHash^{Sapling} DiversifyHashSapling hash函数,用于派生a diversified base g d g_d gd。 D i v e r s i f y H a s h S a p l i n g : B [ l d ] → J ( r ) ∗ ∪ { ⊥ } DiversifyHash^{Sapling}: \mathbb{B}^{[l_d]}\rightarrow \mathbb{J}^{(r)^*}\cup \{\perp\} DiversifyHashSapling:B[ld]→J(r)∗∪{⊥},实际实现为:
D i v e r s i f y H a s h S a p l i n g ( d ) = G r o u p H a s h U J ( r ) ∗ ( " Z c a s h g d " , L E B S 2 O S P l d ( d ) ) DiversifyHash^{Sapling}(d)=GroupHash_{U}^{\mathbb{J}^{(r)^*}}("Zcash_gd", LEBS2OSP_{l_d}(d)) DiversifyHashSapling(d)=GroupHashUJ(r)∗("Zcashgd",LEBS2OSPld(d))
其中 U U U为MPC randomness beacon, G r o u p H a s h U J ( r ) ∗ GroupHash_{U}^{\mathbb{J}^{(r)^*}} GroupHashUJ(r)∗为Group Hash into Jubjub算法,输出为Jubjub point:
-
N o t e C o m m i t m e n t NoteCommitment NoteCommitment:为“Notes”章节中的 N o t e C o m m i t m e n t S a p l i n g NoteCommitment^{Sapling} NoteCommitmentSapling。
-
T o S c a l a r ToScalar ToScalar:为Sapling Key Components中的 T o S c a l a r S a p l i n g ToScalar^{Sapling} ToScalarSapling。 T o S c a l a r S a p l i n g ( x : B Y [ l P R F e x p a n d / 8 ] ) = L E O S 2 I P l P R F e x p a n d ( x ) ( m o d r J ) ToScalar^{Sapling}(x:\mathbb{B}^{\mathbb{Y}^{[l_{PRFexpand}/8]}})=LEOS2IP_{l_{PRFexpand}}(x)(\mod r_{\mathbb{J}}) ToScalarSapling(x:BY[lPRFexpand/8])=LEOS2IPlPRFexpand(x)(modrJ),输入为64byte值,输出为Jubjub的Scalar值。
1.2 对称加密
令
S
y
m
Sym
Sym 为authenticated one-time symmetric encryption scheme with keyspace
S
y
m
.
K
Sym.\mathbf{K}
Sym.K,对应的明文为
S
y
m
.
P
Sym.\mathbf{P}
Sym.P,对应的密文为
S
y
m
.
C
Sym.\mathbf{C}
Sym.C。
【此处的“one-time”是指,honest protocol参与方仅使用特定的key对一个消息进行加密,不会将该key用于重复加密。但是对于adversary来说,其可能对同一key用多个adaptive chosen ciphertext进行query。】
加密算法
S
y
m
.
E
n
c
r
y
p
t
Sym.Encrypt
Sym.Encrypt为:
S
y
m
.
K
×
S
y
m
.
P
→
S
y
m
.
C
Sym.\mathbf{K}\times Sym.\mathbf{P}\rightarrow Sym.\mathbf{C}
Sym.K×Sym.P→Sym.C。
解密算法
S
y
m
.
D
e
c
r
y
p
t
Sym.Decrypt
Sym.Decrypt为:
S
y
m
.
K
×
S
y
m
.
C
→
S
y
m
.
P
∪
{
⊥
}
Sym.\mathbf{K}\times Sym.\mathbf{C}\rightarrow Sym.\mathbf{P}\cup \{\perp\}
Sym.K×Sym.C→Sym.P∪{⊥}。
对应任意的
K
∈
S
y
m
.
K
K\in Sym.\mathbf{K}
K∈Sym.K 和
P
∈
S
y
m
.
P
P\in Sym.\mathbf{P}
P∈Sym.P,有
S
y
m
.
D
e
c
r
y
p
t
K
(
S
y
m
.
E
n
c
r
y
p
t
K
(
P
)
)
=
P
Sym.Decrypt_{K}(Sym.Encrypt_{K}(P))=P
Sym.DecryptK(Sym.EncryptK(P))=P。
⊥
\perp
⊥用于表示the decryption of an invalid ciphertext。
2. In-band secret distribution (Sapling)——in-band秘密分发
在Sapling中,note中需要秘密发送给接收方的信息有 d , v , r c m d,v,rcm d,v,rcm,同时也发送相应的memo field。
为了在不需要out-of-band communication channel的情况下将这些秘密安全的发送给接收方,会使用diversified transmission key p k d pk_d pkd对这些秘密进行加密。拥有相应incoming viewing key i v k ivk ivk的接收方可重构出原始的note和memo field。
与Sprout不同,每个Sapling shielded output会用新的ephemeral public key来加密。
2.1 Encryption (Sapling)
- p k d pk_d pkd:为 the diversified transmission key for the intended recipient address of a new Sapling note。为Jubjub point, K A . P u b l i c P r i m e S u b g r o u p KA.PublicPrimeSubgroup KA.PublicPrimeSubgroup。
- g d g_d gd:为diversifier d d d对应的diversified base, g d = D i v e r s i f y H a s h ( d ) g_d=DiversifyHash(d) gd=DiversifyHash(d)。为Jubjub point, K A . P u b l i c P r i m e S u b g r o u p KA.PublicPrimeSubgroup KA.PublicPrimeSubgroup。
仅在sending notes环节会对Sapling notes进行加密,此处可假设 g d g_d gd已计算且不为 ⊥ \perp ⊥。同时此处也假设ephemeral private key e s k esk esk也已选定。
o v k ovk ovk: B Y [ l o v k / 8 ] ∪ { ⊥ } \mathbb{B}^{\mathbb{Y}^{[l_{ovk}/8]}}\cup \{\perp\} BY[lovk/8]∪{⊥},为可用于decrypt 该payment的outgoing viewing key, o v k ovk ovk的取值可为以下之一:
- payment发送方(或发送方之一)的outgoing viewing key;
- the outgoing viewing key for all payments associated with an “account”,具体见ZIP-32中定义。
- 为 ⊥ \perp ⊥,if the sender should not be able to decrypt the payment once it has deleted its own copy。
n
p
=
(
l
e
a
d
B
y
t
e
:
B
Y
,
d
:
B
[
l
d
]
,
v
:
{
0..
2
l
v
a
l
u
e
−
1
}
,
m
e
m
o
:
B
Y
[
512
]
)
\mathbf{np}=(leadByte:\mathbb{B}^{\mathbb{Y}}, d:\mathbb{B}^{[l_d]},v:\{0..2^{l_{value}}-1\}, memo:\mathbb{B}^{\mathbb{Y}^{[512]}})
np=(leadByte:BY,d:B[ld],v:{0..2lvalue−1},memo:BY[512]):为Sapling note plaintext。【实际代码实现时,SaplingNotePlaintext
组成为(leadbyte, note, memo)
,具体见github.com/zcash/zcash/src/zcash/note.hpp
中相关定义,即实际
n
p
=
(
l
e
a
d
B
y
t
e
:
B
Y
,
d
:
B
[
l
d
]
,
v
:
{
0..
2
l
v
a
l
u
e
−
1
}
,
m
e
m
o
:
B
Y
[
512
]
,
p
k
d
,
r
c
m
)
\mathbf{np}=(leadByte:\mathbb{B}^{\mathbb{Y}}, d:\mathbb{B}^{[l_d]},v:\{0..2^{l_{value}}-1\}, memo:\mathbb{B}^{\mathbb{Y}^{[512]}}, pk_d, rcm)
np=(leadByte:BY,d:B[ld],v:{0..2lvalue−1},memo:BY[512],pkd,rcm)】
c
v
cv
cv:为Output description中的value commitment。
c
m
cm
cm:为Output description中的note commitment。
需要使用 c m , c v cm,cv cm,cv来派生the outgoing cipher key o c k ock ock,需要利用outgoing cipher key o c k ock ock来加密生成 outgoing ciphertext C o u t C^{out} Cout。
详细的加密流程为:
1)令
P
e
n
c
P^{enc}
Penc为
n
p
\mathbf{np}
np的raw encoding。
2)令
e
p
k
=
K
A
.
D
e
r
i
v
e
P
u
b
l
i
c
(
e
s
k
,
g
d
)
epk=KA.DerivePublic(esk,g_d)
epk=KA.DerivePublic(esk,gd)。
3)令
e
p
h
e
m
e
r
a
l
K
e
y
=
L
E
B
S
2
O
S
P
l
G
(
r
e
p
r
G
(
e
p
k
)
)
ephemeralKey=LEBS2OSP_{l_{\mathbb{G}}}(repr_{\mathbb{G}}(epk))
ephemeralKey=LEBS2OSPlG(reprG(epk))。
4)令
s
h
a
r
e
d
S
e
c
r
e
t
=
K
A
.
A
g
r
e
e
(
e
s
k
,
p
k
d
)
sharedSecret=KA.Agree(esk,pk_d)
sharedSecret=KA.Agree(esk,pkd)。
5)令
K
e
n
c
=
K
D
F
(
s
h
a
r
e
d
S
e
c
r
e
t
,
e
p
h
e
m
e
r
a
l
K
e
y
)
K^{enc}=KDF(sharedSecret, ephemeralKey)
Kenc=KDF(sharedSecret,ephemeralKey),其输入为the shared Diffie-Hellman secret
s
h
a
r
e
d
S
e
c
r
e
t
sharedSecret
sharedSecret 和 the ephemeral public key
e
p
k
epk
epk,输出为对称加密中的秘钥。
K
D
F
S
a
p
l
i
n
g
:
K
A
S
a
p
l
i
n
g
.
S
h
a
r
e
d
S
e
c
r
e
t
×
B
Y
[
l
J
/
8
]
→
S
y
m
.
K
KDF^{Sapling}:KA^{Sapling}.SharedSecret \times \mathbb{B}^{\mathbb{Y}^{[l_{\mathbb{J}}/8]}}\rightarrow Sym.\mathbf{K}
KDFSapling:KASapling.SharedSecret×BY[lJ/8]→Sym.K。
6)对称加密
C
e
n
c
=
S
y
m
.
E
n
c
r
y
p
t
K
e
n
c
(
P
e
n
c
)
C^{enc}=Sym.Encrypt_{K^{enc}}(P^{enc})
Cenc=Sym.EncryptKenc(Penc)
7.1)若
o
v
k
=
⊥
ovk=\perp
ovk=⊥,则选择随机
o
c
k
←
R
S
y
m
.
K
,
o
p
←
R
B
Y
[
(
l
G
+
256
)
/
8
]
ock\leftarrow_{R} Sym.\mathbf{K}, \mathbf{op}\leftarrow_R\mathbb{B}^{\mathbb{Y}^{[(l_{\mathbb{G}}+256)/8]}}
ock←RSym.K,op←RBY[(lG+256)/8]。
7.2)若
o
v
k
≠
⊥
ovk\neq \perp
ovk=⊥,则有:
令
c
v
=
L
E
B
S
2
O
S
P
l
G
(
r
e
p
r
G
(
c
v
)
)
cv=LEBS2OSP_{l_{\mathbb{G}}}(repr_{\mathbb{G}}(cv))
cv=LEBS2OSPlG(reprG(cv))
令
c
m
∗
=
L
E
B
S
2
O
S
P
256
(
E
x
t
r
a
c
t
G
(
r
)
(
c
m
)
)
cm*=LEBS2OSP_{256}(Extract_{\mathbb{G}^{(r)}}(cm))
cm∗=LEBS2OSP256(ExtractG(r)(cm))。【实际为encode the
u
u
u-coordinate of the note commitment。】
令
o
c
k
=
P
R
F
o
v
k
o
c
k
(
c
v
,
c
m
∗
,
e
p
h
e
m
e
r
a
l
K
e
y
)
ock=PRF_{ovk}^{ock}(cv,cm*,ephemeralKey)
ock=PRFovkock(cv,cm∗,ephemeralKey)
令
o
p
=
L
E
B
S
2
O
S
P
l
G
+
256
(
r
e
p
r
G
(
p
k
d
)
∣
∣
l
2
L
E
B
S
P
256
(
e
s
k
)
)
\mathbf{op}=LEBS2OSP_{l_{\mathbb{G}}+256}(repr_{\mathbb{G}}(pk_d) || l2LEBSP_{256}(esk))
op=LEBS2OSPlG+256(reprG(pkd)∣∣l2LEBSP256(esk))
8)对称加密
C
o
u
t
=
S
y
m
.
E
n
c
r
y
p
t
o
c
k
(
o
p
)
C^{out}=Sym.Encrypt_{ock}(\mathbf{op})
Cout=Sym.Encryptock(op)。
最终加密后的transmitted note ciphertext 为 ( e p h e m e r a l K e y , C e n c , C o u t ) (ephemeralKey, C^{enc}, C^{out}) (ephemeralKey,Cenc,Cout)。
注意:若改为依赖于out-of-band transmission of the note to the recipient,从技术角度来看,可将特定note的 C e n c C^{enc} Cenc替换为随机(且不可解密)的密文。此时,为了保证indistinguishability from other Output descriptions,ephemeral key 仍然必须生成为random public key(而不是a random bit sequence)。这种操作模式有其它安全性的考量,如如何validate a Sapling note received out-of-band,在zcash协议文档中未详细阐述。
2.2 采用incoming viewing key来解密(Sapling)【接收方】
- i v k ivk ivk: { 0.. 2 251 − 1 } \{0..2^{251}-1\} {0..2251−1},为接收方的incoming viewing key。
- ( e p h e m e r a l K e y , C e n c , C o u t ) (ephemeralKey, C^{enc},C^{out}) (ephemeralKey,Cenc,Cout):为the transmitted note ciphertext from the Output description。
- c m ∗ cm* cm∗:为Output description的 c m u cmu cmu field。
接收方将尝试解密transmitted note ciphertext中的
e
p
h
e
m
e
r
a
l
K
e
y
ephemeralKey
ephemeralKey和
C
e
n
c
C^{enc}
Cenc,具体的解密流程为:
1)令
e
p
k
=
a
b
s
t
G
(
e
p
h
e
m
e
r
a
l
K
e
y
)
epk=abst_{\mathbb{G}}(ephemeralKey)
epk=abstG(ephemeralKey),若
e
p
k
=
⊥
epk=\perp
epk=⊥,则返回
⊥
\perp
⊥。
2)令
s
h
a
r
e
d
S
e
c
r
e
t
=
K
A
.
A
g
r
e
e
(
i
v
k
,
e
p
k
)
sharedSecret=KA.Agree(ivk,epk)
sharedSecret=KA.Agree(ivk,epk)。
3)令
K
e
n
c
=
K
D
F
(
s
h
a
r
e
d
S
e
c
r
e
t
,
e
p
h
e
m
e
r
a
l
K
e
y
)
K^{enc}=KDF(sharedSecret, ephemeralKey)
Kenc=KDF(sharedSecret,ephemeralKey),其输入为the shared Diffie-Hellman secret
s
h
a
r
e
d
S
e
c
r
e
t
sharedSecret
sharedSecret 和 the ephemeral public key
e
p
k
epk
epk,输出为对称加密中的秘钥。
K
D
F
S
a
p
l
i
n
g
:
K
A
S
a
p
l
i
n
g
.
S
h
a
r
e
d
S
e
c
r
e
t
×
B
Y
[
l
J
/
8
]
→
S
y
m
.
K
KDF^{Sapling}:KA^{Sapling}.SharedSecret \times \mathbb{B}^{\mathbb{Y}^{[l_{\mathbb{J}}/8]}}\rightarrow Sym.\mathbf{K}
KDFSapling:KASapling.SharedSecret×BY[lJ/8]→Sym.K。
4)对称解密
P
e
n
c
=
S
y
m
.
D
e
c
r
y
p
t
K
e
n
c
(
C
e
n
c
)
P^{enc}=Sym.Decrypt_{K^{enc}}(C^{enc})
Penc=Sym.DecryptKenc(Cenc),若
P
e
n
c
=
⊥
P^{enc}=\perp
Penc=⊥,则返回
⊥
\perp
⊥。
5)decode
P
e
n
c
P^{enc}
Penc,提取
n
p
=
(
l
e
a
d
B
y
t
e
:
B
Y
,
d
:
B
[
l
d
]
,
v
:
{
0..
2
l
v
a
l
u
e
−
1
}
,
m
e
m
o
:
B
Y
[
512
]
,
p
k
d
,
r
c
m
)
\mathbf{np}=(leadByte:\mathbb{B}^{\mathbb{Y}}, d:\mathbb{B}^{[l_d]},v:\{0..2^{l_{value}}-1\}, memo:\mathbb{B}^{\mathbb{Y}^{[512]}}, pk_d, rcm)
np=(leadByte:BY,d:B[ld],v:{0..2lvalue−1},memo:BY[512],pkd,rcm)
6)令
r
c
m
=
L
E
O
S
2
I
P
256
(
r
c
m
)
rcm=LEOS2IP_{256}(rcm)
rcm=LEOS2IP256(rcm),
g
d
=
D
i
v
e
r
s
i
f
y
H
a
s
h
(
d
)
g_d=DiversifyHash(d)
gd=DiversifyHash(d),若
r
c
m
≥
r
G
或
g
d
=
⊥
rcm\geq r_{\mathbb{G}}或g_d=\perp
rcm≥rG或gd=⊥,则返回
⊥
\perp
⊥。
7)计算
p
k
d
=
K
A
.
D
e
r
i
v
e
P
u
b
l
i
c
(
i
v
k
,
g
d
)
pk_d=KA.DerivePublic(ivk,g_d)
pkd=KA.DerivePublic(ivk,gd)。
8)令
n
=
(
d
,
p
k
d
,
v
,
r
c
m
)
\mathbf{n}=(d,pk_d,v,rcm)
n=(d,pkd,v,rcm)
9)计算
c
m
∗
′
=
N
o
t
e
C
o
m
m
i
t
m
e
n
t
(
n
)
cm*'=NoteCommitment(\mathbf{n})
cm∗′=NoteCommitment(n),若
l
2
L
E
O
S
P
256
(
E
x
t
r
a
c
t
G
(
r
)
(
c
m
∗
′
)
)
=
c
m
∗
l2LEOSP_{256}(Extract_{\mathbb{G}^{(r)}}(cm*'))=cm*
l2LEOSP256(ExtractG(r)(cm∗′))=cm∗不成立,则返回
⊥
\perp
⊥。
10)最终解密结果为
(
n
,
m
e
m
o
)
(\mathbf{n}, memo)
(n,memo)。
注意:
1)通常情况下,只有区块交易中的transmitted note ciphertexts 需要被解密。此时,任何received Sapling note都必须为positioned note,使得可快速计算相应的
ρ
\rho
ρ值。
2)在Sapling中,每个positioned note有关联的
ρ
\rho
ρ值,该
ρ
\rho
ρ值可根据note commitment
c
m
cm
cm 和 note position
p
o
s
pos
pos计算出来:
ρ
=
M
i
x
i
n
g
P
e
d
e
r
s
e
n
H
a
s
h
(
c
m
,
p
o
s
)
\rho = MixingPedersenHash(cm,pos)
ρ=MixingPedersenHash(cm,pos)
对于Sapling note,其nullifier
n
f
nf
nf 可derived as
P
R
F
n
k
⋆
n
f
S
a
p
l
i
n
g
(
ρ
⋆
)
PRF_{nk\star}^{nfSapling}(\rho\star)
PRFnk⋆nfSapling(ρ⋆),其中
n
k
⋆
nk\star
nk⋆代表与note关联的nullifier deriving key,
ρ
⋆
=
r
e
p
r
J
(
ρ
)
\rho\star=repr_{\mathbb{J}}(\rho)
ρ⋆=reprJ(ρ)。
3)判断某个Sapling note是否为unspent的,需用到nullifier deriving key,并根据以上公式计算出nullifier值,判断该nullifier值是否在区块链的nullifier set中,若在,则表示该Sapling note已花费。
4)从区块链节点的角度来看,note由unspent变为spent是通过best valid block chain中的新交易实现的。若区块链重构了,选择了一条不同的best valid block chain,则可能会存在某个note 为output的交易不在区块链中的情况。
5)客户端可能会尝试解密mempool中交易的transmitted note ciphertext。但是,此时,不应假设该交易即将mined,而应将解密后的信息看成是临时的、私有的。???
2.3 采用full viewing key解密(Sapling)【发送方的outgoing viewing key】
- o v k ovk ovk: B Y [ l o v k / 8 ] \mathbb{B}^{\mathbb{Y}^{[l_{ovk}/8]}} BY[lovk/8],为发送方的outgoing viewing key。【若加密过程中使用的 o v k ovk ovk为 ⊥ \perp ⊥,则该payment无法用本节办法解密。】
- ( e p h e m e r a l K e y , C e n c , C o u t ) (ephemeralKey, C^{enc},C^{out}) (ephemeralKey,Cenc,Cout):为the transmitted note ciphertext from the Output description。
- c m ∗ cm* cm∗:为Output description的 c m u cmu cmu field。
- c v cv cv:为Output description的 c v cv cv field。
拥有outgoing viewing key的发送方,将尝试解密transmitted note ciphertext中的
e
p
h
e
m
e
r
a
l
K
e
y
ephemeralKey
ephemeralKey和
C
e
n
c
C^{enc}
Cenc以及
C
o
u
t
C^{out}
Cout,具体的解密流程为:
1)令
o
c
k
=
P
R
F
o
v
k
o
c
k
(
c
v
,
c
m
∗
,
e
p
h
e
m
e
r
a
l
K
e
y
)
ock=PRF_{ovk}^{ock}(cv,cm*,ephemeralKey)
ock=PRFovkock(cv,cm∗,ephemeralKey)
2)对称解密
o
p
=
S
y
m
.
D
e
c
r
y
p
t
o
c
k
(
C
o
u
t
)
\mathbf{op}=Sym.Decrypt_{ock}(C^{out})
op=Sym.Decryptock(Cout),若
o
p
=
⊥
\mathbf{op}=\perp
op=⊥,则返回
⊥
\perp
⊥。
3)从
o
p
\mathbf{op}
op中提取出
(
p
k
⋆
d
:
B
[
l
G
]
,
e
s
k
:
B
Y
[
32
]
)
(pk\star_d:\mathbb{B}^{[l_{\mathbb{G}}]}, esk: \mathbb{B}^{\mathbb{Y}^{[32]}})
(pk⋆d:B[lG],esk:BY[32])。
4)令
e
s
k
=
L
E
O
S
2
I
P
256
(
e
s
k
)
,
p
k
d
=
a
b
s
t
G
(
p
k
⋆
d
)
esk=LEOS2IP_{256}(esk), pk_d=abst_{\mathbb{G}}(pk\star_d)
esk=LEOS2IP256(esk),pkd=abstG(pk⋆d),若
e
s
k
≥
r
G
esk\geq r_{\mathbb{G}}
esk≥rG或
p
k
d
=
⊥
pk_d=\perp
pkd=⊥,则返回
⊥
\perp
⊥。
5)令
s
h
a
r
e
d
S
e
c
r
e
t
=
K
A
.
A
g
r
e
e
(
e
s
k
,
p
k
d
)
sharedSecret=KA.Agree(esk, pk_d)
sharedSecret=KA.Agree(esk,pkd)。
6)令
K
e
n
c
=
K
D
F
(
s
h
a
r
e
d
S
e
c
r
e
t
,
e
p
h
e
m
e
r
a
l
K
e
y
)
K^{enc}=KDF(sharedSecret, ephemeralKey)
Kenc=KDF(sharedSecret,ephemeralKey),其输入为the shared Diffie-Hellman secret
s
h
a
r
e
d
S
e
c
r
e
t
sharedSecret
sharedSecret 和 the ephemeral public key
e
p
k
epk
epk,输出为对称加密中的秘钥。
K
D
F
S
a
p
l
i
n
g
:
K
A
S
a
p
l
i
n
g
.
S
h
a
r
e
d
S
e
c
r
e
t
×
B
Y
[
l
J
/
8
]
→
S
y
m
.
K
KDF^{Sapling}:KA^{Sapling}.SharedSecret \times \mathbb{B}^{\mathbb{Y}^{[l_{\mathbb{J}}/8]}}\rightarrow Sym.\mathbf{K}
KDFSapling:KASapling.SharedSecret×BY[lJ/8]→Sym.K。
7)对称解密
P
e
n
c
=
S
y
m
.
D
e
c
r
y
p
t
K
e
n
c
(
C
e
n
c
)
P^{enc}=Sym.Decrypt_{K^{enc}}(C^{enc})
Penc=Sym.DecryptKenc(Cenc),若
P
e
n
c
=
⊥
P^{enc}=\perp
Penc=⊥,则返回
⊥
\perp
⊥。
8)decode
P
e
n
c
P^{enc}
Penc,提取
n
p
=
(
l
e
a
d
B
y
t
e
:
B
Y
,
d
:
B
[
l
d
]
,
v
:
{
0..
2
l
v
a
l
u
e
−
1
}
,
m
e
m
o
:
B
Y
[
512
]
,
p
k
d
,
r
c
m
)
\mathbf{np}=(leadByte:\mathbb{B}^{\mathbb{Y}}, d:\mathbb{B}^{[l_d]},v:\{0..2^{l_{value}}-1\}, memo:\mathbb{B}^{\mathbb{Y}^{[512]}}, pk_d, rcm)
np=(leadByte:BY,d:B[ld],v:{0..2lvalue−1},memo:BY[512],pkd,rcm)
9)令
r
c
m
=
L
E
O
S
2
I
P
256
(
r
c
m
)
rcm=LEOS2IP_{256}(rcm)
rcm=LEOS2IP256(rcm),
g
d
=
D
i
v
e
r
s
i
f
y
H
a
s
h
(
d
)
g_d=DiversifyHash(d)
gd=DiversifyHash(d),若
r
c
m
≥
r
G
或
g
d
=
⊥
rcm\geq r_{\mathbb{G}}或g_d=\perp
rcm≥rG或gd=⊥ 或
p
k
d
∉
J
(
r
)
pk_d\notin \mathbb{J}^{(r)}
pkd∈/J(r),则返回
⊥
\perp
⊥。【注意,此处增加了 要求the decoded point
p
k
d
pk_d
pkd of a Sapling note to be in the subgroup
J
(
r
)
\mathbb{J}^{(r)}
J(r),即判断
若
p
k
d
∉
J
(
r
)
,
返
回
⊥
若pk_d\notin \mathbb{J}^{(r)},返回\perp
若pkd∈/J(r),返回⊥。】
10)令
n
=
(
d
,
p
k
d
,
v
,
r
c
m
)
\mathbf{n}=(d,pk_d,v,rcm)
n=(d,pkd,v,rcm)
11)计算
c
m
∗
′
=
N
o
t
e
C
o
m
m
i
t
m
e
n
t
(
n
)
cm*'=NoteCommitment(\mathbf{n})
cm∗′=NoteCommitment(n),若
l
2
L
E
O
S
P
256
(
E
x
t
r
a
c
t
G
(
r
)
(
c
m
∗
′
)
)
=
c
m
∗
l2LEOSP_{256}(Extract_{\mathbb{G}^{(r)}}(cm*'))=cm*
l2LEOSP256(ExtractG(r)(cm∗′))=cm∗不成立,则返回
⊥
\perp
⊥。
12)若
r
e
p
r
G
(
K
A
.
D
e
r
i
v
e
P
u
b
l
i
c
(
e
s
k
,
g
d
)
)
≠
e
p
h
e
m
e
r
a
l
K
e
y
repr_{\mathbb{G}}(KA.DerivePublic(esk,g_d))\neq ephemeralKey
reprG(KA.DerivePublic(esk,gd))=ephemeralKey,则返回
⊥
\perp
⊥。
10)最终解密结果为
(
n
,
m
e
m
o
)
(\mathbf{n}, memo)
(n,memo)。
3. Block chain scanning (Sapling)
在Sapling中,区块链扫描仅需 n k nk nk和 i v k ivk ivk。之前的Sprout中扫描需要spending key。【Sapling中区块扫描的实际代码实现,还额外用到了 a k ak ak,即需要的key参数有 ( a k , n k , i v k ) (ak,nk,ivk) (ak,nk,ivk)。】
已知
(
n
k
,
i
v
k
)
(nk,ivk)
(nk,ivk),对区块链扫描可获取 each note sent to the corresponding shielded payment address, its memo field, and its final status (spent or unspent)。完整的扫描流程为:
注意:
1)以上算法并不需要用到
o
v
k
ovk
ovk,或者transmitted note ciphertext中的
C
o
u
t
C^{out}
Cout。原因在于,当扫描整个区块链时,确实不需要
o
v
k
ovk
ovk或
C
o
u
t
C^{out}
Cout。之所以支持使用
o
v
k
ovk
ovk来解密(如2.3节所示),是为了允许在仅有交易本身的情况下,可恢复交易中发送的note plaintext信息。
2)当扫描区块链中部分区块时,采用
o
v
k
ovk
ovk来解密每笔交易中的
C
o
u
t
C^{out}
Cout是有益的。可以扫描出区块区间中所spent的note信息,尽管该note的receive信息可能在区块区间之外。
3)以上算法无法发现 通过“out-of-band” 或者 with incorrect transmitted note ciphertexts 的notes信息。It is possible to detect whether such notes were spent only if their nullifiers are known。
Zcash的rustzcash.rs
实际实现中,计算note nullifier值
n
f
\mathbf{nf}
nf时,不只用到了
n
k
nk
nk,还用到了
a
k
ak
ak。
/// Compute Sapling note nullifier.
#[no_mangle]
pub extern "system" fn librustzcash_sapling_compute_nf(
diversifier: *const [c_uchar; 11],
pk_d: *const [c_uchar; 32],
value: uint64_t,
r: *const [c_uchar; 32],
ak: *const [c_uchar; 32],
nk: *const [c_uchar; 32],
position: uint64_t,
result: *mut [c_uchar; 32],
) -> bool {
let note = match priv_get_note(diversifier, pk_d, value, r) {
Ok(p) => p,
Err(_) => return false,
};
let ak = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*ak })[..], &JUBJUB) {
Ok(p) => p,
Err(_) => return false,
};
let ak = match ak.as_prime_order(&JUBJUB) {
Some(ak) => ak,
None => return false,
};
let nk = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*nk })[..], &JUBJUB) {
Ok(p) => p,
Err(_) => return false,
};
let nk = match nk.as_prime_order(&JUBJUB) {
Some(nk) => nk,
None => return false,
};
let vk = ViewingKey { ak, nk };
let nf = note.nf(&vk, position, &JUBJUB);
let result = unsafe { &mut *result };
result.copy_from_slice(&nf);
true
}