Tags: crypto 

Rating:

I learnt from this writup: https://ctftime.org/writeup/14059

Solve it using my favorite language.

```
package org.zeroctf.babysponge;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solver {

private static final BigInteger maxValue = new BigInteger("18446744073709551615");

private static BigInteger ROL64(BigInteger a, int n) {
return a.shiftRight(64 - (n%64)).add(a.shiftLeft(n%64).and(maxValue));
}

private static BigInteger load64(int[] b) {
BigInteger result = new BigInteger("0");
for (int i = 0; i < 8; ++i) {
BigInteger B = new BigInteger(Integer.toString(b[i]));
result = result.add(B.shiftLeft(8*i));
}
return result;
}

private static int[] store64(BigInteger a) {
int[] result = new int[8];
BigInteger c = new BigInteger("255");
for (int i = 0; i < 8; ++i) {
result[i] = a.shiftRight(8*i).and(c).intValue();
}
return result;
}

private static void KeccakF1600onLanes(BigInteger[][] lanes) {
int R = 1;
for (int round = 0; round < 24; ++round) {
BigInteger[] C = new BigInteger[5];
for (int x = 0; x < 5; ++x) {
BigInteger item = new BigInteger("0");
for (int y = 0; y < 5; ++y) {
item = item.xor(lanes[x][y]);
}
C[x] = item;
}

BigInteger[] D = new BigInteger[5];
for (int x = 0; x < 5; ++x) {
D[x] = C[(x + 4)%5].xor(ROL64(C[(x + 1)%5], 1));
}

for (int x = 0; x < 5; ++x) {
for (int y = 0; y < 5; ++y) {
lanes[x][y] = lanes[x][y].xor(D[x]);
}
}

//printLanes(lanes);

for (int i = 0; i < 1; ++i) {
int x = 1;
int y = 0;
BigInteger current = lanes[x][y];
for (int t = 0; t < 24; ++t) {
int temp = x;
x = y;
y = (2*temp + 3*y)%5;
BigInteger temp2 = current;
current = lanes[x][y];
lanes[x][y] = ROL64(temp2, (t + 1)*(t + 2)/2);
}
}

//printLanes(lanes);

for (int y = 0; y < 5; ++y) {
BigInteger[] T = new BigInteger[5];
for (int x = 0; x < 5; ++x) {
T[x] = lanes[x][y];
}
for (int x = 0; x < 5; ++x) {
lanes[x][y] = T[x].xor(T[(x + 1)%5].xor(maxValue).and(T[(x + 2)%5]));
}
}

//printLanes(lanes);

for (int j = 0; j < 7; ++j) {
R = ((R << 1) ^ ((R >> 7) * 0x71)) & 0xff;
if ((R & 2) != 0) {
BigInteger B = new BigInteger("1");
BigInteger B2 = new BigInteger("1");
B = B.shiftLeft(j).subtract(new BigInteger("1"));
B = B2.shiftLeft(B.intValue());
lanes[0][0] = lanes[0][0].xor(B);
}
}

//printLanes(lanes);
}
}

private static int[] KeccakF1600(int[] state) {
BigInteger[][] lanes = new BigInteger[5][5];
for (int x = 0; x < 5; ++x) {
for (int y = 0; y < 5; ++y) {
int[] b = new int[8];
for (int k = 0; k < 8; ++k) {
b[k] = state[8*(x + 5*y) + k];
}
lanes[x][y] = load64(b);
}
}

//printLanes(lanes);
KeccakF1600onLanes(lanes);
//printLanes(lanes);

int[] result = new int[200];
for (int x = 0; x < 5; ++x) {
for (int y = 0; y < 5; ++y) {
int[] item = store64(lanes[x][y]);
for (int k = 0; k < 8; ++k) {
result[8*(x + 5*y) + k] = item[k];
}
}
}

return result;
}

private static int[] Keccak(int rate, int capacity, int[] inputBytes, int outputByteLen) {
int[] state = new int[200];
int rateInBytes = rate/8;
int blockSize = 0;
int inputOffset = 0;
while (inputOffset < inputBytes.length) {
blockSize = Math.min(inputBytes.length - inputOffset, rateInBytes);
for (int i = 0; i < blockSize; ++i) {
state[i] = state[i] ^ inputBytes[i + inputOffset];
}
inputOffset += blockSize;
if (blockSize == rateInBytes) {
state = KeccakF1600(state);
blockSize = 0;
}
}

int[] result = new int[outputByteLen];
System.arraycopy(state, 0, result, 0, outputByteLen);
return result;
}

private static int[] convert(int num, int outputLen) {
int[] result = new int[outputLen];
for (int i = 0; i < 4; ++i) {
result[outputLen - 1 - i] = num & 0xff;
num = num >> 8;
}

/*
for (int i = 4; i < outputLen; ++i) {
result[outputLen - 1 - i] = 0;
}*/

return result;
}

private static String arrToStr(int[] arr) {
StringBuffer sb = new StringBuffer();
for (int ch : arr) {
String hex = Integer.toHexString(ch);
if (hex.length() == 1) {
sb.append("0");
}
sb.append(Integer.toHexString(ch));
}
return sb.toString();
}

private static int[] arrXor(int[] m, int[] n) {
int[] result = new int[194];
for (int i = 0; i < 194; ++i) {
result[i] = m[i] ^ n[i];
}
return result;
}

private static void printArr(int[] arr) {
System.out.println("====printArr====");
System.out.println(arrToStr(arr));
System.out.println("====printArr====");
}

private static void printLanes(BigInteger[][] lanes) {
System.out.println("====printLanes====");
StringBuffer sb = new StringBuffer();
for (int x = 0; x < 5; ++x) {
for (int y = 0; y < 5; ++y) {
int[] t = store64(lanes[x][y]);
for (int i = 0; i < 8; ++i) {
String hex = Integer.toHexString(t[i]);
if (hex.length() == 1) {
sb.append("0");
}
sb.append(hex);
}
}
}
System.out.println(sb.toString());
System.out.println("====printLanes====");
}

public static void main(String[] args) {

Map<String, Integer> checkMap = new HashMap<String, Integer>();

for (int i = 0; i <= 2147483647; ++i) {
int[] inputBytes = convert(i, 194);
int[] state = Keccak(1552, 48, inputBytes, 200);
int[] capacity = new int[6];

System.arraycopy(state, 194, capacity, 0, 6);
String key = arrToStr(capacity);

if (checkMap.containsKey(key)) {
System.out.println("Found!\n");
int[] m = convert(checkMap.get(key), 194);
int[] n = convert(i, 194);
printArr(m);
printArr(n);

int[] exm1 = Keccak(1552, 48, m, 200);
int[] exm2 = Keccak(1552, 48, n, 200);
printArr(exm1);
printArr(exm2);

int[] ex = arrXor(exm1, exm2);
int[] res1 = new int[194*2];
int[] res2 = new int[194*2];

System.out.println("Result is:\n");
System.arraycopy(m, 0, res1, 0, 194);
System.arraycopy(n, 0, res2, 0, 194);
System.arraycopy(ex, 0, res2, 194, 194);
printArr(res1);
printArr(res2);
} else {
checkMap.put(key, i);
}
if (i % 10000 == 0) {
System.out.println(i);
}
}
}
}

```