Skip to content

Commit

Permalink
Merge pull request #13 from altr-benjamin/feat.byte-array-ctors
Browse files Browse the repository at this point in the history
Add byte[] overloads for all String ctors
  • Loading branch information
bschoening authored Nov 18, 2024
2 parents 09c4d43 + 6556460 commit 257fc57
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 13 deletions.
48 changes: 39 additions & 9 deletions src/main/java/com/privacylogistics/FF3Cipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,27 @@ public FF3Cipher(String key, String tweak) {
this(key, tweak, 10);
}

/**
* Constructor with default radix of 10.
*
* @param key encryption key used to initialize AES ECB
* @param tweak used in each round and split into right and left sides
*/
public FF3Cipher(byte[] key, byte[] tweak) {
this(key, tweak, 10);
}

/**
* Constructor with a custom alphabet
*
* @param key encryption key used to initialize AES ECB
* @param tweak used in each round and split into right and left sides
* @param alphabet the cipher alphabet
*/
public FF3Cipher(String key, String tweak, String alphabet) {
public FF3Cipher(byte[] key, byte[] tweak, String alphabet) {
this.alphabet = alphabet;
this.radix = alphabet.length();

byte[] keyBytes = hexStringToByteArray(key);

// Calculate range of supported message lengths [minLen..maxLen]
// radix 10: 6 ... 56, 26: 5 ... 40, 36: 4 .. 36

Expand All @@ -66,10 +74,9 @@ public FF3Cipher(String key, String tweak, String alphabet) {
// ToDo: With log2 we could further simplify this
// this.maxLen = (int) (2 * Math.floor(96/Math.log2(radix)));

int keyLen = keyBytes.length;
// Check if the key is 128, 192, or 256 bits = 16, 24, or 32 bytes
if (keyLen != 16 && keyLen != 24 && keyLen != 32) {
throw new IllegalArgumentException("key length " + keyLen + " but must be 128, 192, or 256 bits");
if (key.length != 16 && key.length != 24 && key.length != 32) {
throw new IllegalArgumentException("key length " + key.length + " but must be 128, 192, or 256 bits");
}

// While FF3 allows radices in [2, 2^16], currently only tested up to 64
Expand All @@ -82,15 +89,16 @@ public FF3Cipher(String key, String tweak, String alphabet) {
throw new IllegalArgumentException("minLen or maxLen invalid, adjust your radix");
}

this.defaultTweak = hexStringToByteArray(tweak);
this.defaultTweak = tweak;

// AES block cipher in ECB mode with the block size derived based on the length of the key
// Always use the reversed key since Encrypt and Decrypt call cipher expecting that
// Feistel ciphers use the same func for encrypt/decrypt, so mode is always ENCRYPT_MODE

try {
reverseBytes(keyBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
byte[] reversedKey = key.clone();
reverseBytes(reversedKey);
SecretKeySpec keySpec = new SecretKeySpec(reversedKey, "AES");
aesCipher = Cipher.getInstance("AES/ECB/NoPadding");
aesCipher.init(Cipher.ENCRYPT_MODE, keySpec);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
Expand All @@ -99,6 +107,17 @@ public FF3Cipher(String key, String tweak, String alphabet) {
}
}

/**
* Constructor with a custom alphabet
*
* @param key encryption key used to initialize AES ECB
* @param tweak used in each round and split into right and left sides
* @param alphabet the cipher alphabet
*/
public FF3Cipher(String key, String tweak, String alphabet) {
this(hexStringToByteArray(key), hexStringToByteArray(tweak), alphabet);
}

/**
* Constructor with a standardized radix
*
Expand All @@ -107,6 +126,17 @@ public FF3Cipher(String key, String tweak, String alphabet) {
* @param radix the domain of the alphabet, 10, 26 or 36
*/
public FF3Cipher(String key, String tweak, int radix) {
this(hexStringToByteArray(key), hexStringToByteArray(tweak), alphabetForBase(radix));
}

/**
* Constructor with a standardized radix
*
* @param key encryption key used to initialize AES ECB
* @param tweak used in each round and split into right and left sides
* @param radix the domain of the alphabet, 10, 26 or 36
*/
public FF3Cipher(byte[] key, byte[] tweak, int radix) {
this(key, tweak, alphabetForBase(radix));
}

Expand Down
38 changes: 34 additions & 4 deletions src/test/java/com/privacylogistics/FF3CipherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnJre;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.condition.JRE;

Expand All @@ -27,6 +28,7 @@
import static com.privacylogistics.FF3Cipher.reverseString;
import static com.privacylogistics.FF3Cipher.encode_int_r;
import static com.privacylogistics.FF3Cipher.decode_int;
import static com.privacylogistics.FF3Cipher.hexStringToByteArray;

public class FF3CipherTest {

Expand Down Expand Up @@ -119,9 +121,25 @@ public class FF3CipherTest {
};

@Test
public void testCreate() {
FF3Cipher c = new FF3Cipher("EF4359D8D580AA4F7F036D6F04FC6A94", "D8E7920AFA330A73");
assertNotNull(c);
public void testConstructors() {
String keyStr = "EF4359D8D580AA4F7F036D6F04FC6A94";
String tweakStr = "D8E7920AFA330A73";
byte[] keyBytes = hexStringToByteArray(keyStr);
byte[] tweakBytes = hexStringToByteArray(tweakStr);

FF3Cipher cs0 = new FF3Cipher(keyStr, tweakStr);
FF3Cipher cs1 = new FF3Cipher(keyStr, tweakStr, 62);
FF3Cipher cs2 = new FF3Cipher(keyStr, tweakStr, "0123456789");
FF3Cipher cb0 = new FF3Cipher(keyBytes, tweakBytes);
FF3Cipher cb1 = new FF3Cipher(keyBytes, tweakBytes, 62);
FF3Cipher cb2 = new FF3Cipher(keyBytes, tweakBytes, "0123456789");

assertNotNull(cs0);
assertNotNull(cs1);
assertNotNull(cs2);
assertNotNull(cb0);
assertNotNull(cb1);
assertNotNull(cb2);
}

@Test
Expand Down Expand Up @@ -221,7 +239,7 @@ public void testAcvpFF3_1() throws Exception {
}

@Test
public void testFF3_1() throws Exception {
public void testFF3_1_str() throws Exception {
// Test with 56 bit tweak
String[] testVector = TestVectors[0];
FF3Cipher c = new FF3Cipher(testVector[Tkey], "D8E7920AFA330A", Integer.parseInt(testVector[Tradix]));
Expand All @@ -232,6 +250,18 @@ public void testFF3_1() throws Exception {
assertEquals(pt, plaintext);
}

@Test
public void testFF3_1_bytes() throws Exception {
// Test with 56 bit tweak
String[] testVector = TestVectors[0];
FF3Cipher c = new FF3Cipher(hexStringToByteArray(testVector[Tkey]), hexStringToByteArray("D8E7920AFA330A"), Integer.parseInt(testVector[Tradix]));
String pt = testVector[Tplaintext], ct = "477064185124354662";
String ciphertext = c.encrypt(pt);
String plaintext = c.decrypt(ciphertext);
assertEquals(ct, ciphertext);
assertEquals(pt, plaintext);
}

@Test
@EnabledOnJre(value = JRE.JAVA_11)
public void testCustomAlphabet() throws Exception {
Expand Down

0 comments on commit 257fc57

Please sign in to comment.