Report firmware version in Zcash batch results
What changed, and why it matters
This commit adds the firmware version number to Zcash batch signing responses. It is a feature/documentation change, not a security fix or vulnerability. The version was already included in single-transaction responses; now it is also included in batch responses so wallets can identify which firmware version produced the signatures.
No security action required. Treat as a normal feature commit. Verify downstream wallets handle the new required CBOR key gracefully if they parse batch results.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change extends the zcash-batch-sig-result UR/CBOR envelope with a required key 3 containing the three-byte firmware version [major, minor, build]. It updates the ur-registry dependency to a newer revision that supports this field, modifies encode_zcash_batch_sig_result to pass KEYSTONE_FW_VERSION.encode(), and updates docs, changelog, and a unit test accordingly. No cryptographic, parsing, or access-control logic is altered.
Changed components
Zcash batch signing response encoding (rust/rust_c/src/zcash/mod.rs)ur-registry dependency (rust/Cargo.toml, rust/Cargo.lock)Zcash version reporting module (rust/apps/zcash/src/version.rs)Zcash protocol documentation (docs/protocols/ur_registrys/zcash.md)Inspect captured patch +29 / −18
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3654bbb..6a8fe13 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,8 @@
### What's new
-1. Added support for Zcash batch PCZT signing
+1. Added support for Zcash batch PCZT signing with compact signature results
+ that report the signing firmware version
### Bug Fixes
diff --git a/docs/protocols/ur_registrys/zcash.md b/docs/protocols/ur_registrys/zcash.md
index 6a2f71e..52bf878 100644
--- a/docs/protocols/ur_registrys/zcash.md
+++ b/docs/protocols/ur_registrys/zcash.md
@@ -59,7 +59,8 @@ zcash-pczt {
The outer UR registry envelope carries a request id for response correlation and
an opaque `data` field containing the PCZT-owned batch request. The matching
compact response uses `zcash-batch-sig-result`, echoes the request id, and
-carries the PCZT-owned response in its own opaque `data` field.
+carries the PCZT-owned response in its own opaque `data` field. It also reports
+the signing firmware version once for the entire response.
Batch version 1 is supported by cypherpunk firmware and currently accepts up to
50 PCZTs. The encoded batch data and request id together, and the canonical PCZT
@@ -72,10 +73,11 @@ are rejected.
#### Outer UR/CBOR envelopes
-Both registry types use definite-length CBOR maps with the same integer keys.
-Firmware requires `request-id` to be non-empty. Key `1` follows `zcash-pczt` by
-carrying opaque transaction data, and key `2` follows `zcash-sign-result` by
-carrying the request id.
+Both registry types use definite-length CBOR maps. Firmware requires
+`request-id` to be non-empty. Key `1` follows `zcash-pczt` by carrying opaque
+transaction data, and key `2` follows `zcash-sign-result` by carrying the
+request id. The result's required key `3` is the three-byte firmware version
+`[major, minor, build]` that produced the signatures.
```cddl
zcash-sign-batch = {
@@ -86,6 +88,7 @@ zcash-sign-batch = {
zcash-batch-sig-result = {
1: bytes, ; BatchSignResponse::serialize output
2: bytes, ; echoed request-id
+ 3: bytes, ; firmware version [major, minor, build]
}
```
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index e41bca9..59b6bf0 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -4814,7 +4814,7 @@ dependencies = [
[[package]]
name = "ur-registry"
version = "1.0.5"
-source = "git+https://github.com/KeystoneHQ/keystone-sdk-rust.git?rev=bf5b6e4b5c533e8934c4611837db2b6567ae837e#bf5b6e4b5c533e8934c4611837db2b6567ae837e"
+source = "git+https://github.com/KeystoneHQ/keystone-sdk-rust.git?rev=f6b688eb9b6eb928f400b4cd7d2d623fdbc3b34d#f6b688eb9b6eb928f400b4cd7d2d623fdbc3b34d"
dependencies = [
"bs58",
"hex",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 3886a33..86bb8b6 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -131,6 +131,6 @@ zcash_protocol = { git = "https://github.com/zcash/librustzcash", rev = "878db20
zcash_transparent = { git = "https://github.com/zcash/librustzcash", rev = "878db2074ae8ac2682d3e6c61c00f7018b6adc0c" }
# Keep `orchard` aligned with the pinned PCZT/librustzcash revision.
orchard = { git = "https://github.com/zcash/orchard", rev = "8995ee7e26f8b654a5457d05c95ee5b3132b3edd" }
-# Use the SDK rev that wraps PCZT-owned Postcard messages as opaque UR data and
-# correlates each request and result through the outer registry envelope.
-ur-registry = { git = "https://github.com/KeystoneHQ/keystone-sdk-rust.git", rev = "bf5b6e4b5c533e8934c4611837db2b6567ae837e" }
+# Use the SDK rev that wraps PCZT-owned Postcard messages as opaque UR data,
+# correlates requests and results, and reports one firmware version per batch result.
+ur-registry = { git = "https://github.com/KeystoneHQ/keystone-sdk-rust.git", rev = "f6b688eb9b6eb928f400b4cd7d2d623fdbc3b34d" }
diff --git a/rust/apps/zcash/src/version.rs b/rust/apps/zcash/src/version.rs
index 59479e1..7489b8d 100644
--- a/rust/apps/zcash/src/version.rs
+++ b/rust/apps/zcash/src/version.rs
@@ -1,9 +1,12 @@
-//! Firmware version stamped into every signed PCZT response via
-//! `global.proprietary["keystone:fw_version"]`.
+//! Firmware version reported by Zcash signing responses.
//!
-//! Wallets read this stamp to decide whether the device meets their
-//! minimum version requirements. The firmware itself does not enforce
-//! any minimum, it just reports its own version.
+//! Single-PCZT responses stamp it into
+//! `global.proprietary["keystone:fw_version"]`. Compact batch responses carry
+//! the same encoding once in the outer `zcash-batch-sig-result` envelope.
+//!
+//! Wallets read this stamp to identify the firmware that produced a signature.
+//! The firmware does not interpret it as a minimum requirement; it only reports
+//! its own version.
//!
//! Version encoding is three raw bytes `[major, minor, build]`, matching the
//! `SOFTWARE_VERSION_MAJOR / MINOR / BUILD` triple in `src/config/version.h`.
diff --git a/rust/rust_c/src/zcash/mod.rs b/rust/rust_c/src/zcash/mod.rs
index 3105909..5573d9d 100644
--- a/rust/rust_c/src/zcash/mod.rs
+++ b/rust/rust_c/src/zcash/mod.rs
@@ -14,6 +14,8 @@ use alloc::{boxed::Box, format, string::String, string::ToString, vec::Vec};
use app_zcash::get_address;
#[cfg(feature = "cypherpunk")]
use app_zcash::pczt::{sign::SpendAuthCache, structs::ParsedPczt};
+#[cfg(feature = "cypherpunk")]
+use app_zcash::version::KEYSTONE_FW_VERSION;
use core::slice;
use cryptoxide::hashing::sha256;
use cty::c_char;
@@ -350,13 +352,14 @@ fn encode_checked_zcash_batch(request_id: &[u8], data: Vec<u8>) -> Result<Vec<u8
})
}
-/// Wraps the PCZT crate's signature response with its echoed request id.
+/// Wraps the PCZT crate's signature response with its echoed request id and
+/// the firmware version that produced the signatures.
#[cfg(feature = "cypherpunk")]
fn encode_zcash_batch_sig_result(
request_id: Vec<u8>,
data: Vec<u8>,
) -> Result<Vec<u8>, RustCError> {
- ZcashBatchSigResult::new(request_id, data)
+ ZcashBatchSigResult::new(request_id, data, KEYSTONE_FW_VERSION.encode().to_vec())
.try_into()
.map_err(|e| RustCError::InvalidData(format!("encode Zcash batch result envelope: {e:?}")))
}
@@ -1047,7 +1050,7 @@ mod tests {
#[cfg(feature = "cypherpunk")]
#[test]
- fn test_encode_zcash_batch_sig_result_wraps_pczt_response() {
+ fn test_encode_zcash_batch_sig_result_wraps_response_with_firmware_version() {
use zcash_vendor::{orchard::ValuePool, pczt::roles::signer::SpendAuthSignature};
let request_id = vec![0xaa, 0xbb];
@@ -1071,6 +1074,7 @@ mod tests {
assert_eq!(decoded.get_request_id(), request_id);
assert_eq!(decoded.get_data(), response_bytes);
+ assert_eq!(decoded.get_firmware_version(), KEYSTONE_FW_VERSION.encode());
assert_eq!(
BatchSignResponse::parse(decoded.get_data()).unwrap(),
response
Why this scored 18/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.