Define Derive_rcmrseedSapling(leadByte)={LEOS2IP256(rseed),ToScalarSapling(PRFrseedexpand([0x04])),if leadByte=0x01if leadByte=0x02
Define Hrseedesk,Sapling(_)=ToScalarSapling(PRFrseedexpand([0x05])).
(Hesk,Sapling intentionally takes an argument that is unused.)
Replace the lines deriving rcm and esk with
Derive rcm=Derive_rcmrseedSapling(leadByte)
Derive esk=Hrseedesk,Sapling(⊥)
§ 4.7.3 ‘Sending Notes (Orchard)’
Add before "For each Action description":
Define Hrseedrcm,Orchard(g⋆d,pk⋆d,v,ρ,ψ)=
ToScalarOrchard(PRFrseedexpand(pre_rcm))
where pre_rcm=[0x0B]∣∣LEBS2OSP256(g⋆d)∣∣LEBS2OSP256(pk⋆d)∣∣I2LEOSP64(v)∣∣ρ∣∣I2LEOSP256(ψ).
Define Derive_rcmrseedOrchard(leadByte,g⋆d,pk⋆d,v,ρ,ψ)={ToScalarOrchard(PRFrseedexpand([0x05]∣∣ρ)),Hrseedrcm,Orchard(g⋆d,pk⋆d,v,ρ,ψ),if leadByte=0x02if leadByte=0x03
Define Hrseedesk,Orchard(ρ)=ToScalarOrchard(PRFrseedexpand([0x04]∣∣ρ)).
Define Hrseedψ,Orchard(ρ)=ToBaseOrchard(PRFrseedexpand([0x09]∣∣ρ)).
The first-byte domain separator 0x0A for PRFexpand is reserved by this ZIP for use by split notes in ZIP 226 26.
Insert before the derivation of esk:
Let g⋆d=reprP(gd),
and pk⋆d=reprP(pkd).
and use these in the inputs to NoteCommitOrchard.
Replace the lines deriving esk, rcm, and ψ
with
Derive esk=Hrseedesk,Orchard(ρ)
Derive ψ=Hrseedψ,Orchard(ρ)
Derive rcm=Derive_rcmrseedOrchard(leadByte,g⋆d,pk⋆d,v,ρ,ψ)
(The order of derivation has to be changed because the inputs to Derive_rcm
include ψ.)
§ 4.8.2 ‘Dummy Notes (Sapling)’
Add
Let Derive_rcmSapling be as defined in
§ 4.7.2 ‘Sending Notes (Sapling)’.
Replace the line deriving rcm with
Derive rcm=Derive_rcmrseedSapling(leadByte)
§ 4.8.3 ‘Dummy Notes (Orchard)’
Insert before "The spend-related fields ...":
Let Derive_rcmOrchard and Hψ,Orchard be
as defined in § 4.7.3 ‘Sending Notes (Orchard)’.
Replace the lines deriving rcm and ψ with
Let g⋆d=reprP(gd),
and pk⋆d=reprP(pkd).
Derive ψ=Hrseedψ,Orchard(ρ)
Derive rcm=Derive_rcmrseedOrchard(leadByte,g⋆d,pk⋆d,v,ρ,ψ)
and use g⋆d, pk⋆d,
in the inputs to NoteCommitOrchard.
(The order of derivation has to be changed because the inputs to Derive_rcm
include ψ.)
The choice of leadByte, as specified in § 3.2.1, is identical between
dummy and non-dummy notes.
§ 4.20.2 and § 4.20.3 ‘Decryption using an Incoming/Full Viewing Key (Sapling and Orchard)’
For both § 4.20.2 and § 4.20.3, add before the decryption procedure:
Let Derive_rcmSapling and Hesk,Sapling be
as defined in § 4.7.2 ‘Sending Notes (Sapling)’.
Let Derive_rcmOrchard, Hesk,Orchard,
and Hψ,Orchard be as defined in
§ 4.7.3 ‘Sending Notes (Orchard)’.
For § 4.20.3, replace
if esk≥rG or pkd=⊥, return ⊥
with
if esk≥rG or pkd=⊥ or (for Sapling) pkd∈J(r)∗
(see note below), return ⊥
and in the note containing "However, this is technically redundant with the
later check that returns ⊥ if pkd∈J(r)∗",
delete the word "later".
For both § 4.20.2 and § 4.20.3, replace
for Sapling, let pre_rcm=[4] and pre_esk=[5]
for Orchard, let ρ=I2LEOSP256(nfold from the same Action description ), pre_rcm=[5]∣∣ρ, and pre_esk=[4]∣∣ρ
with
let ρ=I2LEOSP256(nfold from the same Action description )
For § 4.20.2, replace
let rcm={LEOS2IP256(rseed),ToScalar(PRFrseedexpand(pre_rcm)),if leadByte=0x01otherwise
if rcm≥rG, return ⊥
let gd=DiversifyHash(d). if (for Sapling) gd=⊥, return ⊥
[Canopy onward] if leadByte=0x01:
esk=ToScalarprotocol(PRFrseedexpand(pre_esk))
if reprG(KA.DerivePublic(esk,gd))=ephemeralKey, return ⊥
let pkd=KA.DerivePublic(ivk,gd)
with
let gd=DiversifyHash(d). if (for Sapling) gd=⊥, return ⊥
[Canopy onward] if leadByte=0x01:
let esk=Hrseedesk,protocol(ρ)
if reprG(KA.DerivePublic(esk,gd))=ephemeralKey, return ⊥
let pkd=KA.DerivePublic(ivk,gd)
let g⋆d=reprP(gd), pk⋆d=reprP(pkd)
let ψ=Hrseedψ,Orchard(ρ) for Orchard or ⊥ for Sapling
let rcm={Derive_rcmrseedSapling(leadByte),Derive_rcmrseedOrchard(leadByte,g⋆d,pk⋆d,v,ρ,ψ),if protocol=Saplingif protocol=Orchard
if rcm≥rG, return ⊥
The order of operations has to be altered because the derivation of
rcm can depend on gd and pkd.
The definitions of pre_rcm and pre_esk are moved or inlined
into § 4.7.2 ‘Sending Notes (Sapling)’ and § 4.7.3 ‘Sending Notes (Orchard)’ which
define Derive_rcm{Sapling,Orchard} and Hesk,{Sapling,Orchard}.
For § 4.20.3, replace
let rcm={LEOS2IP256(rseed),ToScalar(PRFrseedexpand(pre_rcm)),if leadByte=0x01otherwise
if rcm≥rG, return ⊥
let gd=DiversifyHash(d). if (for Sapling) gd=⊥ or pkd∈J(r)∗ (see note below), return ⊥
with
let gd=DiversifyHash(d). if (for Sapling) gd=⊥, return ⊥
let g⋆d=reprP(gd), pk⋆d=reprP(pkd)
let ψ=Hrseedψ,Orchard(ρ) for Orchard or ⊥ for Sapling
let rcm={Derive_rcmrseedSapling(leadByte),Derive_rcmrseedOrchard(leadByte,g⋆d,pk⋆d,v,ρ,ψ),if protocol=Saplingif protocol=Orchard
if rcm≥rG, return ⊥
and delete "where ψ=ToBaseOrchard(PRFrseedexpand([9]∣∣ρ))".
§ 5.3 ‘Constants’
Add the definitions
ℓqsk⦂N:=256
ℓqk⦂N:=256
Changes to ZIPs
ZIP 32
Add a ∗ to the arrows leading to ask and rivk
in the diagram in section ‘Orchard internal key derivation’
12, with the following note:
∗ The derivations of ask and rivk shown in
the diagram are not the only possibility. For further detail see
§ 4.2.3 ‘Orchard Key Components’ in the protocol specification.
However, if ask, ak, nk, rivk,
and rivk_internal are not generated as in the diagram, then
it may not be possible to recover the resulting notes as specified in
[ZIP 2005] in the event that attacks using quantum computers become
practical.
ZIP 212
Add a note before the Abstract:
This ZIP reflects the changes made to note encryption for the Canopy upgrade.
It does not include subsequent changes in [ZIP 2005].
Rationale
Cryptographic background
This rationale is written primarily for cryptologists and protocol
designers familiar with the Zcash shielded protocols. It is recommended
to first read the slides of, and/or watch the following presentations:
- Understanding Zcash Security at Zcon3 27
- Post-Quantum Zcash at ZconVI 8
To understand the modelling of hash function and commitment security
against a quantum adversary we also recommend 28 and 29.
Proposed Recovery Protocol
The details of the protocol in this section are subject to change. This
description is meant to facilitate analysis of whether the specification
changes to be adopted now are likely to be sufficient to securely support
a Recovery Protocol.
The proposed Recovery Protocol works, roughly speaking, by enforcing the
derivations given in the [Flow diagram for the Orchard protocol], and we
suggest having that diagram open in another window to refer to it.
Proposed Recovery Statement
Import this definition from § 4.2.3 ‘Orchard Key Components’ 15:
Define Hqkrivk_ext(ak,nk)=ToScalarOrchard(PRFqkexpand([0x0D]∣∣I2LEOSP256(ak)∣∣I2LEOSP256(nk))).
Import this definition from ZIP 32 12:
Hrivk_extrivk_int(ak,nk)=ToScalarOrchard(PRFrivk_extexpand([0x83]∣∣I2LEOSP256(ak)∣∣I2LEOSP256(nk)))
Import these definitions from § 4.7.3 ‘Sending Notes (Orchard)’ 30:
Define Hrseedrcm,Orchard(g⋆d,pk⋆d,v,ρ,ψ)=
ToScalarOrchard(PRFrseedexpand(pre_rcm))
where pre_rcm=[0x0B]∣∣LEBS2OSP256(g⋆d)∣∣LEBS2OSP256(pk⋆d)∣∣I2LEOSP64(v)∣∣ρ∣∣I2LEOSP256(ψ).
Define Hrseedψ,Orchard(ρ)=ToBaseOrchard(PRFrseedexpand([0x09]∣∣ρ)).
Import this definition from § 5.4.7.1 ‘Spend Authorization Signature (Sapling and Orchard)’ 31:
Define GOrchard=GroupHashP(“z.cash:Orchard”,“G”).
A valid instance of a Recovery Statement assures that given a primary input:
(rtOrchardrknfSigHash⦂{0..qP−1},⦂SpendAuthSigOrchard.Public),⦂FqP,⦂MessageHash)
the prover knows an auxiliary input:
(pathposαakPnkrivk_extrivk(qk,σqsk)σskrseedgdvρ⦂{0..qP−1}[MerkleDepthOrchard],⦂{0..2MerkleDepthOrchard−1},⦂FrP,⦂P∗,⦂FqP,⦂FrP,⦂FrP,⦂(BY[ℓqk/8]×SoKqsk((qk),SigHash))∪{(⊥,⊥)},⦂SoKsk((akP,nk,rivk_ext),SigHash)∪{⊥},⦂BY[32],⦂P∗,⦂{0..2ℓvalue−1},⦂FqP)
where
BindKeyssk(sk,akP,nk,rivk_ext)=(akP=[Hask(sk)]GOrchard∧nk=Hnk(sk)∧rivk_ext=Hrivk_legacy(sk))
and
SoKqsk.StatementSoKsk.Statement={qsk⦂BY[ℓqsk/8]={sk⦂B[ℓsk]∣qk=Hqk(qsk)}∣BindKeyssk(sk,akP,nk,rivk_ext)}
such that the following conditions hold:
{rk=SpendAuthSigOrchard.RandomizePublic(α,akP)(∧ak=ExtractP(akP)(∧((qk,σqsk)=(⊥,⊥))=(σsk=⊥)(∧(qk,σqsk)=(⊥,⊥)⇒(SoKqsk.Validate((qk),SigHash,σqsk)∧rivk_ext=Hqkrivk_ext(ak,nk))(∧σsk=⊥⇒SoKsk.Validate((akP,nk,rivk_ext),SigHash,σsk)(∧rivk∈{rivk_ext,Hrivk_extrivk_int(ak,nk)}(∧let ivk=Commitrivkivk(ak,nk)(∧ivk∈{0,⊥}(∧let pkd=[ivk]gd(∧let ψ=Hrseedψ,Orchard(ρ)(∧let noterepr=(reprP(gd),reprP(pkd),v,ρ,ψ)(∧let rcm=Hrseedrcm,Orchard(0x03,noterepr)(∧let cm=NoteCommitrcmOrchard(noterepr)(∧cm=⊥(∧let cmx=ExtractP(cm)(∧let leaf=MerkleCRH(cmx,ρ)(∧path is a path to leaf in the rehashed commitment tree(∧nf=DeriveNullifiernk(ρ,ψ,cm)(}
and nf is the revealed nullifier.
(We don't need to check the derivation of gd from d.)
Cost
Note that in the "use_qsk" case, one BLAKE2b compression is required
to compute rivk_ext (provided ℓqk≤504 bits, so
that the input qk∣∣[0x0D]∣∣ak∣∣nk
fits in a single 128-byte BLAKE2b block), and in the "not use_qsk"
case, three BLAKE2b compressions in total are required to compute Hask,
Hnk, and Hrivk_legacy. Since these cases are mutually
exclusive, it is possible to multiplex the same three compression function instances.
So, supporting "use_qsk" in addition to "not use_qsk" costs
very little extra.
All of the operations below need to be implemented with complete additions,
even if they are incomplete in the current Orchard[ZSA] statement/circuit.
In SoKsk and the main circuit:
- 7 BLAKE2b-512 compressions:
- 3 multiplexed between Hrivk_ext when use_qsk is true (1 compression),
and Hask + Hnk + Hrivk_legacy when use_qsk is false (3 compressions)
- 1 to compute Hrivk_int
- 1 to compute Hψ
- 2 to compute Hrcm
- 1 use of Commitivk (SinsemillaShortCommit)
- 1 use of NoteCommit (SinsemillaCommit)
- 1 full-width fixed-base Pallas scalar multiplication, [ask]GOrchard
- 1 full-width variable-base Pallas scalar multiplication, [ivk]gd
- 1 Merkle tree path check
- 1 additional use of MerkleCRH to compute leaf.
In SoKqsk:
- 1 BLAKE3 compression for Hqk (with the derive_key context key precomputed as a constant)
plus, potentially, linking commitments between
the circuits.
The expensive parts of this are the 7 BLAKE2b compressions.
Security analysis
Let us consider the security of the Orchard protocol against a
discrete-logarithm-breaking adversary — which by definition includes
quantum adversaries. Similar attacks apply to the Sapling protocol.
Suppose that the proof system has been replaced by one that is
post-quantum knowledge-sound.
Repairing the note commitment Merkle tree
MerkleCRH is instantiated using
SinsimillaHash 32.
Its collision resistance depends on the Discrete Logarithm Relation Problem
on the Pallas curve 33, and so it is not
post-quantum collision-resistant or collapsing. However, because the note
commitment tree is public, it is possible to re-hash all of its leaves to
construct a new Merkle tree using a collapsing hash function.
Suppose this has been done.
The post-quantum security of Merkle trees —considered as position-binding
vector commitments which is the property required by Zcash 27—
is proven under reasonable assumptions in 34, 35, and
36.
Note: when we rehash the commitment tree, we could include both ρ and
cmx for each note (i.e. what is currently the leaf layer becomes
MerkleCRH(ρ,cmx) where MerkleCRH
is collapsing). This change might not be necessary; it just removes potential
complications due to duplicate commitments for the same note.
Attacks against binding of note commitments
We still face the problem that NoteCommit is not binding against
a discrete-logarithm-breaking adversary: given the discrete logarithm
relations between bases, we can easily write a linear equation in the
scalar field with multiple solutions of the inputs for a given commitment.
This allows an adversary to find two distinct notes corresponding to
openings of NoteCommit on the same commitment.
They create one note as an output and spend the other note — which may
have a greater value, or a value in a different ZSA asset, breaking the
Balance property.
Repairing note commitments
We prefer to fix this without changing NoteCommit itself.
Instead we change how rcm is computed to be a hash of rseed and
noterepr=(g⋆d,pk⋆d,v,ρ,ψ),
as detailed in the [Specification] section.
Specifically, when leadByte=0x03 we have:
rcm=Hrseedrcm(noterepr)=ToScalar(PRFrseedexpand(pre_rcm))
where pre_rcm=[0x0B]∣∣encode(noterepr)
Informal security argument for binding of note commitments
For the formal versions of these arguments and their adaptation to the
post-quantum setting, see the key-binding and
Spendability theorems below, and the
Adaptation to the quantum setting section.
We can view the output of NoteCommitrcm for
notetuple=(rseed,noterepr)
as the point addition of a randomization term
[Hrseedrcm(noterepr)]R,
and some other function of notetuple.
Without loss of generality, we can write that function as
[f(notetuple)]R,
by expanding each of the Sinemilla bases
Cj=Q(D) or S(j) used by
HashToSinsimillaPoint
as Cj=[cj]R for some cj. That is,
the note commitment for notetuple is
[Hrseedrcm(noterepr)+f(notetuple)]R.
We will model Hrcm as a random oracle independent of f with
uniform output on FrP. This is reasonable because
Hrcm cannot depend on any of the cj, and in any case it is
instantiated using BLAKE2b, a conventional hash function not related to the
Pallas curve.
The fact that NoteCommit has
ψ=Hrseedψ(ρ) as an
input does not affect the analysis provided that Hrcm and
Hψ can be treated as independent. In practice both
are defined in terms of PRFexpand, but with strict domain
separation, and so the assumption of independence is reasonable as long
as BLAKE2b-512 can be modelled as a random oracle. Note that since the
input to Hrcm needs more than one BLAKE2b input block, we
require that a HAIFA sponge can be modelled as a random oracle which is
justified by 37. It is possible that there could be
better-than-generic quantum attacks against BLAKE2b-512, but none have
been published to our knowledge. In practice, we consider it reasonable to
assume that BLAKE2b-512 has the properties needed for Hrcm
to be collapsing. Taking the 512-bit BLAKE2b output modulo
rP≈2254 cannot introduce a problem.
For each Hrcm oracle query the adversary chooses
notetuple=(rseed,noterepr)
and obtains a "random"
rcm=Hrseedrcm(noterepr).
We have cmx=ExtractP([Hrseedrcm(noterepr)+f(notetuple)]R).
Over all Pallas curve points P, the number of outputs of
ExtractP(P) is (FrP+1)/2≈2253 —
one for each possible x-coordinate of Pallas curve points, plus one for the
zero point OP which is mapped to 0.
Only one point maps to 0, as opposed to two points mapping to every
other possible output of ExtractP, but that has
negligible effect.
There are two values of cm that match cmx in their x-coordinate.
Because the output of NoteCommitrcm for notetuple is
of the form cm=[f(notetuple)+rcm]R,
for any given note we have exactly two values of rcm that will pass
the commitment check.
Suppose there are N legitimate notes in the tree. The adversary is trying
to find a note that will pass the commitment check without actually being a
note in the tree. As argued above, the distribution of rcm is
indistinguishable from uniform-random on FrP. The
success probability for each attempt in a classical search is therefore
2N/rP where rP is the order of the Pallas curve,
and that is negligible because N≤232≪rP.
We're not finished yet because we also have to prove that the nullifier is
computed deterministically for a given note.
All of the inputs to DeriveNullifier are things we committed to
in the protocol so far except nk. By the same argument used
pre-quantumly, there is only one ivk for a given
(gd,pkd). So in order to just use the existing
protocol for this part, we would need to prove that there is only one
nk (that is feasible to find) such that
Commitrivkivk(ak,nk)=ivk.
Unfortunately that's not true; Commitivk is instantiated by
SinsemillaShortCommit which is not post-quantum binding.
There are two cases depending on which witness branch the adversary
uses (they can choose to attack either, or both simultaneously):