chore(zcash): bump orchard to 0.15.0-pre.1, repoint librustzcash
What changed, and why it matters
This commit updates the Zcash cryptography libraries inside the Keystone hardware wallet firmware. The main functional change is a bug fix: when the wallet tries to read encrypted Zcash note details, it now correctly chooses the decryption method for the newer 'Ironwood' (V3) notes instead of always using the older 'Orchard' (V2) method. Before this change, V3 notes could not be decrypted, which could break or mislead transactions involving the newer Zcash shielded pool. The rest of the change is dependency version bumps and test fixture adjustments.
Treat this as a routine but correctness-critical dependency update. Verify that the updated orchard and librustzcash revisions match the intended upstream Zcash protocol state, run the full Zcash test suite, and consider whether any user-facing transaction flows previously failed silently due to the V3 decryption bug. No emergency action is indicated absent further evidence.
Security signals we found
Incorrect note-encryption domain selection could prevent decryption of Ironwood (V3) shielded outputs
Dependency bump to orchard 0.15.0-pre.1 introduces BundleVersion API and splits V2/V3 note encryption domains
Test fixtures corrected to avoid prohibited orchard_v3 coinbase bundles for V2 Orchard notes
No explicit CVE, advisory, or vendor security disclosure present in commit materials
Evidence from the diff
The commit bumps orchard from a git-pinned 0.14 to the published 0.15.0-pre.1 and repoints the valargroup/librustzcash patches to the ironwood-integration branch (644b1ecf). The key code change is in rust/apps/zcash/src/pczt/parse.rs: decode_output_enc_ciphertext now inspects action.output().note_version() and selects IronwoodDomain for V3 notes and OrchardDomain for V2 notes. Previously it hardcoded OrchardDomain, which could not decrypt V3/Ironwood actions. Test fixtures in pczt/mod.rs were updated to use the new BundleVersion/BundleType API and to mint V2 Orchard source notes via orchard_v2 coinbase rather than orchard_v3 coinbase, which 0.15.0-pre.1 now forbids.
Changed components
rust/apps/zcash/src/pczt/parse.rsrust/apps/zcash/src/pczt/mod.rsrust/Cargo.tomlrust/Cargo.lockrust/zcash_vendor/Cargo.tomlInspect captured patch +87 / −45
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index ef98ac9..a3e5329 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1676,7 +1676,7 @@ checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]]
name = "equihash"
version = "0.3.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"blake2b_simd",
"corez",
@@ -1803,7 +1803,7 @@ dependencies = [
[[package]]
name = "f4jumble"
version = "0.1.1"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"blake2b_simd",
]
@@ -2563,7 +2563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
- "windows-targets 0.48.5",
+ "windows-targets 0.53.2",
]
[[package]]
@@ -3039,8 +3039,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "orchard"
-version = "0.14.0"
-source = "git+https://github.com/zcash/orchard?rev=b2af0a11abe00f59c51258d349c2105fe7a16215#b2af0a11abe00f59c51258d349c2105fe7a16215"
+version = "0.15.0-pre.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8e277dd4b46f5d06deae3ffb8af1a951e8622368f028c2a4d6fe59339566403"
dependencies = [
"aes",
"bitvec",
@@ -3141,7 +3142,7 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pczt"
version = "0.7.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"blake2b_simd",
"bls12_381",
@@ -5409,12 +5410,12 @@ dependencies = [
[[package]]
name = "zcash_address"
version = "0.12.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"bech32 0.11.0",
"bs58",
"corez",
- "f4jumble 0.1.1 (git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed)",
+ "f4jumble 0.1.1 (git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e)",
"zcash_encoding",
"zcash_protocol",
]
@@ -5422,7 +5423,7 @@ dependencies = [
[[package]]
name = "zcash_encoding"
version = "0.4.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"corez",
"hex",
@@ -5432,7 +5433,7 @@ dependencies = [
[[package]]
name = "zcash_keys"
version = "0.14.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"bech32 0.11.0",
"bip32",
@@ -5471,7 +5472,7 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.28.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"blake2b_simd",
"block-buffer 0.11.0-rc.3",
@@ -5501,7 +5502,7 @@ dependencies = [
[[package]]
name = "zcash_protocol"
version = "0.9.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"corez",
"hex",
@@ -5540,7 +5541,7 @@ dependencies = [
[[package]]
name = "zcash_transparent"
version = "0.8.0"
-source = "git+https://github.com/valargroup/librustzcash?rev=dc95dcef33a081b925db551eac8bf6533fff22ed#dc95dcef33a081b925db551eac8bf6533fff22ed"
+source = "git+https://github.com/valargroup/librustzcash?rev=644b1ecf4679e17a9166782f57a795a4f4194d5e#644b1ecf4679e17a9166782f57a795a4f4194d5e"
dependencies = [
"bip32",
"bs58",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index f0f533a..f351571 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -122,13 +122,12 @@ zeroize = { version = "1.8.2", default-features = false }
# third party dependencies end
[patch.crates-io]
-orchard = { git = "https://github.com/zcash/orchard", rev = "b2af0a11abe00f59c51258d349c2105fe7a16215" }
-pczt = { git = "https://github.com/valargroup/librustzcash", rev = "dc95dcef33a081b925db551eac8bf6533fff22ed" }
-zcash_address = { git = "https://github.com/valargroup/librustzcash", rev = "dc95dcef33a081b925db551eac8bf6533fff22ed" }
-zcash_encoding = { git = "https://github.com/valargroup/librustzcash", rev = "dc95dcef33a081b925db551eac8bf6533fff22ed" }
-zcash_keys = { git = "https://github.com/valargroup/librustzcash", rev = "dc95dcef33a081b925db551eac8bf6533fff22ed" }
-zcash_primitives = { git = "https://github.com/valargroup/librustzcash", rev = "dc95dcef33a081b925db551eac8bf6533fff22ed" }
-zcash_protocol = { git = "https://github.com/valargroup/librustzcash", rev = "dc95dcef33a081b925db551eac8bf6533fff22ed" }
-zcash_transparent = { git = "https://github.com/valargroup/librustzcash", rev = "dc95dcef33a081b925db551eac8bf6533fff22ed" }
+pczt = { git = "https://github.com/valargroup/librustzcash", rev = "644b1ecf4679e17a9166782f57a795a4f4194d5e" }
+zcash_address = { git = "https://github.com/valargroup/librustzcash", rev = "644b1ecf4679e17a9166782f57a795a4f4194d5e" }
+zcash_encoding = { git = "https://github.com/valargroup/librustzcash", rev = "644b1ecf4679e17a9166782f57a795a4f4194d5e" }
+zcash_keys = { git = "https://github.com/valargroup/librustzcash", rev = "644b1ecf4679e17a9166782f57a795a4f4194d5e" }
+zcash_primitives = { git = "https://github.com/valargroup/librustzcash", rev = "644b1ecf4679e17a9166782f57a795a4f4194d5e" }
+zcash_protocol = { git = "https://github.com/valargroup/librustzcash", rev = "644b1ecf4679e17a9166782f57a795a4f4194d5e" }
+zcash_transparent = { git = "https://github.com/valargroup/librustzcash", rev = "644b1ecf4679e17a9166782f57a795a4f4194d5e" }
# Use the upstream SDK rev with the Zcash batch registry types until they are published as a crate.
ur-registry = { git = "https://github.com/KeystoneHQ/keystone-sdk-rust.git", rev = "0884de4b2e927bc3f95a98dff62045e0d492e574" }
diff --git a/rust/apps/zcash/src/pczt/mod.rs b/rust/apps/zcash/src/pczt/mod.rs
index cda05a9..0587337 100644
--- a/rust/apps/zcash/src/pczt/mod.rs
+++ b/rust/apps/zcash/src/pczt/mod.rs
@@ -347,10 +347,12 @@ pub(crate) mod test_support {
let value = orchard::value::NoteValue::from_raw(1_000_000);
let note = {
let mut orchard_builder = orchard::builder::Builder::new(
- orchard::BundleProtocol::IronwoodPostNu6_3,
orchard::builder::BundleType::DEFAULT,
+ orchard::bundle::BundleVersion::ironwood_v3(),
+ orchard::bundle::BundleVersion::ironwood_v3().default_flags(),
orchard::Anchor::empty_tree(),
- );
+ )
+ .expect("default flags are representable under the bundle version");
orchard_builder
.add_output(None, recipient, value, Memo::Empty.encode().into_bytes())
.unwrap();
@@ -359,7 +361,7 @@ pub(crate) mod test_support {
.actions()
.get(meta.output_action_index(0).unwrap())
.unwrap();
- let domain = orchard::note_encryption::OrchardDomain::for_action(action);
+ let domain = orchard::note_encryption::IronwoodDomain::for_action(action);
let (note, _, _) =
try_note_decryption(&domain, &orchard_ivk.prepare(), action).unwrap();
note
@@ -457,10 +459,12 @@ pub(crate) mod test_support {
let value = orchard::value::NoteValue::from_raw(1_010_000);
let note = {
let mut orchard_builder = orchard::builder::Builder::new(
- orchard::BundleProtocol::OrchardPostNu6_3,
orchard::builder::BundleType::Coinbase,
+ orchard::bundle::BundleVersion::orchard_v2(),
+ orchard::bundle::Flags::SPENDS_DISABLED,
orchard::Anchor::empty_tree(),
- );
+ )
+ .expect("spends-disabled flags are valid for a coinbase bundle");
orchard_builder
.add_output(None, recipient, value, Memo::Empty.encode().into_bytes())
.unwrap();
@@ -563,10 +567,12 @@ pub(crate) mod test_support {
let value = orchard::value::NoteValue::from_raw(1_000_000);
let note = {
let mut orchard_builder = orchard::builder::Builder::new(
- orchard::BundleProtocol::OrchardPostNu6_3,
orchard::builder::BundleType::Coinbase,
+ orchard::bundle::BundleVersion::orchard_v2(),
+ orchard::bundle::Flags::SPENDS_DISABLED,
orchard::Anchor::empty_tree(),
- );
+ )
+ .expect("spends-disabled flags are valid for a coinbase bundle");
orchard_builder
.add_output(None, recipient, value, Memo::Empty.encode().into_bytes())
.unwrap();
@@ -599,10 +605,12 @@ pub(crate) mod test_support {
};
let mut builder = orchard::builder::Builder::new(
- orchard::BundleProtocol::OrchardPostNu6_3,
orchard::builder::BundleType::DEFAULT,
+ orchard::bundle::BundleVersion::orchard_v3(),
+ orchard::bundle::BundleVersion::orchard_v3().default_flags(),
anchor,
- );
+ )
+ .expect("default flags are representable under the bundle version");
builder
.add_spend(orchard_fvk.clone(), note, merkle_path)
.unwrap();
diff --git a/rust/apps/zcash/src/pczt/parse.rs b/rust/apps/zcash/src/pczt/parse.rs
index 4a018cd..f0b6d81 100644
--- a/rust/apps/zcash/src/pczt/parse.rs
+++ b/rust/apps/zcash/src/pczt/parse.rs
@@ -21,7 +21,11 @@ use zcash_vendor::{
use zcash_note_encryption::Domain;
#[cfg(feature = "cypherpunk")]
use zcash_vendor::orchard::{
- self, keys::OutgoingViewingKey, note::Note, note_encryption::OrchardDomain, Address,
+ self,
+ keys::OutgoingViewingKey,
+ note::Note,
+ note_encryption::{IronwoodDomain, OrchardDomain},
+ Address,
};
#[cfg(feature = "cypherpunk")]
use zcash_vendor::{
@@ -99,16 +103,31 @@ pub fn decode_output_enc_ciphertext(
action: &orchard::pczt::Action,
ovk: Option<&OutgoingViewingKey>,
) -> Result<Option<(Note, Address, [u8; 512])>, ZcashError> {
- let domain = OrchardDomain::for_pczt_action(action);
+ // orchard 0.15.0-pre.1 splits note encryption by version: Ironwood actions carry V3
+ // note plaintexts and must be trial-decrypted with `IronwoodDomain`, while Orchard
+ // actions use the V2 `OrchardDomain`. Select the domain from the action's note version
+ // so both pools decrypt correctly.
+ let is_ironwood = matches!(*action.output().note_version(), orchard::NoteVersion::V3);
if let Some(ovk) = ovk {
- Ok(try_output_recovery_with_ovk(
- &domain,
- ovk,
- action,
- action.cv_net(),
- &action.output().encrypted_note().out_ciphertext,
- ))
+ let out_ciphertext = &action.output().encrypted_note().out_ciphertext;
+ Ok(if is_ironwood {
+ try_output_recovery_with_ovk(
+ &IronwoodDomain::for_pczt_action(action),
+ ovk,
+ action,
+ action.cv_net(),
+ out_ciphertext,
+ )
+ } else {
+ try_output_recovery_with_ovk(
+ &OrchardDomain::for_pczt_action(action),
+ ovk,
+ action,
+ action.cv_net(),
+ out_ciphertext,
+ )
+ })
} else {
// If we reached here, none of our OVKs matched; recover directly as the fallback.
@@ -132,15 +151,30 @@ pub fn decode_output_enc_ciphertext(
value,
rho,
rseed,
- (*action.output().note_version()).into(),
+ *action.output().note_version(),
)
.into_option()
.ok_or_else(|| ZcashError::InvalidPczt("Orchard action contains invalid note".into()))?;
- let pk_d = OrchardDomain::get_pk_d(¬e);
- let esk = OrchardDomain::derive_esk(¬e).expect("Orchard notes are post-ZIP 212");
-
- Ok(try_output_recovery_with_pkd_esk(&domain, pk_d, esk, action))
+ Ok(if is_ironwood {
+ let pk_d = IronwoodDomain::get_pk_d(¬e);
+ let esk = IronwoodDomain::derive_esk(¬e).expect("Orchard notes are post-ZIP 212");
+ try_output_recovery_with_pkd_esk(
+ &IronwoodDomain::for_pczt_action(action),
+ pk_d,
+ esk,
+ action,
+ )
+ } else {
+ let pk_d = OrchardDomain::get_pk_d(¬e);
+ let esk = OrchardDomain::derive_esk(¬e).expect("Orchard notes are post-ZIP 212");
+ try_output_recovery_with_pkd_esk(
+ &OrchardDomain::for_pczt_action(action),
+ pk_d,
+ esk,
+ action,
+ )
+ })
}
}
diff --git a/rust/zcash_vendor/Cargo.toml b/rust/zcash_vendor/Cargo.toml
index 65bf26b..315ee8a 100644
--- a/rust/zcash_vendor/Cargo.toml
+++ b/rust/zcash_vendor/Cargo.toml
@@ -41,7 +41,7 @@ chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
] }
postcard = { version = "1.0.3", features = ["alloc"] }
getset = { version = "0.1.3" }
-orchard = { version = "0.14", default-features = false, optional = true }
+orchard = { version = "0.15.0-pre.1", default-features = false, optional = true }
pczt = { version = "0.7", default-features = false }
serde = { workspace = true }
serde_with = { version = "3.11.0", features = [
Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.