-
-
Save lovromazgon/007acc8a96a5f65c7194 to your computer and use it in GitHub Desktop.
Rabbit Stream Cipher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @see {@link http://tools.ietf.org/rfc/rfc4503.txt} | |
*/ | |
public class Rabbit { | |
private static final int[] A = new int[] { 0x4D34D34D, 0xD34D34D3, 0x34D34D34, 0x4D34D34D, 0xD34D34D3, 0x34D34D34, 0x4D34D34D, 0xD34D34D3 }; | |
private static final long MAX_UNSIGNED_INT = Integer.MAX_VALUE * 2l + 2; //2^32 | |
private static final boolean DEBUG = false; | |
private int[] X; | |
private int[] C; | |
private byte b; | |
private byte[] keyStream; | |
private int keyIndex; | |
public Rabbit() { | |
reset(); | |
} | |
public void reset() { | |
b = 0; | |
X = new int[8]; | |
C = new int[8]; | |
keyStream = null; | |
keyIndex = 0; | |
} | |
public byte[] crypt(byte[] data) { | |
byte[] result = new byte[data.length]; | |
for (int i = 0; i < data.length; i++) { | |
if (keyStream == null || keyIndex == 16) { | |
extractKeyStream(); | |
keyIndex = 0; | |
if (DEBUG) { | |
System.out.println("Key Stream:"); | |
for (byte k : keyStream) { | |
System.out.print(Integer.toHexString(Byte.toUnsignedInt(k)) + " "); | |
} | |
System.out.println(); | |
} | |
} | |
result[i] = (byte) ((data[i] ^ keyStream[keyIndex++]) & 0xff); | |
} | |
return result; | |
} | |
public void extractKeyStream() { | |
nextState(); | |
keyStream = new byte[16]; | |
int temp; | |
for (int j = 0; j < 8; j++) { | |
if (j%2 == 0) { | |
temp = ((X[6-j] >> 16) & 0xFFFF) ^ (X[(9-j) % 8] & 0xFFFF); | |
} | |
else { | |
temp = (X[7-j] & 0xFFFF) ^ ((X[(12-j) % 8] >> 16) & 0xFFFF); | |
} | |
keyStream[2*j] = (byte)((temp >> 8) & 0xFF); | |
keyStream[2*j + 1] = (byte)(temp & 0xFF); | |
} | |
} | |
public void setupKey(byte[] input) { | |
if (input.length != 16) { | |
throw new IllegalArgumentException("I need a 128-bit key"); | |
} | |
int[] key = new int[8]; | |
for (int j = 0; j < 8; j++) { | |
key[-j+7] = (Byte.toUnsignedInt(input[2*j]) << 8) | (Byte.toUnsignedInt(input[2*j+1])); | |
} | |
for (int j = 0; j < 8; j++) { | |
if (j%2 == 0) { | |
X[j] = key[(j+1) % 8] << 16 | key[j]; | |
C[j] = key[(j+4) % 8] << 16 | key[(j+5) % 8]; | |
} | |
else { | |
X[j] = key[(j+5) % 8] << 16 | key[(j+4) % 8]; | |
C[j] = key[j] << 16 | key[(j+1) % 8]; | |
} | |
} | |
nextState(); | |
nextState(); | |
nextState(); | |
nextState(); | |
for (int j = 0; j < 8; j++) { | |
C[j] = C[j] ^ X[(j+4) % 8]; | |
} | |
if (DEBUG) { | |
System.out.println("After key setup:"); | |
printX(); | |
printC(); | |
} | |
} | |
public void setupIV(byte[] input) { | |
if (input.length != 8) { | |
throw new IllegalArgumentException("I need a 64-bit iv"); | |
} | |
int[] iv = new int[4]; | |
for (int j = 0; j < 4; j++) { | |
iv[3-j] = (Byte.toUnsignedInt(input[2*j]) << 8) | (Byte.toUnsignedInt(input[2*j+1])); | |
} | |
for (int j = 0; j < 8; j++) { | |
C[j] ^= iv[j%4 == 1 ? 3 : (9-j)%4] << 16 | iv[j%4 == 3 ? 0 : j%4] & 0xFFFF; | |
} | |
nextState(); | |
nextState(); | |
nextState(); | |
nextState(); | |
if (DEBUG) { | |
System.out.println("After iv setup:"); | |
printX(); | |
printC(); | |
} | |
} | |
private void nextState() { | |
updateCounter(); | |
int G[] = new int[8]; | |
for(int j = 0; j < 8; j++) { | |
long t = X[j] + C[j] & 0xFFFFFFFFL; | |
G[j] = (int)(((t * t) ^ ((t * t) >> 32)) % MAX_UNSIGNED_INT); | |
} | |
for (int j = 0; j< 8; j++) { | |
int j1 = (((j-1) % 8) + 8) % 8; | |
int j2 = (((j-2) % 8) + 8) % 8; | |
if (j%2 == 0) { | |
X[j] = G[j] + rotl(G[j1], 16) + rotl(G[j2], 16); | |
} | |
else { | |
X[j] = G[j] + rotl(G[j1], 8) + G[j2]; | |
} | |
} | |
} | |
private void updateCounter() { | |
for (int j = 0; j < 8; j++) { | |
long temp = (C[j] & 0xFFFFFFFFL) + (A[j] & 0xFFFFFFFFL) + b; | |
b = (temp >= MAX_UNSIGNED_INT) ? (byte)1 : 0; | |
C[j] = (int) (temp % MAX_UNSIGNED_INT); | |
} | |
} | |
public void printX() { | |
System.out.println("------"); | |
for (int i = 0; i < 8; i++) { | |
System.out.println("X" + i + " = " + Integer.toHexString(X[i])); | |
} | |
System.out.println("------"); | |
} | |
public void printC() { | |
System.out.println("------"); | |
for (int i = 0; i < 8; i++) { | |
System.out.println("C" + i + " = " + Integer.toHexString(C[i])); | |
} | |
System.out.println("------"); | |
} | |
private static int rotl(int val, int pas) { | |
return (val << pas) | (val >>> (32 - pas)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.Assert; | |
import org.junit.Test; | |
public class RabbitTest { | |
private static final byte[] INPUT = convertData( | |
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " + | |
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " + | |
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); | |
private static final String[][] TEST_DATA = { | |
{ | |
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", | |
null, | |
"B1 57 54 F0 36 A5 D6 EC F5 6B 45 26 1C 4A F7 02 " + | |
"88 E8 D8 15 C5 9C 0C 39 7B 69 6C 47 89 C6 8A A7 " + | |
"F4 16 A1 C3 70 0C D4 51 DA 68 D1 88 16 73 D6 96" | |
}, | |
{ | |
"91 28 13 29 2E 3D 36 FE 3B FC 62 F1 DC 51 C3 AC", | |
null, | |
"3D 2D F3 C8 3E F6 27 A1 E9 7F C3 84 87 E2 51 9C " + | |
"F5 76 CD 61 F4 40 5B 88 96 BF 53 AA 85 54 FC 19 " + | |
"E5 54 74 73 FB DB 43 50 8A E5 3B 20 20 4D 4C 5E" | |
}, | |
{ | |
"83 95 74 15 87 E0 C7 33 E9 E9 AB 01 C0 9B 00 43", | |
null, | |
"0C B1 0D CD A0 41 CD AC 32 EB 5C FD 02 D0 60 9B " + | |
"95 FC 9F CA 0F 17 01 5A 7B 70 92 11 4C FF 3E AD " + | |
"96 49 E5 DE 8B FC 7F 3F 92 41 47 AD 3A 94 74 28" | |
}, | |
{ | |
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", | |
"00 00 00 00 00 00 00 00", | |
"C6 A7 27 5E F8 54 95 D8 7C CD 5D 37 67 05 B7 ED " + | |
"5F 29 A6 AC 04 F5 EF D4 7B 8F 29 32 70 DC 4A 8D " + | |
"2A DE 82 2B 29 DE 6C 1E E5 2B DB 8A 47 BF 8F 66" | |
}, | |
{ | |
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", | |
"C3 73 F5 75 C1 26 7E 59", | |
"1F CD 4E B9 58 00 12 E2 E0 DC CC 92 22 01 7D 6D " + | |
"A7 5F 4E 10 D1 21 25 01 7B 24 99 FF ED 93 6F 2E " + | |
"EB C1 12 C3 93 E7 38 39 23 56 BD D0 12 02 9B A7" | |
}, | |
{ | |
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", | |
"A6 EB 56 1A D2 F4 17 27", | |
"44 5A D8 C8 05 85 8D BF 70 B6 AF 23 A1 51 10 4D " + | |
"96 C8 F2 79 47 F4 2C 5B AE AE 67 C6 AC C3 5B 03 " + | |
"9F CB FC 89 5F A7 1C 17 31 3D F0 34 F0 15 51 CB" | |
} | |
}; | |
@Test | |
public void test() { | |
Rabbit r = new Rabbit(); | |
int i = 0; | |
boolean error = false; | |
for (String[] testData : TEST_DATA) { | |
r.reset(); | |
byte[] key = convertData(testData[0]); | |
byte[] iv = convertData(testData[1]); | |
byte[] expectedOut = convertData(testData[2]); | |
r.setupKey(key); | |
if (iv != null) { | |
r.setupIV(iv); | |
} | |
byte[] out = r.crypt(INPUT); | |
try { | |
Assert.assertArrayEquals(expectedOut, out); | |
System.out.println(++i + ": SUCCESS"); | |
} catch (AssertionError e) { | |
error = true; | |
System.out.println(++i + ": FAILED"); | |
System.out.println("\tExpected: " + convertData(expectedOut)); | |
System.out.println("\tActual: " + convertData(out)); | |
} | |
} | |
if (error) { | |
Assert.fail("Some tests failed."); | |
} | |
} | |
private static byte[] convertData(String data) { | |
if (data == null || data.length() == 0) { | |
return null; | |
} | |
byte[] array = new byte[(data.length() + 1) / 3]; | |
int i = 0; | |
for(String value : data.split(" ")) { | |
array[i++] = (byte) (Integer.parseInt(value, 16) & 0xFF); | |
} | |
return array; | |
} | |
private static String convertData(byte[] data) { | |
StringBuilder sb = new StringBuilder(); | |
for (byte b : data) { | |
sb.append(" "); | |
String hex = Integer.toHexString(Byte.toUnsignedInt(b)); | |
if (hex.length() == 1) { | |
sb.append(" "); | |
} | |
sb.append(hex); | |
} | |
sb.deleteCharAt(0); | |
return sb.toString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment