Skip to content

Instantly share code, notes, and snippets.

@iGio90
Last active April 2, 2019 01:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iGio90/41782cd43663cd9f2783bff171ef6f2f to your computer and use it in GitHub Desktop.
Save iGio90/41782cd43663cd9f2783bff171ef6f2f to your computer and use it in GitHub Desktop.
Clash of Clans public key hasher
package com.igio90;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.BitSet;
/**
* I've spent some nice times understanding the logic, I'm not even sure if it's an hasher built by sc
* nor if that code is the right representation of what is currently used on SC games.
* The following implementation has been built by translating gdb assembly into java.
* The tables are coming from Clash Of Clans, and the following code (not sure?) will probably work on
* Boom Beach and HayDay too (with different tables).
*
* There are possible ways to patch the table to generate a different kay to match any custom ones
* but Pinocchio is patching at an higher level which wouldn't need anything more.
*
*
* Created by igio90 on 14/09/17.
*/
public class SCHasher {
private static final char[] HEX_TABLE = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
private static final int[] SC_MAGIC_TABLE = new int[] {
0x03D1, 0x1030, 0x1993, 0x3CBB, 0x3E1C, 0xBCB5, 0x7AB4,
0xFB55, 0xCCE5, 0x3F07, 0xE5D0, 0x42E5, 0x0F09, 0xBD44,
0xCCF7
};
private static final int[] SC_AND_TABLE = new int[] {
0x8000, 0xC000, 0xE000, 0x8000, 0xF800, 0x0000, 0xC000,
0x0000, 0x0000, 0xE000, 0xF800, 0xC000, 0x8000, 0xC000,
0x0000
};
private static final int[] SC_SHIFT_TABLE = new int[] {
15, 14, 13, 15, 11, 0, 14, 0, 0, 13, 11, 14, 15, 14, 1
};
private static final int[] SC_BIT_INDEX_TABLE = new int[] {
1, 2, 3, 1, 5, 0, 2, 0, 0, 3, 5, 2, 1, 2, 0
};
private static final int SC_MAGIC_SEED = 0x7EB1;
public static void main(String args[]) {
String serverPubKey = unpack(SC_MAGIC_SEED);
}
static String unpack(int seed) {
int a = littleToBig(seed);
int b;
int c;
int d;
BitSet bt;
StringBuilder result = new StringBuilder();
result.append(toHexString(fromInt16(SC_MAGIC_SEED)));
for (int i=0;i<15;i++) {
b = SC_MAGIC_TABLE[i];
c = a ^ b;
d = SC_AND_TABLE[i] & c;
bt = BitSet.valueOf(fromInt16(ByteBuffer.wrap(fromInt16(c)).order(ByteOrder.LITTLE_ENDIAN).getShort()));
if (SC_AND_TABLE[i] > 0) {
int e = d >> SC_SHIFT_TABLE[i];
BitSet bt2 = BitSet.valueOf(fromInt16(ByteBuffer.wrap(fromInt16(e)).order(ByteOrder.LITTLE_ENDIAN).getShort()));
int k = 0;
int s = SC_BIT_INDEX_TABLE[i];
for (int m = s; m < 16; m++) {
bt2.set(m, bt.get(k));
k++;
}
a = toInt16(bt2.toByteArray());
} else if (SC_SHIFT_TABLE[i] > 0) {
b = SC_MAGIC_TABLE[i];
c = a ^ b;
d = c << SC_SHIFT_TABLE[i];
bt = BitSet.valueOf(fromInt16(ByteBuffer.wrap(fromInt16(d)).order(ByteOrder.LITTLE_ENDIAN).getShort()));
a = toInt16(bt.toByteArray());
} else {
a = toInt16(bt.toByteArray());
}
result.append(toHexString(fromInt16(a)));
a = littleToBig(a);
}
return result.toString();
}
static int littleToBig(int i) {
int b0,b1;
b0 = (i & 0x000000ff);
b1 = (i & 0x0000ff00) >> 8;
return (b0 << 8) | b1;
}
static byte[] fromInt16(int i) {
byte[] result = new byte[2];
result[0] = (byte) (i >> 8);
result[1] = (byte) (i);
return result;
}
public static int toInt16(byte[] b) {
return ByteBuffer.wrap(b).order(ByteOrder.BIG_ENDIAN).getShort();
}
public static String toHexString(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return "";
}
char[] hexChars = new char[bytes.length*2];
int v;
for(int j=0; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j*2] = HEX_TABLE[v>>>4];
hexChars[j*2 + 1] = HEX_TABLE[v & 0x0F];
}
return new String(hexChars);
}
}
Copy link

ghost commented Sep 14, 2017

gj

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment