Skip to content

Commit 850d2bb

Browse files
committed
Fix Base58 zero buffer bug
1 parent c44cbb3 commit 850d2bb

3 files changed

Lines changed: 53 additions & 1 deletion

File tree

src/main/java/org/apache/commons/codec/binary/Base58.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private void convertFromBase58(final byte[] base58, final Context context) {
184184
value = value.add(BigInteger.valueOf(digit).multiply(power));
185185
power = power.multiply(BASE);
186186
}
187-
byte[] decoded = value.toByteArray();
187+
byte[] decoded = value.equals(BigInteger.ZERO) ? EMPTY : value.toByteArray();
188188
if (decoded.length > 1 && decoded[0] == 0) {
189189
final byte[] tmp = new byte[decoded.length - 1];
190190
System.arraycopy(decoded, 1, tmp, 0, tmp.length);

src/test/java/org/apache/commons/codec/binary/Base58OutputStreamTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@
2323
import static org.junit.jupiter.api.Assertions.assertThrows;
2424

2525
import java.io.ByteArrayOutputStream;
26+
import java.io.IOException;
2627
import java.io.OutputStream;
2728
import java.nio.charset.StandardCharsets;
2829
import java.util.Arrays;
2930

31+
import org.apache.commons.lang3.ArrayFill;
3032
import org.junit.jupiter.api.Test;
33+
import org.junit.jupiter.params.ParameterizedTest;
34+
import org.junit.jupiter.params.provider.ValueSource;
3135

3236
/**
3337
* Tests {@link Base58OutputStream}.
@@ -160,6 +164,39 @@ private void testByteByByte(final byte[] encoded, final byte[] decoded, final in
160164
assertArrayEquals(decoded, output, "Streaming byte-by-byte Base58 wrap-wrap-wrap!");
161165
}
162166

167+
@ParameterizedTest
168+
@ValueSource(ints = { 0, 1, 2, 3, 4 })
169+
void testDecodeByte49(final int len) throws IOException {
170+
// Sanity check, each step from scratch:
171+
final byte[] zeros = new byte[len];
172+
final byte[] encoded0s = ArrayFill.fill(zeros.clone(), (byte) '1');
173+
assertArrayEquals(encoded0s, Base58.builder().get().encode(zeros));
174+
final byte[] decoded = Base58.builder().get().decode(encoded0s);
175+
assertArrayEquals(zeros, decoded, () -> String.format("zeros=%s, decoded=%s", Arrays.toString(zeros), Arrays.toString(decoded)));
176+
// Version 1.21.1:
177+
// AssertionFailedError: Streaming byte-by-byte Base58 decode, chunkSize=0, separator=[10], encoded=[49], decoded=[0], output=[0, 0] ==> array lengths
178+
// differ, expected: <1> but was: <2>
179+
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
180+
final byte[] byteData = new byte[len];
181+
try (OutputStream out = Base58OutputStream.builder().setOutputStream(byteOut).setEncode(true).get()) {
182+
for (final byte element : byteData) {
183+
out.write(element);
184+
}
185+
}
186+
final byte[] output0 = byteOut.toByteArray();
187+
assertArrayEquals(encoded0s, output0, () -> String.format("Streaming byte-by-byte Base58 decode, encoded=%s, decoded=%s, output=%s",
188+
Arrays.toString(encoded0s), Arrays.toString(byteData), Arrays.toString(output0)));
189+
byteOut = new ByteArrayOutputStream();
190+
try (OutputStream out = Base58OutputStream.builder().setOutputStream(byteOut).setEncode(false).get()) {
191+
for (final byte element : encoded0s) {
192+
out.write(element);
193+
}
194+
}
195+
final byte[] output1 = byteOut.toByteArray();
196+
assertArrayEquals(byteData, output1, () -> String.format("Streaming byte-by-byte Base58 decode, encoded=%s, decoded=%s, output=%s",
197+
Arrays.toString(encoded0s), Arrays.toString(byteData), Arrays.toString(output1)));
198+
}
199+
163200
@Test
164201
void testRfcTestVector1() {
165202
assertEquals("2NEpo7TZRRrLZSi2U", Base58.builder().get().encodeToString("Hello World!".getBytes(StandardCharsets.US_ASCII)));

src/test/java/org/apache/commons/codec/binary/Base58Test.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static org.junit.jupiter.api.Assertions.assertThrows;
2525
import static org.junit.jupiter.api.Assertions.assertTrue;
2626

27+
import java.io.IOException;
2728
import java.nio.ByteBuffer;
2829
import java.nio.charset.Charset;
2930
import java.nio.charset.StandardCharsets;
@@ -33,7 +34,10 @@
3334

3435
import org.apache.commons.codec.DecoderException;
3536
import org.apache.commons.codec.EncoderException;
37+
import org.apache.commons.lang3.ArrayFill;
3638
import org.junit.jupiter.api.Test;
39+
import org.junit.jupiter.params.ParameterizedTest;
40+
import org.junit.jupiter.params.provider.ValueSource;
3741

3842
/**
3943
* Tests {@link Base58}.
@@ -224,6 +228,17 @@ void testRoundTrip() {
224228
}
225229
}
226230

231+
@ParameterizedTest
232+
@ValueSource(ints = { 0, 1, 2, 3, 4 })
233+
void testRoundtripByte0(final int len) throws IOException {
234+
// Sanity check, each step from scratch:
235+
final byte[] zeros = new byte[len];
236+
final byte[] encoded0s = ArrayFill.fill(zeros.clone(), (byte) '1');
237+
assertArrayEquals(encoded0s, Base58.builder().get().encode(zeros));
238+
final byte[] decoded = Base58.builder().get().decode(encoded0s);
239+
assertArrayEquals(zeros, decoded, () -> String.format("zeros=%s, decoded=%s", Arrays.toString(zeros), Arrays.toString(decoded)));
240+
}
241+
227242
@Test
228243
void testSingleBytes() {
229244
// Test encoding of single bytes

0 commit comments

Comments
 (0)