I am trying to implement the Tiny Encryption Algorithm (TEA) in java. Since the algorithm divides each 64 bit block into left and right sub-blocks, where each sub-block is 32 unsigned integer according to this source.
As expected, I faced the issue of java not supporting unsigned 32 bit integer. I was getting errors about the number Format everywhere.
So I decided to use BigInteger, which introduced a new problem to me. Based on my understanding, since TEA uses shifting and addition of 32 bits integers, It is supposed to keep the result of these operation in 32 bits, so that the ciphertext will also be 32 bits per sub-block. However, with shifting and addition, BigInteger did not keep 32 bits per sub-block. Indeed, I got unexpectedly a very large number of bits as a ciphertext output.
So I decided to keep the BigInteger, while implementing the shift, addition, and subtraction in separate methods. Unfortunately, I am not getting correct results. As illustrated below, the decrypted ciphertext does not equal the original plaintext. What is the solution to my problem? I am getting the following output:
Original Plain Text:0x0123456789ABCDEF
CipherText:0xa0761126d09724fd
Decrypted CipherText is:0x8d5a4a234b3c6720
Below is my code.
import java.math.BigInteger;
public class TEA {
BigInteger [] K ; //128 bits key
private String plainText;
public static final BigInteger delta = new BigInteger("9e3779b9",16);
//constructor receives a string of plaintext and 128 bit key in hexadecimal
public TEA(String plainText, String key)
{
parseKey(key);
}
//constructor receives a hexadecimal
public TEA(String key)
{
parseKey(key);
}
//parses a 128 bit key, given in hexadecimal form, and store its value in 4 integers (total of 128 bits),
private void parseKey(String key)
{
if(key.substring(0,2).equals("0x"))
key= key.substring(2);
//validating input
if(key.length() != 32)
{
System.out.println("Invalid key size!");
return;
}
//dividing the key into 4 strings
String[] kStr = new String[4];
int index=-1;
for(int i=0; i<key.length(); i++)
{
if(i%8 == 0)
{
index++;
kStr[index]="";
}
kStr[index] = kStr[index] + key.charAt(i);
}
//converting the 4 hex strings into 4 integers
K= new BigInteger[4];
for(int i=0; i<4; i++)
K[i] = new BigInteger(kStr[i], 16);
}
//receives a plaintext block of 64 bits in hexadecimal to be encrypted
//returns the cipher block
String encryptBlock(String plainTextBlock)
{
if(plainTextBlock.substring(0,2).equals("0x"))
plainTextBlock= plainTextBlock.substring(2);
//validating input
if(plainTextBlock.length()!=16)
{
System.out.println("Invalid block size!");
return null;
}
//separating the string block into left and right blocks
String LStr = plainTextBlock.substring(0, 8); //left block (32 bit)
String RStr = plainTextBlock.substring(8); //right block (32 bit)
//converting left and right blocks to integers
BigInteger L = new BigInteger(LStr, 16);
BigInteger R = new BigInteger(RStr, 16);
BigInteger sum= new BigInteger("0");
//32 rounds
for(int i=0; i<32; i++)
{
sum = sum.add(delta);
L= sum(L, (sum(shiftLeft(R,4),K[0])) .xor(sum(R,sum)) .xor(sum(shiftRight(R,5),K[1]))) ;
R= sum(R, (sum(shiftLeft(L,4),K[2])) .xor(sum(L,sum)) .xor(sum(shiftRight(L,5),K[3]))) ;
//R= R.add( (shiftLeft(R,4).add(K[2])).xor(L.add(sum)).xor(shiftRight(L,5).add(K[3])) );
}
//joining back the blocks as hex
String cipherBlock = "0x"+L.toString(16)+R.toString(16)+"";
return cipherBlock;
}
//receives a ciphertext block of 64 bits in hexadecimal to be decrypted
//returns the plaintext block
String decryptBlock(String cipherBlock)
{
if(cipherBlock.substring(0,2).equals("0x"))
cipherBlock= cipherBlock.substring(2);
//validating input
if(cipherBlock.length()!=16)
{
System.out.println("Invalid block size!");
return null;
}
//separating the string block into left and right blocks
String LStr = cipherBlock.substring(0, 8); //left block (32 bit)
String RStr = cipherBlock.substring(8); //right block (32 bit)
//converting left and right blocks to integers
BigInteger L = new BigInteger(LStr, 16);
BigInteger R = new BigInteger(RStr, 16);
BigInteger sum= shiftLeft(delta,5);
//32 rounds
for(int i=0; i<32; i++)
{
R= subtract(R, (sum(shiftLeft(L,4),K[2])) .xor(sum(L,sum)) .xor(sum(shiftRight(L,5),K[3]))) ;
L= subtract(L, (sum(shiftLeft(R,4),K[0])) .xor(sum(R,sum)) .xor(sum(shiftRight(R,5),K[1]))) ;
//R= R.subtract( (L.shiftLeft(4).add(K[2])).xor(L.add(sum)).xor(L.shiftRight(5).add(K[3])) );
//L= L.subtract( (R.shiftLeft(4).add(K[0])).xor(R.add(sum)).xor(R.shiftRight(5).add(K[1])) );
sum = sum.subtract(delta);
}
//joining back the blocks as hex
String plainTextBlock = "0x"+L.toString(16)+R.toString(16)+"";
return plainTextBlock;
}
private BigInteger shiftLeft(BigInteger x, int steps)
{
BigInteger shifted=null;
boolean negative =false;
String xStr = x.toString(2);
//removing negative sign while shifting (currently)
if(xStr.charAt(0)=='-')
{
negative= true;
xStr = xStr.substring(1);
}
int additionalSize = 32- xStr.length();
for(int i=0; i<additionalSize; i++)
xStr= "0"+xStr;
for(int i=0; i<steps; i++)
{
xStr = xStr.substring(1);
xStr = xStr+"0";
}
//one last addition of negative sign if the number is negative
if(negative==true)
xStr= "-"+xStr;
//System.out.println(xStr);
shifted = new BigInteger(xStr,2);
return shifted;
}
private BigInteger shiftRight(BigInteger x, int steps)
{
BigInteger shifted=null;
boolean negative = false;
String xStr = x.toString(2);
//removing negative sign while shifting (currently)
if(xStr.charAt(0)=='-')
{
negative= true;
xStr = xStr.substring(1);
}
int additionalSize = 32- xStr.length();
for(int i=0; i<additionalSize; i++)
xStr= "0"+xStr;
for(int i=0; i<steps; i++)
{
xStr = xStr.substring(0,xStr.length()-1);
xStr = "0"+xStr;
}
//one last addition of negative sign if the number is negative
if(negative==true)
xStr= "-"+xStr;
shifted = new BigInteger(xStr,2);
return shifted;
}
private BigInteger sum(BigInteger a, BigInteger b)
{
BigInteger sum = a.add(b);
String sumStr = sum.toString(2);
if(sumStr.length()>32)
{
int diff = sumStr.length()- 32;
sumStr = sumStr.substring(diff);
}
BigInteger newSum = new BigInteger(sumStr,2);
return newSum;
}
private BigInteger subtract(BigInteger a, BigInteger b)
{
BigInteger sub = a.subtract(b);
String subStr = sub.toString(2);
if(subStr.length()>32)
{
int diff = subStr.length()- 32;
subStr = subStr.substring(diff);
}
BigInteger newSub = new BigInteger(subStr,2);
return newSub;
}
public static void main(String[] args)
{
String plainText="0x0123456789ABCDEF";
String key= "0xA56BABCD00000000FFFFFFFFABCDEF01";
TEA tea = new TEA(key);
String cipherText = tea.encryptBlock(plainText);
System.out.println("Original Plain Text:"+plainText);
System.out.println("CipherText:"+cipherText);
System.out.println("Decrypted CipherText is:"+tea.decryptBlock(cipherText));
}
}
I saw no reason to use BigIntegers, so I tried my own implementation in Java that is almost a verbatim copy of the C code in the wikipedia article. It looks correct to me, though I don't have tests to run against it.
public class TEAToy {
private final static int DELTA = 0x9e3779b9;
private final static int DECRYPT_SUM_INIT = 0xC6EF3720;
private final static long MASK32 = (1L << 32) - 1;
public static long encrypt(long in, int [] k) {
int v1 = (int) in;
int v0 = (int) (in >>> 32);
int sum = 0;
for (int i=0; i<32; i++) {
sum += DELTA;
v0 += ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>>5) + k[1]);
v1 += ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>>5) + k[3]);
}
return (v0 & MASK32) << 32 | (v1 & MASK32);
}
public static long decrypt(long in, int [] k) {
int v1 = (int) in;
int v0 = (int) (in >>> 32);
int sum = DECRYPT_SUM_INIT;
for (int i=0; i<32; i++) {
v1 -= ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>>5) + k[3]);
v0 -= ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>>5) + k[1]);
sum -= DELTA;
}
return (v0 & MASK32) << 32 | (v1 & MASK32);
}
As you can see, the fact the Java's ints are signed makes very little difference.
I am trying to generate all of the possible binary combinations for two bytes ex.
00000000 00000001
00000000 00000010
00000000 00000011
I have the class that I'm working on but its clearly not working at all. I cant get the method to return the output as it is generated.
I got the code below to work properly, but only for calculation 1 byte. How would I change this to calculate all of the possible outcomes for 2 bytes?
package referenceCode;
public class BinaryGenerator {
private int val = 0;
private int[] values = new int[]{0,1};
//This method converts the Binary Pattern output into a char[] so that it can be printed out to a file
public int[] binaryPatternToString() {
int numBits = 8;
values[0] = 0;
values[1] = 1;
int[] returned = null;
for (int i = 1; i < numBits; i++) {
returned = binaryGenerator(i);
for (int j = 1; j < numBits; j++) {
}
}
return returned;
}
private int[] binaryGenerator(int iVal) {
int[] moreValues = new int[values.length * 2];
int start = (int)Math.pow(2, iVal);
for (int j = 0; j < values.length; j++) {
moreValues[j * 2] = values[j] << 1;
moreValues[j * 2 + 1] = values[j] << 1 | 1;
}
values = moreValues;
for (int value : values) {
System.out.println(Integer.toBinaryString(value));
}
return moreValues;
}}
Would it be a better idea or more efficient to make it a recursive method instead of a method with a for loop?
As you may know all java Integers are based on binary numbers. So for 2 bytes, the maximum number is 2^16 = 65536. Simply loop through all numbers and get their binary values, zero-pad them if necessary and finally store them in a list. This will return all possible 2-byte binary numbers. For more bytes, simply increment the byte-variable.
Implementation:
int bytes = 2;
int nBits = bytes * 8;
int maxNumber = 1 << nBits; //this equals 2^nBits or in java: Math.pow(2,nbits)
ArrayList<String> binaries = new ArrayList<>();
for (int i = 0; i < maxNumber; i++) {
String binary = Integer.toBinaryString(i);
while (binary.length() != nBits) {
binary = "0" + binary;
}
binaries.add(binary);
}
System.out.println(binaries);
The bytes and nBits variables are included simply for clarity.
You can also use a recursive method. Start with an empty String and recursively add a 0 or a 1 to the start of the string and continue until you've reached the number of bits you wanted:
public static ArrayList<String> getBinaries(int bits, String current) {
ArrayList<String> binaries = new ArrayList<>();
if (current.length() == bits) {
binaries.add(current);
return binaries;
}
//pad a 0 and 1 in front of current;
binaries.addAll(getBinaries(bits, "0" + current));
binaries.addAll(getBinaries(bits, "1" + current));
return binaries;
}
You can call this function with: getBinaries(16,"") for 2 bytes.
I took the liberty of writing my own version so you can see a simpler way to produce these numbers.
The hardest part here is incrementing the list of booleans. In general it's just like adding 1. You increment the one's slot, and if it was already a 1, you move on to the 10s slot, and so on. Otherwise, you just loop through all the posobilities, printing each one out.
import java.util.ArrayList;
import java.util.List;
public class Sandbox {
// list of booleans to represent each bit
private static List<Boolean> bytes = new ArrayList<>();
public static void main(String[] args) {
// initialize the list to all false
for(int i = 0; i < 16; i++) {
bytes.add(false);
}
// calculate the number of permutations
int numPermutations = (int)Math.pow(2, 16);
// print the first permutation
print();
// loop through all permutations
for(int i = 0; i < numPermutations; i++) {
// increment the 2 bytes
increment();
// print the current permutation
print();
}
}
/**
* Prints out the current permutation
*/
private static void print() {
// loop through the bytes
for(Boolean bool : bytes) {
// print 1 or 0
if(bool)
System.out.print(1);
else
System.out.print(0);
}
// end the line
System.out.println();
}
/**
* Increment the bytes
*/
private static void increment() {
// set increment position to the end of the list
int position = bytes.size() - 1;
// loop through changing next digit if necessary, stopping
// if the front of the list is reached.
do {
bytes.set(position, !bytes.get(position));
} while(!bytes.get(position--) && position >= 0);
}
}
This is a homework assignment that I can't wrap my head around. I have to do it manually, so I can't use "getBytes()." Also, I have to convert to decimal format first, and then convert the decimal to ASCII (e.g. {0,1,1,0,0,0,1,0} = 98 in decimal format, which is a 'b'). I have arranged the binary code into an array, and want to use a for loop to traverse the array position by position. However, I'm not sure I'm using the correct parameters for the for loop, and am not sure how to divide the code into bits of "8." Another thing, how to I convert the decimal value to ASCII? Should I just list out all the possible letters I know I will get, and then refer to them using an if-else loop? Or could I just convert the decimal to a char? Here is my code so far... (It's a bit messy, sorry)
class Decoder
{
public void findCode(Picture stegoObj)
{
Pixel pixTar = new Pixel(stegoObj,0,0);
Pixel [] pixelArray = stegoObj.getPixels();
int blueVal = 0;
for(int length = 0; length < pixelArray.length; length++)
{
blueVal = pixTar.getBlue();
}
System.out.println(blueVal);
stegoObj.explore();
}
public void decode(int [] binary)
{
int binaryLen = binary.length;
int totVal = 0;
int newVal = 0;
int bitVal = 0;
for(int x = binaryLen - 1; x >= 0; x--)
{
bitVal = binary[x];
int exp = x - (binaryLen - 1);
totVal += (pow(bitVal, exp));
}
System.out.println(totVal);
}
}
public class DecodeImage
{
public static void main(String[] args)
{
Picture stegoObj = new Picture("SecretMessage.bmp");
Decoder deco = new Decoder();
int[] binArray = {0,1,0,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,0,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,0,0,1,0,0,1,1,0,1,1,0,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,1,0,0,1,0,1,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,1,1,0,1,1,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,1,1,0,0,1,1,0,0,1,0,1,0,0,0,0,1,1,0,1,0,0,0,0,1,0,1,0};
//int[] binArray = {0,1,1,0,0,0,1,0};
//deco.findCode(stegoObj);
deco.decode(binArray);
}
}
EDIT:
Okay, so I figured out this much, under the class Decoder, in the decode block, in the for loop:
for(int x = binaryLen - 1; x >= 0; x--)
{
bitVal = binary[x];
preVal = bitVal * base;
totVal += preVal;
base = base * 2;
}
You've got the right idea for decode. I don't see why your code wouldn't work, although I don't see the pow implementation anywhere.
Decimal to ascii is easy, just cast the value to a char:
int v = ...; // decimal value
char c = (char)v; // ascii value
int[] bValue = {1,0,0,0,1,0,1};
int iValue = 0;
// convert binary to decimal
for (int i = 0, pow = bValue.length - 1 ; i < bValue.length ; i++, pow--)
iValue += bValue[i]*Math.pow(2, pow);
// gets the value as a char
char cValue = (char)iValue;
System.out.println("Int value: "+iValue +", Char value : "+cValue);
If you need the whole ASCII table, you may put the values into a Map where the key is the integer value, and the value is the corresponding ASCII entry.
I need to store a couple binary sequences that are 16 bits in length into a byte array (of length 2). The one or two binary numbers don't change, so a function that does conversion might be overkill. Say for example the 16 bit binary sequence is 1111000011110001. How do I store that in a byte array of length two?
String val = "1111000011110001";
byte[] bval = new BigInteger(val, 2).toByteArray();
There are other options, but I found it best to use BigInteger class, that has conversion to byte array, for this kind of problems. I prefer if, because I can instantiate class from String, that can represent various bases like 8, 16, etc. and also output it as such.
Edit: Mondays ... :P
public static byte[] getRoger(String val) throws NumberFormatException,
NullPointerException {
byte[] result = new byte[2];
byte[] holder = new BigInteger(val, 2).toByteArray();
if (holder.length == 1) result[0] = holder[0];
else if (holder.length > 1) {
result[1] = holder[holder.length - 2];
result[0] = holder[holder.length - 1];
}
return result;
}
Example:
int bitarray = 12321;
String val = Integer.toString(bitarray, 2);
System.out.println(new StringBuilder().append(bitarray).append(':').append(val)
.append(':').append(Arrays.toString(getRoger(val))).append('\n'));
I have been disappointed with all of the solutions I have found to converting strings of bits to byte arrays and vice versa -- all have been buggy (even the BigInteger solution above), and very few are as efficient as they should be.
I realize the OP was only concerned with a bit string to an array of two bytes, which the BitInteger approach seems to work fine for. However, since this post is currently the first search result when searching "bit string to byte array java" in Google, I am going to post my general solution here for people dealing with huge strings and/or huge byte arrays.
Note that my solution below is the only solution I have ran that passes all of my test cases -- many online solutions to this relatively simple problem simply do not work.
Code
/**
* Zips (compresses) bit strings to byte arrays and unzips (decompresses)
* byte arrays to bit strings.
*
* #author ryan
*
*/
public class BitZip {
private static final byte[] BIT_MASKS = new byte[] {1, 2, 4, 8, 16, 32, 64, -128};
private static final int BITS_PER_BYTE = 8;
private static final int MAX_BIT_INDEX_IN_BYTE = BITS_PER_BYTE - 1;
/**
* Decompress the specified byte array to a string.
* <p>
* This function does not pad with zeros for any bit-string result
* with a length indivisible by 8.
*
* #param bytes The bytes to convert into a string of bits, with byte[0]
* consisting of the least significant bits in the byte array.
* #return The string of bits representing the byte array.
*/
public static final String unzip(final byte[] bytes) {
int byteCount = bytes.length;
int bitCount = byteCount * BITS_PER_BYTE;
char[] bits = new char[bitCount];
{
int bytesIndex = 0;
int iLeft = Math.max(bitCount - BITS_PER_BYTE, 0);
while (bytesIndex < byteCount) {
byte value = bytes[bytesIndex];
for (int b = MAX_BIT_INDEX_IN_BYTE; b >= 0; --b) {
bits[iLeft + b] = ((value % 2) == 0 ? '0' : '1');
value >>= 1;
}
iLeft = Math.max(iLeft - BITS_PER_BYTE, 0);
++bytesIndex;
}
}
return new String(bits).replaceFirst("^0+(?!$)", "");
}
/**
* Compresses the specified bit string to a byte array, ignoring trailing
* zeros past the most significant set bit.
*
* #param bits The string of bits (composed strictly of '0' and '1' characters)
* to convert into an array of bytes.
* #return The bits, as a byte array with byte[0] containing the least
* significant bits.
*/
public static final byte[] zip(final String bits) {
if ((bits == null) || bits.isEmpty()) {
// No observations -- return nothing.
return new byte[0];
}
char[] bitChars = bits.toCharArray();
int bitCount = bitChars.length;
int left;
for (left = 0; left < bitCount; ++left) {
// Ignore leading zeros.
if (bitChars[left] == '1') {
break;
}
}
if (bitCount == left) {
// Only '0's in the string.
return new byte[] {0};
}
int cBits = bitCount - left;
byte[] bytes = new byte[((cBits) / BITS_PER_BYTE) + (((cBits % BITS_PER_BYTE) > 0) ? 1 : 0)];
{
int iRight = bitCount - 1;
int iLeft = Math.max(bitCount - BITS_PER_BYTE, left);
int bytesIndex = 0;
byte _byte = 0;
while (bytesIndex < bytes.length) {
while (iLeft <= iRight) {
if (bitChars[iLeft] == '1') {
_byte |= BIT_MASKS[iRight - iLeft];
}
++iLeft;
}
bytes[bytesIndex++] = _byte;
iRight = Math.max(iRight - BITS_PER_BYTE, left);
iLeft = Math.max((1 + iRight) - BITS_PER_BYTE, left);
_byte = 0;
}
}
return bytes;
}
}
Performance
I was bored at work so I did some performance testing comparing against the accepted answer here for when N is large. (Pretending to ignore the fact that the BigInteger approach posted above doesn't even work properly as a general approach.)
This is running with a random bit string of size 5M and a random byte array of size 1M:
String -> byte[] -- BigInteger result: 39098ms
String -> byte[] -- BitZip result: 29ms
byte[] -> String -- Integer result: 138ms
byte[] -> String -- BitZip result: 71ms
And the code:
public static void main(String[] argv) {
int testByteLength = 1000000;
int testStringLength = 5000000;
// Independently random.
final byte[] randomBytes = new byte[testByteLength];
final String randomBitString;
{
StringBuilder sb = new StringBuilder();
Random rand = new Random();
for (int i = 0; i < testStringLength; ++i) {
int value = rand.nextInt(1 + i);
sb.append((value % 2) == 0 ? '0' : '1');
randomBytes[i % testByteLength] = (byte) value;
}
randomBitString = sb.toString();
}
byte[] resultCompress;
String resultDecompress;
{
Stopwatch s = new Stopwatch();
TimeUnit ms = TimeUnit.MILLISECONDS;
{
s.start();
{
resultCompress = compressFromBigIntegerToByteArray(randomBitString);
}
s.stop();
{
System.out.println("String -> byte[] -- BigInteger result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
{
s.start();
{
resultCompress = zip(randomBitString);
}
s.stop();
{
System.out.println("String -> byte[] -- BitZip result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
{
s.start();
{
resultDecompress = decompressFromIntegerParseInt(randomBytes);
}
s.stop();
{
System.out.println("byte[] -> String -- Integer result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
{
s.start();
{
resultDecompress = unzip(randomBytes);
}
s.stop();
{
System.out.println("byte[] -> String -- BitZip result: " + s.elapsed(ms) + "ms");
}
s.reset();
}
}
}