From e6ca13121b887e228a96d4f91ad8bc2038176dd4 Mon Sep 17 00:00:00 2001 From: Brad Schoening Date: Sun, 9 Feb 2025 22:04:01 -0500 Subject: [PATCH] Fixed bug when BigInteger overflows to 13th byte --- .../java/com/privacylogistics/FF3Cipher.java | 26 ++++++++--------- .../com/privacylogistics/FF3CipherTest.java | 29 +++++++++++++++++++ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/privacylogistics/FF3Cipher.java b/src/main/java/com/privacylogistics/FF3Cipher.java index 02f0c59..6a8c948 100644 --- a/src/main/java/com/privacylogistics/FF3Cipher.java +++ b/src/main/java/com/privacylogistics/FF3Cipher.java @@ -433,10 +433,16 @@ protected static byte[] calculateP(int i, String alphabet, byte[] W, char[] B) { // The remaining 12 bytes of P are copied from reverse(B) with padding - byte[] bBytes = decode_int_r(B, alphabet).toByteArray(); - - System.arraycopy(bBytes, 0, P, (BLOCK_SIZE - bBytes.length), bBytes.length); - logger.trace("round: {} W: {} P: {}", () -> i, () -> byteArrayToHexString(W), () -> byteArrayToIntString(P)); + BigInteger val = decode_int_r(B, alphabet); + byte[] bBytes = val.toByteArray(); + + // BigInteger's toByteArray may return a 13th sign byte, but we consider the value unsigned + if (bBytes.length > 12) { + System.arraycopy(bBytes, 1, P, (BLOCK_SIZE - 12), 12); + } else { + System.arraycopy(bBytes, 0, P, (BLOCK_SIZE - bBytes.length), bBytes.length); + } + logger.trace("round: {} P: {} W: {}", () -> i, () -> byteArrayToHexString(P), () -> byteArrayToHexString(W)); return P; } @@ -453,7 +459,7 @@ protected static String reverseString(String s) { * Reverse a byte array in-place * @param b a mutable byte array */ - protected void reverseBytes(byte[] b) { + protected static void reverseBytes(byte[] b) { for (int i = 0; i < b.length / 2; i++) { byte temp = b[i]; b[i] = b[b.length - i - 1]; @@ -466,7 +472,7 @@ protected void reverseBytes(byte[] b) { * @param s a character string containing hexadecimal digits * @return a byte array with the values parsed from the string */ - protected static byte[] hexStringToByteArray(String s) { + public static byte[] hexStringToByteArray(String s) { byte[] data = new byte[s.length() / 2]; for (int i = 0; i < s.length(); i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) @@ -490,14 +496,6 @@ protected static String byteArrayToHexString(byte[] byteArray) { return new String(hexChars, StandardCharsets.UTF_8); } - /** - * used for debugging output - * @param byteArray a byte array - * @return a decimal string encoding of a number - */ - protected static String byteArrayToIntString(byte[] byteArray) { - return Arrays.toString(byteArray); - } /** * Return a char[] representation of a number in the given base system diff --git a/src/test/java/com/privacylogistics/FF3CipherTest.java b/src/test/java/com/privacylogistics/FF3CipherTest.java index 60e1ef1..3882b80 100644 --- a/src/test/java/com/privacylogistics/FF3CipherTest.java +++ b/src/test/java/com/privacylogistics/FF3CipherTest.java @@ -187,6 +187,18 @@ public void testDecodeInt() { assertEquals(new BigInteger("2297305934914824457484538562"), (decode_int_r("2658354847544284194395037922".toCharArray(), "0123456789"))); } + @Test + public void testReverseBytes() { + byte [] mutableArray = new byte[] {1}; + reverseBytes(mutableArray); + assertArrayEquals(new byte[]{1}, mutableArray); + mutableArray = new byte[] {1,2,3}; + reverseBytes(mutableArray); + assertArrayEquals(new byte[]{3,2,1}, mutableArray); + mutableArray = new byte[] {1,2,3,4}; + reverseBytes(mutableArray); + assertArrayEquals(new byte[]{4,3,2,1}, mutableArray); + } @Test public void testNistFF3() throws Exception { // NIST FF3-AES 128, 192, 256 @@ -291,6 +303,23 @@ public void testGermanAlphabet() throws Exception { assertEquals(pt, plaintext); } + @Test + public void testDecodeIntHighByte() throws Exception { + // Test the German alphabet with a radix of 70. German consists of the latin alphabet + // plus four additional letters, each of which have uppercase and lowercase letters + + String alphabet = FF3Cipher.ASCII_UPPERCASE + FF3Cipher.ASCII_LOWERCASE + DIGITS; + String key = "2DE79D232DF5585D68CE47882AE256D6"; + String tweak = "CBD09280979564"; + String pt = "Ceciestuntestdechiffrement123cet"; + String ct = "0uaTPI9g49f9MMw54OvY8x5rmNcrhydM"; + FF3Cipher c = new FF3Cipher(key, tweak, alphabet); + String ciphertext = c.encrypt(pt); + assertEquals(ct, ciphertext); + String plaintext = c.decrypt(ciphertext); + assertEquals(pt, plaintext); + } + @Test void testTweakHasNoSideEffects() throws Exception { String key = "EF4359D8D580AA4F7F036D6F04FC6A94";