Skip to content
ZIPs.tools

ZIP 216

Require Canonical Jubjub Point Encodings

  • Final
Proposal metadata
ID
216
Status
Final
Category
Consensus
Owners
  • Jack Grigg
  • Daira-Emma Hopwood
Created
2021-02-11
License
MIT
Contents
  1. Terminology
  2. Abstract
  3. Motivation
  4. Specification
  5. Retroactive applicability
  6. Rationale
  7. Security and Privacy Considerations
  8. Deployment
  9. References
Preamble
ZIP
216
Title
Require Canonical Jubjub Point Encodings
Status
Final
Category
Consensus
Created
2021-02-11
License
MIT

Terminology

The key word "MUST" in this document is to be interpreted as described in BCP 141 when, and only when, they appear in all capitals.

The term "network upgrade" in this document is to be interpreted as described in ZIP 200.2

The terms "settled" and "full validator" in this document are to be interpreted as described in3.

The terms "Mainnet" and "Testnet" in this document are to be interpreted as described in4.

Abstract

This ZIP fixes an oversight in the implementation of the Sapling consensus rules, by rejecting all non‑canonical representations of Jubjub points.

Motivation

The Sapling specification was originally written with the intent that all values, including Jubjub points, are strongly typed with canonical representations.5 This has significant advantages for security analysis, because it allows the protocol to be modelled with just the abstract types.

The intention of the Jubjub implementation (both in the jubjub crate6 and its prior implementations) was to ensure that only canonical point encodings would be accepted by the decoding logic. However, an oversight in the implementation allowed an edge case to slip through: for each point on the curve where the uu-coordinate is zero, there are two encodings that will be accepted:

// Fix the sign of `u` if necessary
let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1);
let u_negated = -u;
let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);

This code accepts either sign bit, because u_negated == u.

There are two points on the Jubjub curve with uu-coordinate zero:

  • (0,1)(0, 1), which is the identity;
  • (0,−1)(0, -1), which is a point of order two.

Each of these has a single non‑canonical encoding in which the value of the sign bit is 11.

This creates a consensus issue because (unlike other non‑canonical point encodings that are rejected) either of the above encodings can be decoded, and then re-encoded to a different encoding. For example, if a non‑canonical encoding appeared in a transaction field, then node implementations that store points internally as abstract curve points, and used those to derive transaction IDs, would derive different IDs than nodes which store transactions as bytes (such as zcashd).

This issue is not known to cause any security vulnerability, beyond the risk of consensus incompatibility. In fact, for some of the fields that would otherwise be affected, the issue does not occur because there are already consensus rules that prohibit small‑order points, and this incidentally prohibits non‑canonical encodings.

Adjustments to the protocol specification were made in versions 2020.1.8, 2020.1.9, 2020.1.15, 2021.1.17, and 2023.4.0 to match the zcashd implementation. Further changes were also made in version 2025.6.0. The fact that this required six specification revisions to get right, conclusively demonstrates the problem.

Specification

Let abstJ\mathsf{abst}_{\mathbb{J}}, reprJ\mathsf{repr}_{\mathbb{J}}, and qJq_{\mathbb{J}} be as defined in7.

Define a non‑canonical compressed encoding of a Jubjub point to be a sequence of 256256 bits, bb, such that abstJ(b)≠⊥\mathsf{abst}_{\mathbb{J}}(b) \neq \bot and reprJ(abstJ(b))≠b\mathsf{repr_{\mathbb{J}}}\big(\mathsf{abst}_{\mathbb{J}}(b)\big) \neq b.

Non‑normative note: There are two such bit sequences, I2LEOSPℓJ(2255+1)\mathsf{I2LEOSP}_{\ell_{\mathbb{J}}}(2^{255} + 1) and I2LEOSPℓJ(2255+qJ−1)\mathsf{I2LEOSP}_{\ell_{\mathbb{J}}}(2^{255} + q_{\mathbb{J}} - 1). The Sapling protocol uses little‑endian ordering when converting between bit and byte sequences, so the first of these sequences corresponds to a 0x01\mathtt{0x01} byte, followed by 3030 zero bytes, and then a 0x80\mathtt{0x80} byte.

Once this ZIP activates, the following places within the Sapling consensus protocol where Jubjub points occur MUST reject non‑canonical Jubjub point encodings.

In Sapling Spend descriptions8:

  • the R‾\underline{R} component (i.e. the first 3232 bytes) of the spendAuthSig\mathtt{spendAuthSig} RedDSA signature.

In transactions9:

  • the R‾\underline{R} component (i.e. the first 3232 bytes) of the bindingSigSapling\mathtt{bindingSigSapling} RedDSA signature.

In the plaintext obtained by decrypting the Cout\mathsf{C^{out}} field of a Sapling transmitted note ciphertext (encrypted in10 and decrypted in11):

  • pk⋆d\mathsf{pk\star_d}.

pk⋆d\mathsf{pk\star_d} also MUST be considered invalid if it encodes the zero point OJ\mathcal{O}_{\mathbb{J}}.

These requirements on pk⋆d\mathsf{pk\star_d} affect the decryption of Cout\mathsf{C^{out}} in all cases, but are consensus-critical only in the case of decrypting Cout\mathsf{C^{out}} for a Sapling shielded coinbase output. 1213

Note: When a Sapling transmitted note ciphertext is decrypted using an incoming viewing key14, pkd\mathsf{pk_d} is computed as KASapling.DerivePublic(ivk,gd)=\mathsf{KA^{Sapling}.DerivePublic}(\mathsf{ivk}, \mathsf{g_d}) = [ivk] gd[\mathsf{ivk}]\,\mathsf{g_d}. Since gd\mathsf{g_d} is an output of DiversifyHashSapling  ⦂  B[ℓd]→J(r)∗∪{⊥}\mathsf{DiversifyHash^{Sapling}} \;{\small ⦂}\; \mathbb{B}^{[\ell_d]} \rightarrow \mathbb{J}^{(r)*} \cup \{\bot\}, it cannot be OJ\mathcal{O}_{\mathbb{J}}. ivk\mathsf{ivk} cannot be 00 for a validly constructed Sapling incoming viewing key15, and so the computed value of pkd\mathsf{pk_d} cannot be OJ\mathcal{O}_{\mathbb{J}}. An implementation MAY redundantly enforce that the computed value of pkd\mathsf{pk_d} is not OJ\mathcal{O}_{\mathbb{J}}.

There are some additional fields in the consensus protocol that encode Jubjub points, but where non‑canonical encodings MUST already be rejected as a side effect of existing consensus rules.

In Sapling Spend descriptions:

  • cv\mathtt{cv}
  • rk\mathtt{rk}.

In Sapling Output descriptions16:

  • cv\mathtt{cv}
  • ephemeralKey\mathtt{ephemeralKey}.

These fields cannot by consensus contain small‑order points. All of the points with non‑canonical encodings are small‑order.

Implementations MAY choose to reject non‑canonical encodings of the above four fields early in decoding of a transaction. This eliminates the risk that parts of the transaction could be re-serialized from their internal representation to a different byte sequence than in the original transaction, e.g. when calculating transaction IDs.

In addition, Sapling addresses and full viewing keys MUST be considered invalid when imported if they contain non‑canonical Jubjub point encodings, or encodings of points that are not in the prime‑order subgroup J(r)\mathbb{J}^{(r)}. These requirements MAY be enforced in advance of NU5 activation. This affects the fields listed below.

In Sapling payment addresses17:

  • the encoding of pkd\mathsf{pk_d}.

In Sapling full viewing keys18 and extended full viewing keys19:

  • the encoding of ak\mathsf{ak}
  • the encoding of nk\mathsf{nk}.

pkd\mathsf{pk_d} and ak\mathsf{ak} also MUST be considered invalid if they encode the zero point OJ\mathcal{O}_{\mathbb{J}}.

The fields mentioned in this Specification are intended to be a complete list of the places where compressed encodings of Jubjub points occur in the Zcash consensus protocol and in plaintext, address, or key formats.

Note: Some versions of the Zcash protocol specification mistakenly allowed pkd\mathsf{pk_d} in a Sapling payment address, or pk⋆d\mathsf{pk\star_d} in a decrypted Cout\mathsf{C^{out}}, to encode the zero point. The history is explained in a note added in version 2023.4.0 of the protocol specification20:

A previous version of this specification did not have the requirement for the decoded point pkd\mathsf{pk_d} of a Sapling note to be in the set of prime‑order points J(r)∗\mathbb{J}^{(r)*} (i.e. "if ... pkd∉J(r)∗\mathsf{pk_d} \not\in \mathbb{J}^{(r)*}, return ⊥ ⁣\bot\!"). That did not match the implementation in zcashd. In fact the history is a little more complicated. The current specification matches the implementation in librustzcash as of 21, which has been used in zcashd since v2.1.2. However, there was another implementation of Sapling note decryption used in zcashd for consensus checks, specifically the check that a shielded coinbase output decrypts successfully with the zero ovk\mathsf{ovk}. This was corrected to enforce the same restriction on the decrypted pkd\mathsf{pk_d} in zcashd v5.5.0, originally set to activate in a soft fork at block height 2121200 on both Mainnet and Testnet22. (On Testnet this height was in the past as of the zcashd v5.5.0 release, and so the change would have been immediately enforced on upgrade.) Since the soft fork was observed to be retrospectively valid after that height, the implementation was simplified in23 to use the librustzcash implementation in all cases, which reflects the specification above. zebra always used the librustzcash implementation.

The protocol specification was further updated in version 2025.6.024, to tighten the type of ivk\mathsf{ivk} in Sapling to {1 .. 2ℓivkSapling ⁣−1}\{ 1\,..\, 2^{\ell^{\mathsf{Sapling}}_{\mathsf{ivk}}}\!-1 \} and the type of pkd\mathsf{pk_d} in both Sapling and Orchard to KAprotocol.PublicPrimeOrder\mathsf{KA^{protocol}.PublicPrimeOrder}, in order to make the exclusion of the zero point more obvious25. This also has the effect that a Sapling incoming viewing key26 or a Sapling IVK Encoding in a Unified Incoming Viewing Key27 that encodes the zero ivk\mathsf{ivk} MUST be considered invalid when imported. Another note was also added explicitly covering the encoding of pkd\mathsf{pk_d} in Sapling payment addresses 28:

The restriction on pkd\mathsf{pk_d} reflects its current type KASapling.PublicPrimeOrder=J(r)∗\mathsf{KA^{Sapling}.PublicPrimeOrder} = \mathbb{J}^{(r)*}. In versions of this specification prior to 2025.6.0, pkd\mathsf{pk_d} had type KASapling.PublicPrimeSubgroup=J(r)\mathsf{KA^{Sapling}.PublicPrimeSubgroup} = \mathbb{J}^{(r)}, i.e. including OJ\mathcal{O}_{\mathbb{J}}. Implementations of consumers for this encoding may need to be updated to exclude OJ\mathcal{O}_{\mathbb{J}}, and should be checked for consistency with the current version of [ZIP-216].

Retroactive applicability

As originally specified, this ZIP required that the new validity rules be applied only after NU5 activation. This was necessary because a transaction containing a non‑canonical Jubjub point encoding could have been included in any block before NU5 activation.

However, now that NU5 is a settled upgrade on the Zcash Mainnet and Testnet chains 2930, it can be observed that there were no such non‑canonical encodings in publically visible transaction fields before the Mainnet and Testnet NU5 activations. Therefore, a full validator MAY enforce the above specification retroactively.

It remains possible that there could be non‑canonical pk⋆d\mathsf{pk\star_d} encodings in plaintexts obtained by decrypting the Cout\mathsf{C^{out}} field of a Sapling transmitted note ciphertext. Such encodings MUST be rejected by the decryption procedure in 31 as currently specified. In version 2025.6.0 of the protocol specification32 this procedure has been changed to reject them unconditionally, not only after NU5 activation. (This has no effect on consensus relative to the previous version, because only small‑order Jubjub curve points have non‑canonical encodings, and so the check that returns ⊥\bot if pkd∉J(r)∗\mathsf{pk_d} ∉ \mathbb{J}^{(r)*} would catch all such cases.)

Rejecting non‑canonical pk⋆d\mathsf{pk\star_d} encodings cannot lead to loss of funds sent to a Sapling address that has been correctly generated as specified in 33, because such an address cannot have ivk=0\mathsf{ivk} = 0, which is the only case in which pk⋆d\mathsf{pk\star_d} could be non‑canonical. They are rejected in wallet rescanning by current zcashd and by librustzcash-based light wallets.

Note: There are no such non‑canonical pk⋆d\mathsf{pk\star_d} encodings in the Cout\mathsf{C^{out}} components of shielded coinbase outputs (which are required by consensus to be decryptable by an all-zero ovk\mathsf{ovk}34).

Rationale

Zcash previously had a similar issue with non‑canonical representations of points in Ed25519 public keys and signatures. In that case, given the prevalence of Ed25519 signatures in the wider ecosystem, the decision was made in ZIP 21535 (which activated with the Canopy network upgrade36) to allow non‑canonical representations of points.

In Sapling, we are motivated instead to reject these non‑canonical points:

  • The chance of the identity occurring anywhere within the Sapling components of transactions from implementations following the standard protocol is cryptographically negligible.
  • This re-enables the aforementioned simpler security analysis of the Sapling protocol.
  • The Jubjub curve has a vastly smaller scope of usage in the general cryptographic ecosystem than Curve25519 and Ed25519.

The necessary checks are very simple and do not require cryptographic operations, therefore the performance impact will be negligible.

The public inputs of Jubjub points to the Spend circuit ( ⁣rk\!\mathsf{rk} and cvold\mathsf{cv^{old}}) and Output circuit ( ⁣cvnew\!\mathsf{cv^{new}} and epk\mathsf{epk}) are not affected because they are represented in affine coordinates as elements of the correct field ( ⁣FrS=FqJ\!\mathbb{F}_{r_\mathbb{S}} = \mathbb{F}_{q_\mathbb{J}}), and so no issue of encoding canonicity arises.

Encodings of elliptic curve points on Curve25519, BN‑254 G1\mathbb{G}_1, BN‑254 G2\mathbb{G}_2, BLS12‑381 G1\mathbb{G}_1, and BLS12‑381 G2\mathbb{G}_2 are not affected.

Encodings of elliptic curve points on the Pallas and Vesta curves37 used by the Orchard shielded protocol are also not affected.

Security and Privacy Considerations

This ZIP eliminates a potential source of consensus divergence between differing full node implementations. From February 2023 onward, no known divergence of this type exists for any production implementation of Zcash, but an early alpha version of the zebrad node implementation would have been susceptible to this issue.

Deployment

This ZIP activated with Network Upgrade 5. Requirements on points encoded in payment addresses and full viewing keys MAY be enforced in advance of NU5 activation.

zcashd PRs #600038, #639939, #645940, and #672541 retroactively enforce canonical encoding of Jubjub points for the entire chain history, as described in the Retroactive applicability section.

References


  1. Information on BCP 14 — "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels" and "RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words"↩︎

  2. ZIP 200: Network Upgrade Mechanism↩︎

  3. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 3.3: The Block Chain↩︎

  4. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 3.12: Mainnet and Testnet↩︎

  5. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.9.3: Jubjub↩︎

  6. jubjub Rust crate↩︎

  7. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.9.3: Jubjub↩︎

  8. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.4: Spend Descriptions↩︎

  9. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 7.1: Transaction Encoding and Consensus↩︎

  10. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.1: Encryption (Sapling and Orchard)↩︎

  11. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.3: Decryption using an Outgoing Viewing Key (Sapling and Orchard)↩︎

  12. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 7.1.2: Transaction Consensus Rules↩︎

  13. ZIP 213: Shielded Coinbase↩︎

  14. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.2: Decryption using an Incoming Viewing Key (Sapling and Orchard)↩︎

  15. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.2.2: Sapling Key Components↩︎

  16. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.5: Output Descriptions↩︎

  17. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.6.3.1: Sapling Payment Addresses↩︎

  18. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.6.3.3: Sapling Full Viewing Keys↩︎

  19. ZIP 32: Shielded Hierarchical Deterministic Wallets. Sapling extended full viewing keys↩︎

  20. Zcash Protocol Specification, Version 2023.4.0. Section 10: Change History — 2023.4.0↩︎

  21. zcash/librustzcash PR 109: PaymentAddress encapsulation↩︎

  22. zcash/zcash PR 6459: Migrate to zcash_primitives 0.10↩︎

  23. zcash/zcash PR 6725: Retroactively use Rust to decrypt shielded coinbase before soft fork↩︎

  24. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 10: Change History — 2025.6.0↩︎

  25. zcash/zips issue 664: Sapling pk_d should not allow the zero point↩︎

  26. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.6.3.2: Sapling Incoming Viewing Keys↩︎

  27. ZIP 316: Unified Addresses and Unified Viewing Keys↩︎

  28. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.6.3.1: Sapling Payment Addresses↩︎

  29. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 3.3: The Block Chain↩︎

  30. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 3.12: Mainnet and Testnet↩︎

  31. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.20.3: Decryption using an Outgoing Viewing Key (Sapling and Orchard)↩︎

  32. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 10: Change History — 2025.6.0↩︎

  33. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 4.2.2: Sapling Key Components↩︎

  34. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 7.1.2: Transaction Consensus Rules↩︎

  35. ZIP 215: Explicitly Defining and Modifying Ed25519 Validation Rules↩︎

  36. ZIP 251: Deployment of the Canopy Network Upgrade↩︎

  37. Zcash Protocol Specification, Version 2025.6.1 [NU6.1]. Section 5.4.9.6: Pallas and Vesta↩︎

  38. zcash/zcash PR 6000: Enable ZIP 216 for blocks prior to NU5 activation↩︎

  39. zcash/zcash PR 6399: Retroactively enable ZIP 216 before NU5 activation↩︎

  40. zcash/zcash PR 6459: Migrate to zcash_primitives 0.10↩︎

  41. zcash/zcash PR 6725: Retroactively use Rust to decrypt shielded coinbase before soft fork↩︎

Citation graph

Hover or tap a node to see its title and citations.
Loading citation graph…