Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use libj.math.BigInt for arithmetic #15

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
mavenCentral()
maven("https://maven.repository.redhat.com/ga/")
}

dependencies {
Expand All @@ -17,6 +18,7 @@ dependencies {
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation("org.apache.logging.log4j:log4j-api:2.23.1")
implementation("org.apache.logging.log4j:log4j-core:2.23.1")
implementation("org.libj:math:0.6.8")
}

group = "io.github.mysto"
Expand Down
66 changes: 44 additions & 22 deletions src/main/java/com/privacylogistics/FF3Cipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
import java.nio.charset.StandardCharsets;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libj.math.BigInt;

/**
* Class FF3Cipher implements the FF3 format-preserving encryption algorithm
Expand Down Expand Up @@ -213,14 +213,14 @@ public String encrypt(String plaintext, byte[] tweak) throws BadPaddingException
// Pre-calculate the modulus since it's only one of 2 values,
// depending on whether it is even or odd

BigInteger modU = BigInteger.valueOf(this.radix).pow(u);
BigInteger modV = BigInteger.valueOf(this.radix).pow(v);
BigInt modU = new BigInt(this.radix).pow(u);
BigInt modV = new BigInt(this.radix).pow(v);
logger.trace("u {} v {} modU: {} modV: {}", u, v, modU, modV);
logger.trace("tL: {} tR: {}", () -> byteArrayToHexString(Tl), () -> byteArrayToHexString(Tr));

for (byte i = 0; i < NUM_ROUNDS; ++i) {
int m;
BigInteger c;
BigInt c;
byte[] W;

// Determine alternating Feistel round side, right or left
Expand All @@ -241,7 +241,7 @@ public String encrypt(String plaintext, byte[] tweak) throws BadPaddingException
reverseBytes(S);
logger.trace("\tS: {}", () -> byteArrayToHexString(S));

BigInteger y = new BigInteger(byteArrayToHexString(S), 16);
BigInt y = byteArrayToBigInt(S);

// Calculate c
c = decode_int_r(A, alphabet);
Expand Down Expand Up @@ -337,14 +337,14 @@ public String decrypt(String ciphertext, byte[] tweak) throws BadPaddingExceptio
// Pre-calculate the modulus since it's only one of 2 values,
// depending on whether it is even or odd

BigInteger modU = BigInteger.valueOf(this.radix).pow(u);
BigInteger modV = BigInteger.valueOf(this.radix).pow(v);
BigInt modU = new BigInt(radix).pow(u);
BigInt modV = new BigInt(radix).pow(v);
logger.trace("modU: {} modV: {}", modU, modV);
logger.trace("tL: {} tR: {}", () -> byteArrayToHexString(Tl), () -> byteArrayToHexString(Tr));

for (byte i = (byte) (NUM_ROUNDS - 1); i >= 0; --i) {
int m;
BigInteger c;
BigInt c;
byte[] W;

// Determine alternating Feistel round side, right or left
Expand All @@ -365,12 +365,12 @@ public String decrypt(String ciphertext, byte[] tweak) throws BadPaddingExceptio
reverseBytes(S);
logger.trace("\tS: {}", () -> byteArrayToHexString(S));

BigInteger y = new BigInteger(byteArrayToHexString(S), 16);
BigInt y = byteArrayToBigInt(S);

// Calculate c
c = decode_int_r(B, alphabet);

c = c.subtract(y);
c = c.sub(y);

if (i % 2 == 0) {
c = c.mod(modU);
Expand Down Expand Up @@ -433,7 +433,7 @@ 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();
byte[] bBytes = decode_int_r(B, alphabet).toByteArray(false);

System.arraycopy(bBytes, 0, P, (BLOCK_SIZE - bBytes.length), bBytes.length);
logger.trace("round: {} W: {} P: {}", () -> i, () -> byteArrayToHexString(W), () -> byteArrayToIntString(P));
Expand Down Expand Up @@ -499,11 +499,32 @@ protected static String byteArrayToIntString(byte[] byteArray) {
return Arrays.toString(byteArray);
}

/**
* Return a BigInt value of a byte array with absolute behavior.
* This has the same behavior as constructing a BigInteger by
* passing in a hex string with radix = 16.
*
* @param b byte array to convert
*/
public static BigInt byteArrayToBigInt(byte[] b) {
if (b[0] < 0) {
byte[] wb = new byte[b.length + 1];
wb[0] = 0;
System.arraycopy(b, 0, wb, 1, b.length);
return new BigInt(wb, false);
} else {
return new BigInt(b, false);
}
}

/**
* Return a char[] representation of a number in the given base system
* - the char[] is right padded with zeros to length
* - the char[] is returned in reversed order expected by the calling cryptographic function
* i.e., the decimal value 123 in five decimal places would be '32100'
* i.e., the decimal value 123 in five decimal places would be '32100'.
* Note: in order to efficiently encode its input, this method mutates the value of `n`.
* If you would like to preserve the value of the input, pass a `clone()` of `n` to this
* method.
*
* examples:
* encode_int_r(10, 16,2)
Expand All @@ -513,16 +534,15 @@ protected static String byteArrayToIntString(byte[] byteArray) {
* @param length the length used for padding the output string
* @return a char[] encoding of the number
*/
protected static char[] encode_int_r(BigInteger n, String alphabet, int length) {

protected static char[] encode_int_r(BigInt n, String alphabet, int length) {
char[] x = new char[length];
int i=0;

BigInteger bbase = BigInteger.valueOf(alphabet.length());
int base = alphabet.length();
BigInt bbase = new BigInt(base);
while (n.compareTo(bbase) >= 0) {
BigInteger b = n.mod(bbase);
n = n.divide(bbase);
x[i++] = alphabet.charAt(b.intValue());
int b = (int) n.divRem(base);
x[i++] = alphabet.charAt(b);
}
x[i++] = alphabet.charAt(n.intValue());

Expand All @@ -540,12 +560,14 @@ protected static char[] encode_int_r(BigInteger n, String alphabet, int length)
* @param alphabet the alphabet and radix (len(alphabet)) for decoding
* @return an integer value of the encoded char[]
*/
protected static BigInteger decode_int_r(char[] str, String alphabet) {
BigInteger base = BigInteger.valueOf(alphabet.length());
BigInteger num = BigInteger.ZERO;
protected static BigInt decode_int_r(char[] str, String alphabet) {
int radix = alphabet.length();
BigInt num = new BigInt(0);

for (int i = 0; i < str.length; i++) {
char ch = str[i];
num = num.add(base.pow(i).multiply(BigInteger.valueOf(alphabet.indexOf(ch))));
BigInt base = new BigInt(radix);
num = num.add(base.pow(i).mul(alphabet.indexOf(ch)));
}
return num;
}
Expand Down
25 changes: 12 additions & 13 deletions src/test/java/com/privacylogistics/FF3CipherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
import static com.privacylogistics.FF3Cipher.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.condition.JRE;

import java.math.BigInteger;
import org.libj.math.BigInt;

public class FF3CipherTest {

Expand Down Expand Up @@ -170,21 +169,21 @@ public void testInvalidPlaintext() throws Exception {

@Test
public void testEncodeBigInt() {
assertEquals("101", reverseString(new String(encode_int_r(BigInteger.valueOf(5), "01", 3))));
assertEquals("11", reverseString(new String(encode_int_r(BigInteger.valueOf(6), "01234", 2))));
assertEquals("00012", reverseString(new String(encode_int_r(BigInteger.valueOf(7), "01234", 5))));
assertEquals("a", reverseString(new String(encode_int_r(BigInteger.valueOf(10), "0123456789abcdef", 1))));
assertEquals("20", reverseString(new String(encode_int_r(BigInteger.valueOf(32), "0123456789abcdef", 2))));
assertEquals("101", reverseString(new String(encode_int_r(new BigInt(5), "01", 3))));
assertEquals("11", reverseString(new String(encode_int_r(new BigInt(6), "01234", 2))));
assertEquals("00012", reverseString(new String(encode_int_r(new BigInt(7), "01234", 5))));
assertEquals("a", reverseString(new String(encode_int_r(new BigInt(10), "0123456789abcdef", 1))));
assertEquals("20", reverseString(new String(encode_int_r(new BigInt(32), "0123456789abcdef", 2))));
}

@Test
public void testDecodeInt() {
assertEquals(BigInteger.valueOf(321), (decode_int_r("123".toCharArray(), "0123456789")));
assertEquals(BigInteger.valueOf(101), (decode_int_r("101".toCharArray(), "0123456789")));
assertEquals(BigInteger.valueOf(101), (decode_int_r("10100".toCharArray(), "0123456789")));
assertEquals(BigInteger.valueOf(0x02), (decode_int_r("20".toCharArray(), "0123456789abcdef")));
assertEquals(BigInteger.valueOf(0xAA), (decode_int_r("aa".toCharArray(), "0123456789abcdef")));
assertEquals(new BigInteger("2297305934914824457484538562"), (decode_int_r("2658354847544284194395037922".toCharArray(), "0123456789")));
assertEquals(new BigInt(321), (decode_int_r("123".toCharArray(), "0123456789")));
assertEquals(new BigInt(101), (decode_int_r("101".toCharArray(), "0123456789")));
assertEquals(new BigInt(101), (decode_int_r("10100".toCharArray(), "0123456789")));
assertEquals(new BigInt(0x02), (decode_int_r("20".toCharArray(), "0123456789abcdef")));
assertEquals(new BigInt(0xAA), (decode_int_r("aa".toCharArray(), "0123456789abcdef")));
assertEquals(new BigInt("2297305934914824457484538562"), (decode_int_r("2658354847544284194395037922".toCharArray(), "0123456789")));
}

@Test
Expand Down