fix(ton): update bitstream io usage
What changed, and why it matters
This commit updates the TON (The Open Network) cell serialization code in the Keystone 3 firmware to use a newer version of the bitstream-io library. The changes are mostly mechanical renames of method calls (e.g., write/read becomes write_var/read_var) to match the new library's API. There is no explicit security fix described in the commit, and the diff alone does not show a patched vulnerability. It appears to be a dependency maintenance change.
Treat as routine maintenance. Review the upstream bitstream-io 4.10.0 changelog for any security fixes that may have motivated the upgrade, and verify that the no_std_io2 alias preserves intended behavior. Run TON transaction parsing/serialization tests to ensure no regressions in cell encoding.
Security signals we found
Dependency upgrade of bitstream-io to upstream 4.10.0
Replacement of core2 with no_std_io2
Mechanical API migration in TON cell serialization code
No explicit security mention or CVE reference in commit message
Evidence from the diff
The commit upgrades bitstream-io from a KeystoneHQ fork (tag no_std@0.1.2) to the upstream crates.io version 4.10.0, and replaces the core2 crate with no_std_io2 (aliased as core2). All call sites in the TON cell builder, parser, raw BOC serializer, and utilities are updated to use the new API methods: write_var/read_var for unsigned variable-width values and write_signed_var/read_signed_var for signed values. The change is API-adaptation rather than a logic change. No overflow, bounds-check, or cryptographic bug is directly visible in the diff.
Changed components
rust/apps/ton/src/vendor/cell/builder.rsrust/apps/ton/src/vendor/cell/parser.rsrust/apps/ton/src/vendor/cell/raw.rsrust/apps/ton/src/vendor/cell/util.rsrust/apps/ton/src/vendor/cell/mod.rsrust/apps/ton/Cargo.tomlrust/Cargo.tomlrust/Cargo.lockInspect captured patch +60 / −46
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 60824c1..e48761f 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -171,12 +171,12 @@ dependencies = [
"bech32 0.11.0",
"bitcoin",
"bytes",
- "core2",
"cryptoxide",
"either",
"hex",
"itertools 0.13.0",
"keystore",
+ "no_std_io2",
"rust_tools",
"serde",
"serde_json",
@@ -193,12 +193,12 @@ dependencies = [
"bech32 0.11.0",
"bitcoin",
"bitcoin_hashes 0.14.0",
- "core2",
"cryptoxide",
"either",
"hex",
"itertools 0.13.0",
"keystore",
+ "no_std_io2",
"rust_tools",
"serde",
"serde_json",
@@ -376,13 +376,13 @@ dependencies = [
"anyhow",
"base64 0.22.1",
"bitstream-io",
- "core2",
"crc",
"cryptoxide",
"hex",
"itertools 0.13.0",
"keystore",
"lazy_static",
+ "no_std_io2",
"num-bigint",
"num-integer",
"num-traits",
@@ -712,10 +712,11 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bitstream-io"
-version = "2.2.0"
-source = "git+https://github.com/KeystoneHQ/bitstream-io?tag=no_std%400.1.2#cc3159377ebd4e8f521aa9f3a9bc876e3b3336c1"
+version = "4.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f"
dependencies = [
- "core2",
+ "no_std_io2",
]
[[package]]
@@ -2800,6 +2801,15 @@ dependencies = [
"pin-utils",
]
+[[package]]
+name = "no_std_io2"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "nonempty"
version = "0.11.0"
@@ -3783,7 +3793,6 @@ dependencies = [
"cbc",
"cbindgen",
"cipher",
- "core2",
"cryptoxide",
"cstr_core",
"cty",
@@ -3794,6 +3803,7 @@ dependencies = [
"itertools 0.13.0",
"keystore",
"minicbor",
+ "no_std_io2",
"rand_core 0.6.4",
"rsa",
"rust_tools",
@@ -5483,7 +5493,6 @@ dependencies = [
"bs58",
"byteorder",
"chacha20poly1305",
- "core2",
"f4jumble",
"ff",
"fpe",
@@ -5491,6 +5500,7 @@ dependencies = [
"group",
"hex",
"incrementalmerkletree-testing",
+ "no_std_io2",
"orchard",
"pasta_curves",
"pczt",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 17b8782..b26575c 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -70,7 +70,7 @@ bitcoin = { version = "0.32.4", default-features = false, features = [
] }
bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] }
bitcoin_hashes = { version = "0.14.0", default-features = false }
-core2 = { version = "0.3.3", default-features = false, features = ["alloc"] }
+core2 = { version = "0.9.4", package = "no_std_io2", default-features = false }
thiserror = { version = "1.0", package = "thiserror-core", default-features = false }
rsa = { version = "0.8.2", default-features = false }
sha1 = { version = "0.10.5", default-features = false }
diff --git a/rust/apps/ton/Cargo.toml b/rust/apps/ton/Cargo.toml
index 23c0b13..0df0ebe 100644
--- a/rust/apps/ton/Cargo.toml
+++ b/rust/apps/ton/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
keystore = { workspace = true, default-features = false }
-bitstream-io = { git = "https://github.com/KeystoneHQ/bitstream-io", tag = "no_std@0.1.2", default-features = false, features = [
+bitstream-io = { version = "4.10.0", default-features = false, features = [
"alloc",
] }
num-bigint = { workspace = true }
diff --git a/rust/apps/ton/src/vendor/cell/builder.rs b/rust/apps/ton/src/vendor/cell/builder.rs
index d8daf4a..bf4d6f9 100644
--- a/rust/apps/ton/src/vendor/cell/builder.rs
+++ b/rust/apps/ton/src/vendor/cell/builder.rs
@@ -43,42 +43,42 @@ impl CellBuilder {
pub fn store_u8(&mut self, bit_len: usize, val: u8) -> Result<&mut Self, TonCellError> {
self.bit_writer
- .write(bit_len as u32, val)
+ .write_var(bit_len as u32, val)
.map_cell_builder_error()?;
Ok(self)
}
pub fn store_i8(&mut self, bit_len: usize, val: i8) -> Result<&mut Self, TonCellError> {
self.bit_writer
- .write(bit_len as u32, val)
+ .write_signed_var(bit_len as u32, val)
.map_cell_builder_error()?;
Ok(self)
}
pub fn store_u32(&mut self, bit_len: usize, val: u32) -> Result<&mut Self, TonCellError> {
self.bit_writer
- .write(bit_len as u32, val)
+ .write_var(bit_len as u32, val)
.map_cell_builder_error()?;
Ok(self)
}
pub fn store_i32(&mut self, bit_len: usize, val: i32) -> Result<&mut Self, TonCellError> {
self.bit_writer
- .write(bit_len as u32, val)
+ .write_signed_var(bit_len as u32, val)
.map_cell_builder_error()?;
Ok(self)
}
pub fn store_u64(&mut self, bit_len: usize, val: u64) -> Result<&mut Self, TonCellError> {
self.bit_writer
- .write(bit_len as u32, val)
+ .write_var(bit_len as u32, val)
.map_cell_builder_error()?;
Ok(self)
}
pub fn store_i64(&mut self, bit_len: usize, val: i64) -> Result<&mut Self, TonCellError> {
self.bit_writer
- .write(bit_len as u32, val)
+ .write_signed_var(bit_len as u32, val)
.map_cell_builder_error()?;
Ok(self)
}
diff --git a/rust/apps/ton/src/vendor/cell/mod.rs b/rust/apps/ton/src/vendor/cell/mod.rs
index f816919..befb8f7 100644
--- a/rust/apps/ton/src/vendor/cell/mod.rs
+++ b/rust/apps/ton/src/vendor/cell/mod.rs
@@ -242,8 +242,8 @@ fn get_repr_for_data(
let d2 = get_bits_descriptor(original_data, original_data_bit_len);
// Write descriptors
- writer.write(8, d1).map_cell_parser_error()?;
- writer.write(8, d2).map_cell_parser_error()?;
+ writer.write_var(8, d1).map_cell_parser_error()?;
+ writer.write_var(8, d2).map_cell_parser_error()?;
// Write main data
write_data(&mut writer, data, data_bit_len).map_cell_parser_error()?;
// Write ref data
@@ -357,7 +357,7 @@ fn write_data(
writer.write_bytes(&data[..data_len - 1])?;
let last_byte = data[data_len - 1];
let l = last_byte | 1 << (8 - rest_bits - 1);
- writer.write(8, l)?;
+ writer.write_var(8, l)?;
} else {
writer.write_bytes(data)?;
}
@@ -381,8 +381,12 @@ fn write_ref_depths(
reference.get_depth(level)
};
- writer.write(8, child_depth / 256).map_cell_parser_error()?;
- writer.write(8, child_depth % 256).map_cell_parser_error()?;
+ writer
+ .write_var(8, child_depth / 256)
+ .map_cell_parser_error()?;
+ writer
+ .write_var(8, child_depth % 256)
+ .map_cell_parser_error()?;
}
Ok(())
diff --git a/rust/apps/ton/src/vendor/cell/parser.rs b/rust/apps/ton/src/vendor/cell/parser.rs
index 82583fa..cd10715 100644
--- a/rust/apps/ton/src/vendor/cell/parser.rs
+++ b/rust/apps/ton/src/vendor/cell/parser.rs
@@ -32,49 +32,49 @@ impl CellParser<'_> {
pub fn load_u8(&mut self, bit_len: usize) -> Result<u8, TonCellError> {
self.bit_reader
- .read::<u8>(bit_len as u32)
+ .read_var::<u8>(bit_len as u32)
.map_cell_parser_error()
}
pub fn load_i8(&mut self, bit_len: usize) -> Result<i8, TonCellError> {
self.bit_reader
- .read::<i8>(bit_len as u32)
+ .read_signed_var::<i8>(bit_len as u32)
.map_cell_parser_error()
}
pub fn load_u16(&mut self, bit_len: usize) -> Result<u16, TonCellError> {
self.bit_reader
- .read::<u16>(bit_len as u32)
+ .read_var::<u16>(bit_len as u32)
.map_cell_parser_error()
}
pub fn load_i16(&mut self, bit_len: usize) -> Result<i16, TonCellError> {
self.bit_reader
- .read::<i16>(bit_len as u32)
+ .read_signed_var::<i16>(bit_len as u32)
.map_cell_parser_error()
}
pub fn load_u32(&mut self, bit_len: usize) -> Result<u32, TonCellError> {
self.bit_reader
- .read::<u32>(bit_len as u32)
+ .read_var::<u32>(bit_len as u32)
.map_cell_parser_error()
}
pub fn load_i32(&mut self, bit_len: usize) -> Result<i32, TonCellError> {
self.bit_reader
- .read::<i32>(bit_len as u32)
+ .read_signed_var::<i32>(bit_len as u32)
.map_cell_parser_error()
}
pub fn load_u64(&mut self, bit_len: usize) -> Result<u64, TonCellError> {
self.bit_reader
- .read::<u64>(bit_len as u32)
+ .read_var::<u64>(bit_len as u32)
.map_cell_parser_error()
}
pub fn load_i64(&mut self, bit_len: usize) -> Result<i64, TonCellError> {
self.bit_reader
- .read::<i64>(bit_len as u32)
+ .read_signed_var::<i64>(bit_len as u32)
.map_cell_parser_error()
}
@@ -161,12 +161,12 @@ impl CellParser<'_> {
}
pub fn load_address(&mut self) -> Result<TonAddress, TonCellError> {
- let tp = self.bit_reader.read::<u8>(2).map_cell_parser_error()?;
+ let tp = self.bit_reader.read_var::<u8>(2).map_cell_parser_error()?;
match tp {
0 => Ok(TonAddress::null()),
2 => {
- let _res1 = self.bit_reader.read::<u8>(1).map_cell_parser_error()?;
- let wc = self.bit_reader.read::<u8>(8).map_cell_parser_error()?;
+ let _res1 = self.bit_reader.read_var::<u8>(1).map_cell_parser_error()?;
+ let wc = self.bit_reader.read_var::<u8>(8).map_cell_parser_error()?;
let mut hash_part = [0_u8; 32];
self.bit_reader
.read_bytes(&mut hash_part)
diff --git a/rust/apps/ton/src/vendor/cell/raw.rs b/rust/apps/ton/src/vendor/cell/raw.rs
index b0a05ae..b7d1328 100644
--- a/rust/apps/ton/src/vendor/cell/raw.rs
+++ b/rust/apps/ton/src/vendor/cell/raw.rs
@@ -157,7 +157,7 @@ impl RawBagOfCells {
let mut writer = BitWriter::endian(Vec::with_capacity(total_size as usize), BigEndian);
writer
- .write(32, GENERIC_BOC_MAGIC)
+ .write_var(32, GENERIC_BOC_MAGIC)
.map_boc_serialization_error()?;
//write flags byte
@@ -168,28 +168,28 @@ impl RawBagOfCells {
writer
.write_bit(has_cache_bits)
.map_boc_serialization_error()?;
- writer.write(2, flags).map_boc_serialization_error()?;
+ writer.write_var(2, flags).map_boc_serialization_error()?;
writer
- .write(3, num_ref_bytes)
+ .write_var(3, num_ref_bytes)
.map_boc_serialization_error()?;
writer
- .write(8, num_offset_bytes)
+ .write_var(8, num_offset_bytes)
.map_boc_serialization_error()?;
writer
- .write(8 * num_ref_bytes, self.cells.len() as u32)
+ .write_var(8 * num_ref_bytes, self.cells.len() as u32)
.map_boc_serialization_error()?;
writer
- .write(8 * num_ref_bytes, root_count as u32)
+ .write_var(8 * num_ref_bytes, root_count as u32)
.map_boc_serialization_error()?;
writer
- .write(8 * num_ref_bytes, 0)
+ .write_var(8 * num_ref_bytes, 0)
.map_boc_serialization_error()?; // Complete BOCs only
writer
- .write(8 * num_offset_bytes, full_size)
+ .write_var(8 * num_offset_bytes, full_size)
.map_boc_serialization_error()?;
for &root in &self.roots {
writer
- .write(8 * num_ref_bytes, root as u32)
+ .write_var(8 * num_ref_bytes, root as u32)
.map_boc_serialization_error()?;
}
@@ -288,22 +288,22 @@ fn write_raw_cell(
// data_len_bytes <= 128 by spec, but d2 must be u8 by spec as well
let d2 = (data_len_bytes * 2 - if full_bytes { 0 } else { 1 }) as u8; //subtract 1 if the last byte is not full
- writer.write(8, d1).map_boc_serialization_error()?;
- writer.write(8, d2).map_boc_serialization_error()?;
+ writer.write_var(8, d1).map_boc_serialization_error()?;
+ writer.write_var(8, d2).map_boc_serialization_error()?;
if !full_bytes {
writer
.write_bytes(&data[..data_len_bytes - 1])
.map_boc_serialization_error()?;
let last_byte = data[data_len_bytes - 1];
let l = last_byte | 1 << (8 - padding_bits - 1);
- writer.write(8, l).map_boc_serialization_error()?;
+ writer.write_var(8, l).map_boc_serialization_error()?;
} else {
writer.write_bytes(data).map_boc_serialization_error()?;
}
for r in cell.references.as_slice() {
writer
- .write(8 * ref_size_bytes, *r as u32)
+ .write_var(8 * ref_size_bytes, *r as u32)
.map_boc_serialization_error()?;
}
diff --git a/rust/apps/ton/src/vendor/cell/util.rs b/rust/apps/ton/src/vendor/cell/util.rs
index c1da5cd..bf555d5 100644
--- a/rust/apps/ton/src/vendor/cell/util.rs
+++ b/rust/apps/ton/src/vendor/cell/util.rs
@@ -26,7 +26,7 @@ impl<R: io::Read, E: Endianness> BitReadExt for BitReader<R, E> {
let last_byte_len = num_bits % 8;
if last_byte_len != 0 {
let last_byte = self
- .read::<u8>(last_byte_len as u32)
+ .read_var::<u8>(last_byte_len as u32)
.map_cell_parser_error()?;
slice[full_bytes] = last_byte << (8 - last_byte_len);
}
Why this scored 30/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.