Merge pull request #8818 from sylvestre/fix-base58
Some checks failed
CICD / Style/cargo-deny (push) Waiting to run
CICD / Style/deps (push) Waiting to run
CICD / Build/Makefile (push) Blocked by required conditions
CICD / Build/stable (push) Blocked by required conditions
CICD / Build/nightly (push) Blocked by required conditions
CICD / Binary sizes (push) Blocked by required conditions
CICD / Build (push) Blocked by required conditions
CICD / Tests/BusyBox test suite (push) Blocked by required conditions
CICD / Tests/Toybox test suite (push) Blocked by required conditions
CICD / Code Coverage (push) Waiting to run
CICD / Separate Builds (push) Waiting to run
CICD / Build/SELinux (push) Blocked by required conditions
CICD / Build/SELinux-Stubs (Non-Linux) (push) Blocked by required conditions
CICD / Safe Traversal Security Check (push) Blocked by required conditions
CICD / Run benchmarks (CodSpeed) (push) Blocked by required conditions
GnuTests / Run GNU tests (native) (push) Waiting to run
GnuTests / Run GNU tests (SELinux) (push) Waiting to run
Code Quality / Style/Python (push) Waiting to run
Code Quality / Style/format (push) Waiting to run
CICD / Documentation/warnings (push) Waiting to run
CICD / MinRustV (push) Waiting to run
CICD / Test all features separately (push) Blocked by required conditions
CICD / Dependencies (push) Waiting to run
GnuTests / Aggregate GNU test results (push) Blocked by required conditions
Android / Test builds (push) Waiting to run
Code Quality / Style/lint (push) Waiting to run
Code Quality / Style/spelling (push) Waiting to run
Code Quality / Style/toml (push) Waiting to run
Code Quality / Pre-commit hooks (push) Waiting to run
Devcontainer / Verify devcontainer (push) Waiting to run
FreeBSD / Style and Lint (push) Waiting to run
FreeBSD / Tests (push) Waiting to run
WSL2 / Test (push) Waiting to run
CheckScripts / ShellScript/Check (push) Has been cancelled
CheckScripts / ShellScript/Format (push) Has been cancelled

base58: it wasn't working properly with long input
This commit is contained in:
Daniel Hofstetter 2025-10-06 17:38:22 +02:00 committed by GitHub
commit c6f646f478
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 13 deletions

View file

@ -266,36 +266,50 @@ impl SupportsFastDecodeAndEncode for Base58Wrapper {
return Ok(());
}
// Convert bytes to big integer
let mut num: Vec<u32> = Vec::new();
// Convert bytes to big integer (Vec<u32> in little-endian format)
let mut num = Vec::with_capacity(input_trimmed.len().div_ceil(4) + 1);
for &byte in input_trimmed {
let mut carry = byte as u32;
let mut carry = byte as u64;
for n in &mut num {
let tmp = (*n as u64) * 256 + carry as u64;
let tmp = (*n as u64) * 256 + carry;
*n = tmp as u32;
carry = (tmp >> 32) as u32;
carry = tmp >> 32;
}
if carry > 0 {
num.push(carry);
num.push(carry as u32);
}
}
// Convert to base58
let mut result = Vec::new();
let mut result = Vec::with_capacity((input_trimmed.len() * 138 / 100) + 1);
let alphabet = self.alphabet();
while !num.is_empty() && num.iter().any(|&n| n != 0) {
// Optimized check: stop when all elements are zero
while !num.is_empty() {
// Check if we're done (all zeros)
let mut all_zero = true;
let mut carry = 0u64;
for n in num.iter_mut().rev() {
let tmp = carry * (1u64 << 32) + *n as u64;
*n = (tmp / 58) as u32;
carry = tmp % 58;
if *n != 0 {
all_zero = false;
}
}
result.push(alphabet[carry as usize]);
// Remove leading zeros
while num.last() == Some(&0) && num.len() > 1 {
num.pop();
if all_zero {
break;
}
// Trim trailing zeros less frequently
if num.len() > 1 && result.len() % 8 == 0 {
while num.last() == Some(&0) && num.len() > 1 {
num.pop();
}
}
}
@ -305,7 +319,7 @@ impl SupportsFastDecodeAndEncode for Base58Wrapper {
}
// Add result (reversed because we built it backwards)
for byte in result.into_iter().rev() {
for &byte in result.iter().rev() {
output.push_back(byte);
}
@ -313,7 +327,10 @@ impl SupportsFastDecodeAndEncode for Base58Wrapper {
}
fn unpadded_multiple(&self) -> usize {
1 // Base58 doesn't use padding
// Base58 must encode the entire input as one big integer, not in chunks
// Use a very large value to effectively disable chunking, but avoid overflow
// when multiplied by ENCODE_IN_CHUNKS_OF_SIZE_MULTIPLE (1024) in base_common
usize::MAX / 2048
}
fn valid_decoding_multiple(&self) -> usize {

View file

@ -211,6 +211,31 @@ fn test_base58_decode() {
.stdout_only("Hello, World!");
}
#[test]
fn test_base58_large_file_no_chunking() {
// Regression test: base58 must process entire input as one big integer,
// not in 1024-byte chunks. This test ensures files >1024 bytes work correctly.
let (at, mut ucmd) = at_and_ucmd!();
let filename = "large_file.txt";
// spell-checker:disable
let input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ".repeat(50);
// spell-checker:enable
at.write(filename, &input);
let result = ucmd.arg("--base58").arg(filename).succeeds();
let encoded = result.stdout_str();
// Verify the output ends with the expected suffix (matches GNU basenc output)
// spell-checker:disable
assert!(
encoded
.trim_end()
.ends_with("ZNRRacEnhrY83ZEYkpwWVZNFK5DFRasr\nw693NsNGtiQ9fYAj")
);
// spell-checker:enable
}
#[test]
fn test_choose_last_encoding_base64() {
new_ucmd!()