From f0a932df85e84cafa5bac22d18132236f33adf11 Mon Sep 17 00:00:00 2001 From: fmahfoodh Date: Mon, 9 Nov 2026 01:45:07 +0400 Subject: [PATCH] Convert BigIntegers to null-padded bytes without shifting per byte --- .../util/DataConverter.java | 13 ++- .../util/DataConverterNullPaddedTest.java | 79 +++++++++++++++++++ 2 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 src/test/java/de/rub/nds/modifiablevariable/util/DataConverterNullPaddedTest.java diff --git a/src/main/java/de/rub/nds/modifiablevariable/util/DataConverter.java b/src/main/java/de/rub/nds/modifiablevariable/util/DataConverter.java index db56b052..dc992111 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/util/DataConverter.java +++ b/src/main/java/de/rub/nds/modifiablevariable/util/DataConverter.java @@ -699,13 +699,12 @@ public static byte[] bigIntegerToNullPaddedByteArray(BigInteger input, int outpu numByteBlocks = output.length; } - int i; - for (i = 0; i < numByteBlocks; i++) { - output[output.length - 1 - i] = input.shiftRight(i * 8).byteValue(); - } - if (remainingBits > 0) { - output[output.length - 1 - i] = input.shiftRight(i * 8).byteValue(); - } + int written = remainingBits > 0 ? numByteBlocks + 1 : numByteBlocks; + // byte i of the result from the end is input.shiftRight(i * 8).byteValue(), which is byte i + // from the end of the two's complement encoding (at least bitLength / 8 + 1 bytes long) + byte[] encoded = input.toByteArray(); + System.arraycopy( + encoded, encoded.length - written, output, output.length - written, written); return output; } diff --git a/src/test/java/de/rub/nds/modifiablevariable/util/DataConverterNullPaddedTest.java b/src/test/java/de/rub/nds/modifiablevariable/util/DataConverterNullPaddedTest.java new file mode 100644 index 00000000..81dea5c4 --- /dev/null +++ b/src/test/java/de/rub/nds/modifiablevariable/util/DataConverterNullPaddedTest.java @@ -0,0 +1,79 @@ +/* + * ModifiableVariable - A Variable Concept for Runtime Modifications + * + * Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 + */ +package de.rub.nds.modifiablevariable.util; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.math.BigInteger; +import java.util.Random; +import org.junit.jupiter.api.Test; + +/** bigIntegerToNullPaddedByteArray must equal the byte-wise shift implementation it replaced. */ +class DataConverterNullPaddedTest { + + private static byte[] reference(BigInteger input, int outputSizeInBytes) { + byte[] output = new byte[outputSizeInBytes]; + int numByteBlocks = input.bitLength() / 8; + int remainingBits; + if (numByteBlocks < output.length) { + remainingBits = input.bitLength() % 8; + } else { + remainingBits = 0; + numByteBlocks = output.length; + } + int i; + for (i = 0; i < numByteBlocks; i++) { + output[output.length - 1 - i] = input.shiftRight(i * 8).byteValue(); + } + if (remainingBits > 0) { + output[output.length - 1 - i] = input.shiftRight(i * 8).byteValue(); + } + return output; + } + + private static void check(BigInteger input, int size) { + assertArrayEquals( + reference(input, size), + DataConverter.bigIntegerToNullPaddedByteArray(input, size), + input + " size " + size); + } + + @Test + void testEqualsShiftImplementation() { + Random random = new Random(1); + for (int n = 0; n < 200000; n++) { + int bits = random.nextInt(600); + BigInteger value = new BigInteger(bits, random); + if (random.nextInt(4) == 0) { + // exact powers of two and all-ones values hit the byte boundaries + value = + random.nextBoolean() + ? BigInteger.ONE.shiftLeft(bits) + : BigInteger.ONE.shiftLeft(bits).subtract(BigInteger.ONE); + } + if (random.nextInt(3) == 0) { + value = value.negate(); + } + int bytes = (bits + 7) / 8; + int size = Math.max(0, bytes + random.nextInt(5) - 2); + if (random.nextInt(10) == 0) { + size = random.nextInt(80); + } + check(value, size); + } + } + + @Test + void testSmallValuesAndSizes() { + for (int v = -70000; v <= 70000; v += (v > -300 && v < 300) ? 1 : 97) { + for (int size = 0; size <= 5; size++) { + check(BigInteger.valueOf(v), size); + } + } + } +}