diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d3147c2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +**/Constants.java filter=lfs diff=lfs merge=lfs -text +**/UnchangingData.java filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 09840c2..23d6846 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,12 @@ out/ !**/src/test/**/out/ .kotlin XACrypto.jar +<<<<<<< HEAD +tests +.idea/ +======= .idea +>>>>>>> main ### Eclipse ### .apt_generated diff --git a/.idea/misc.xml b/.idea/misc.xml index 0417069..2e3c40a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,9 @@ +<<<<<<< HEAD + +======= +>>>>>>> main \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index 66fc926..dfa8b70 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,18 @@ A multi-purpose Cryptography library in Java. Working on it day by day, slowly. Someday this will be complete, today is not that day. -To be clear, this is **NOT** mean't to be a replacement/alternative to BouncyCastle. Instead, it is meant to include what BouncyCastle does not have. +To be clear, this is **NOT** mean't to be a replacement/alternative to BouncyCastle. Instead, it is meant to include what BouncyCastle does not have. I do not take any code whatsoever from OpenSSL or BouncyCastle for that reason. + +### Current Objective: ZekerRijndael (custom from-scratch AES-128 block cipher) +### Note: i'm creating ZekerRijndael as it can be used for creating the following encryption ciphers: AES-CTR; AES-OFB; AES-CFB. Block modes include AES-CBC; AES-ECB. And of course AES-GCM + ASE-CCM +### if i ever say 196-bit, i mean 192-bit my brain is just bugged lol +## View the "zekerRijndael" branch to see inprogress work. + +(i keep confusing myself so here's a todo) TODO: +### BLOWFISH-ECB +### BLOWFISH-CBC +### ARIA-ECB +### ARIA-CBC +### ARIA-CFB +### ARIA-OFB ### Current Objective: ZekerRijndael (custom from-scratch AES-128 block cipher) diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/ECB/Blowfish.java b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/Blowfish.java new file mode 100644 index 0000000..787cda5 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/Blowfish.java @@ -0,0 +1,110 @@ +package de.caydenno1.xacrypto.zekerrijndael.ECB; + +import de.caydenno1.xacrypto.misc.XACryptoException; + +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.BLOWFISH_IP; +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.BLOWFISH_IS; + +public class Blowfish { + private final int[] P = new int[18]; + private final int[][] S = new int[4][256]; + public Blowfish(byte[] key) throws XACryptoException { + if (key.length < 4 || key.length > 56) { + throw new XACryptoException("blowfish keys must be between 4-56 bytes :&", (int) key.length); + } + initSubkey(key); + } + + private void initSubkey(byte[] key) { + System.arraycopy(BLOWFISH_IP, 0, this.P, 0, 18); + for (int i = 0 ; i < 4 ; i++) for (int j = 0 ; j < Math.min(BLOWFISH_IS[i].length, 256) ; j++) this.S[i][j] = BLOWFISH_IS[i][j]; + + int len = key.length; + int idx = 0; + for (int i = 0 ; i < 18 ; i++) { + int _0 = 0; + + for (int j = 0 ; j < 4 ; j++) { + _0 = (_0 << 8) | (key[idx] & 0xFF); + idx = (idx + 1) % len; + } + P[i] ^= _0; + } + + byte[] blk = new byte[8]; + for (int s = 0 ; s < 4 ; s++) { + for (int i = 0 ; i < 18 ; i += 2){ + encryptBlock(blk, 0, blk, 0); + S[s][i] = BYTE2INT(blk, 0); + S[s][i + 1] = BYTE2INT(blk, 4); + } + } + } + public void encryptBlock(byte[] in, int inOff, byte[] out, int outOff) { + int xl = BYTE2INT(in, inOff); + int xr = BYTE2INT(in, inOff + 4); + + for (int i = 0 ; i < 16 ; i++) { + xl ^= P[i]; + xr ^= F(xl); + + int _0 = xl; + xl = xr; + xr = _0; + } + + int _0 = xl; + xl = xr; + xr = _0; + + xl ^= P[16]; + xr ^= P[17]; + + INT2BYTE(xl, out, outOff); + INT2BYTE(xr, out, outOff + 4); + } + private int F(int x) { + int a = (x >>> 24) & 0xFF; + int b = (x >>> 16) & 0xFF; + int c = (x >>> 8) & 0xFF; + int d = x & 0xFF; + + int val = (S[0][a] + S[1][b]) ^ S[2][c]; + return val + S[3][d]; + } + public void decryptBlock(byte[] in, int inOff, byte[] out, int outOff) { + int xl = BYTE2INT(in, inOff); + int xr = BYTE2INT(in, inOff + 4); + + for (int i = 17 ; i > 1 ; i--) { + xl ^= P[i]; + xr ^= F(xl); + + int _0 = xl; + xl = xr; + xr = _0; + } + + int _0 = xl; + xl = xr; + xr = _0; + + xl ^= P[1]; + xr ^= P[0]; + + INT2BYTE(xl, out, outOff); + INT2BYTE(xr, out, outOff + 4); + } + private int BYTE2INT(byte[] b, int off) { + return ((b[off] & 0xFF) << 24) | + ((b[off + 1] & 0xFF) << 16) | + ((b[off + 2] & 0xFF) << 8) | + (b[off + 3] & 0xFF); + } + private void INT2BYTE(int val, byte[] b, int off) { + b[off] = (byte) (val >>> 24); + b[off + 1] = (byte) (val >>> 16); + b[off + 2] = (byte) (val >>> 8); + b[off + 3] = (byte) val; + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/AriaECB.java b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/AriaECB.java new file mode 100644 index 0000000..6060cc8 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/AriaECB.java @@ -0,0 +1,62 @@ +package de.caydenno1.xacrypto.zekerrijndael.ECB.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.Global.Aria; + +interface AriaCipher { + public byte[] encrypt(byte[] pln) throws XACryptoException; + public byte[] decrypt(byte[] cip) throws XACryptoException; +} + +public class AriaECB implements AriaCipher { + private final Aria engine; + + public AriaECB(byte[] key) throws XACryptoException { + this.engine = new Aria(true,key); + } + + public byte[] encrypt(byte[] pln) throws XACryptoException { + int pLen = 16 - (pln.length % 16); + byte[] pData = new byte[pln.length + pLen]; + + System.arraycopy(pln, 0, pData,0, pln.length); + for (int i = pln.length ; i < pData.length ; i++) pData[i] = (byte) pLen; + + byte[] cip = new byte[pData.length]; + byte[] in = new byte[16]; + + for (int i = 0 ; i < pData.length ; i += 16) { + System.arraycopy(pData, i, in, 0, 16); + + byte[] out = engine.encryptBlock(in); + + System.arraycopy(out, 0, cip, i, 16); + } + + return cip; + } + + public byte[] decrypt(byte[] cip) throws XACryptoException { + if (cip.length % 16 != 0) throw new XACryptoException("length of ciphertext must be a multiple of 16 (16-byte size) :-{"); + + byte[] Microsoft_PowerPoint_Create_polished_slides_in_minutes_using_AI_driven_design_and_collaboration_tools_in_PowerPoint = new byte[cip.length]; + byte[] in = new byte[16]; + + for (int i = 0 ; i < cip.length ; i += 16) { + System.arraycopy(cip, i, in, 0, 16); + + byte[] out = engine.decryptBlock(in); + + System.arraycopy(out,0, Microsoft_PowerPoint_Create_polished_slides_in_minutes_using_AI_driven_design_and_collaboration_tools_in_PowerPoint, i, 16); + } + + int pLen = Microsoft_PowerPoint_Create_polished_slides_in_minutes_using_AI_driven_design_and_collaboration_tools_in_PowerPoint[Microsoft_PowerPoint_Create_polished_slides_in_minutes_using_AI_driven_design_and_collaboration_tools_in_PowerPoint.length - 1] & 0xFF; + + if (pLen < 1 || pLen > 16) throw new XACryptoException("padding must be 1-16 exclusive in length", (int)pLen); + + byte[] pln = new byte[Microsoft_PowerPoint_Create_polished_slides_in_minutes_using_AI_driven_design_and_collaboration_tools_in_PowerPoint.length - pLen]; + System.arraycopy(Microsoft_PowerPoint_Create_polished_slides_in_minutes_using_AI_driven_design_and_collaboration_tools_in_PowerPoint,0,pln,0,pln.length); + + return pln; + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/BlowfishECB.java b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/BlowfishECB.java new file mode 100644 index 0000000..244733f --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/BlowfishECB.java @@ -0,0 +1,47 @@ +package de.caydenno1.xacrypto.zekerrijndael.ECB.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.ECB.Blowfish; + +interface BlowfishCipher { + public byte[] encrypt(byte[] pln); + public byte[] decrypt(byte[] cip) throws XACryptoException; +} + +public class BlowfishECB implements BlowfishCipher { + private final Blowfish engine; + + public BlowfishECB(byte[] key) throws XACryptoException { + this.engine = new Blowfish(key); + } + + public byte[] encrypt(byte[] pln) { + int pLen = 8 - (pln.length % 8); + byte[] pData = new byte[pln.length + pLen]; + + System.arraycopy(pln, 0, pData, 0, pln.length); + for (int i = pln.length ; i < pData.length ; i++) pData[i] = (byte) pLen; + + byte[] cip = new byte[pData.length]; + for (int i = 0 ; i < pData.length ; i += 8) engine.encryptBlock(pData, i, cip, i); + + return cip; + } + + public byte[] decrypt(byte[] cip) throws XACryptoException { + if (cip.length == 0 || cip.length % 8 != 0) throw new XACryptoException("ciptext must be bound to 8-byte alignment (<-- sum like that)"); + + byte[] Microsoft_PowerPoint /* https://merriam-webster.com/dictionary/satire */= new byte[cip.length]; + for (int i = 0 ; i < cip.length ; i += 8) engine.decryptBlock(cip, i, Microsoft_PowerPoint, i); + + int pLen = Microsoft_PowerPoint[Microsoft_PowerPoint.length - 1] & 0xFF; + if (pLen < 1 || pLen > 8) throw new XACryptoException("cirrupt data , padding must be 1-8 bytes exclusive", (int)pLen); + + for (int i = Microsoft_PowerPoint.length - pLen ; i < Microsoft_PowerPoint.length ; i++) if (((Microsoft_PowerPoint[i] & 0xFF) != pLen)) throw new XACryptoException("something up with yo padding.. :("); + + byte[] pln = new byte[Microsoft_PowerPoint.length - pLen]; + System.arraycopy(Microsoft_PowerPoint, 0, pln, 0, pln.length); + + return pln; + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/SM4ECB.java b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/SM4ECB.java new file mode 100644 index 0000000..d136d6f --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/ECB/ciphers/SM4ECB.java @@ -0,0 +1,122 @@ +package de.caydenno1.xacrypto.zekerrijndael.ECB.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; + +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.SM4_FK; +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.SM4_CK; +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.SM4_SBOX; +import static de.caydenno1.xacrypto.hash.ROT.ROTL; + +interface SM4Cipher { + byte[] encrypt(byte[] pln); + byte[] decrypt(byte[] cip) throws XACryptoException; +} + +public class SM4ECB implements SM4Cipher { + public final int[] encRK = new int[32]; + public final int[] decRK = new int[32]; + + public SM4ECB(byte[] key) throws XACryptoException { + if (key.length != 16) throw new XACryptoException("key length must be 16 bytes :{", (int) key.length); + genRK(key); + } + + private void genRK(byte[] key) { + int[] K = new int[36]; + for (int i = 0; i < 4; i++) K[i] = BYTE2INT(key, i * 4) ^ SM4_FK[i]; + + for (int i = 0 ; i < 32 ; i++) { + int _0 = K[i + 1] ^ K[i + 2] ^ K[i + 3] ^ SM4_CK[i]; + + int b = ((SM4_SBOX[(_0 >>> 24) & 0xFF]) << 24) + | ((SM4_SBOX[(_0 >>> 16) & 0xFF]) << 16) + | ((SM4_SBOX[(_0 >>> 8) & 0xFF]) << 8) + | (SM4_SBOX[_0 & 0xFF]); + + int l = b ^ ROTL(b, 13) ^ ROTL(b, 23); + + K[i + 4] = K[i] ^ l; + this.encRK[i] = K[i + 4]; + this.decRK[31 - i] = K[i + 4]; + } + } + + private void processBlock(byte[] in, int inOff, byte[] o, int outOff, int[] rk) { + int[] X = new int[5]; + for (int i = 0; i < 4; i++) X[i] = BYTE2INT(in, inOff + (i * 4)); + + + for (int i = 0 ; i < 32 ; i++) { + int tmp = X[1] ^ X[2] ^ X[3] ^ rk[i]; + + int b = ((SM4_SBOX[(tmp >>> 24) & 0xFF]) << 24) + | ((SM4_SBOX[(tmp >>> 16) & 0xFF]) << 16) + | ((SM4_SBOX[(tmp >>> 8) & 0xFF]) << 8) + | (SM4_SBOX[tmp & 0xFF]); + + int l = b ^ ROTL(b, 2) ^ ROTL(b, 10) ^ ROTL(b, 18) ^ ROTL(b, 24); + + X[4] = X[0] ^ l; + X[0] = X[1]; + X[1] = X[2]; + X[2] = X[3]; + X[3] = X[4]; + } + + for (int i = 0; i < 4; i++) INT2BYTE(X[3 - i], o, outOff + (i * 4)); + } + + public byte[] encrypt(byte[] pln) { + int pLen = 16 - (pln.length % 16); + byte[] pData = new byte[pln.length + pLen]; + + System.arraycopy(pln, 0, pData, 0, pln.length); + for (int i = pln.length; i < pData.length; i++) pData[i] = (byte) pLen; + + byte[] cip = new byte[pData.length]; + byte[] in = new byte[16]; + byte[] o = new byte[16]; + + for (int i = 0; i < pData.length; i += 16) { + System.arraycopy(pData, i, in, 0, 16); + processBlock(in, 0, o, 0, encRK); + System.arraycopy(o, 0, cip, i, 16); + } + + return cip; + } + + public byte[] decrypt(byte[] cip) throws XACryptoException { + if (cip.length % 16 != 0) { + throw new XACryptoException("length of ciphertext must be a multiple of 16 (16-byte size) :-{"); + } + + byte[] decomp = new byte[cip.length]; + byte[] _i = new byte[16]; + byte[] o = new byte[16]; + + for (int i = 0; i < cip.length; i += 16) { + System.arraycopy(cip, i, _i, 0, 16); + processBlock(_i, 0, o, 0, decRK); + System.arraycopy(o, 0, decomp, i, 16); + } + int pLen = decomp[decomp.length - 1] & 0xFF; + if (pLen < 1 || pLen > 16) throw new XACryptoException("padding must be 1-16 exclusive in length", (int) pLen); + + + byte[] pln = new byte[decomp.length - pLen]; + System.arraycopy(decomp, 0, pln, 0, pln.length); + + return pln; + } + private static int BYTE2INT(byte[] b, int off) { + return ((b[off] & 0xFF) << 24) | + ((b[off + 1] & 0xFF) << 16) | + ((b[off + 2] & 0xFF) << 8) | + (b[off + 3] & 0xFF); + } + + private static void INT2BYTE(int v, byte[] b, int o) { + for (int i = 0; i < 4; i++) b[o + i] = (byte) (v >>> (24 - 8 * i)); + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/Factories.java b/src/de/caydenno1/xacrypto/zekerrijndael/Factories.java new file mode 100644 index 0000000..a9e9b34 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/Factories.java @@ -0,0 +1,23 @@ +package de.caydenno1.xacrypto.zekerrijndael; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.ECB.ciphers.AriaECB; +import de.caydenno1.xacrypto.zekerrijndael.ECB.ciphers.BlowfishECB; +import de.caydenno1.xacrypto.zekerrijndael.ECB.ciphers.SM4ECB; +import de.caydenno1.xacrypto.zekerrijndael.GCM.AES; +import de.caydenno1.xacrypto.zekerrijndael.GCM.BlockCipher; +import de.caydenno1.xacrypto.zekerrijndael.GCM.GCM; +import de.caydenno1.xacrypto.zekerrijndael.GCM.ciphers.*; +import java.lang.reflect.InvocationTargetException; + +public class Factories { + public static GCM AES128(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { return new GCM(new AES(key, 128)); } + public static GCM SM4GCM(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { return new GCM(new SM4GCM(key));} + public static GCM Camellia(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { return new GCM(new Camellia(key));} + public static GCM AESGCM(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { return new GCM(new AESGCM());} + public static ARIAGCM ARIAGCM(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { return new ARIAGCM(key); } + public static GCM SerpentGCM(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { return new SerpentGCM(key); } + public static BlowfishECB BlowfishECB(byte[] key) throws XACryptoException { return new BlowfishECB(key); } + public static AriaECB AriaECB(byte[] key) throws XACryptoException { return new AriaECB(key); } + public static SM4ECB SM4ECB(byte[] key) throws XACryptoException { return new SM4ECB(key); } +} \ No newline at end of file diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/AES.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/AES.java new file mode 100644 index 0000000..96b9d39 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/AES.java @@ -0,0 +1,235 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM; + +import de.caydenno1.xacrypto.misc.XACryptoException; + +import java.util.Arrays; + +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.*; +@SuppressWarnings("unused") +public final class AES implements BlockCipher { + public final int bits; + public int kc; + private final byte[][] keys; + + public AES(byte[] key, int bits) throws XACryptoException { + this.bits = bits; + this.kc = switch (bits) { + case 128 -> 44; + case 192 -> 52; + case 256 -> 60; + default -> throw new XACryptoException( + "only 128, 192 and 256 bit ciphers are supported atm. sorry :%" + ); + }; + keys = new byte[kc][4]; + + keyExpansion(key); + } + + public byte[] encryptBlock(byte[] input) throws XACryptoException { + int m = switch (bits) { + case 128 -> 10; + case 192 -> 12; + case 256 -> 14; + default -> throw new XACryptoException("only 128, 192 and 256 bit ciphers are supported atm. sorry :%"); + }; + + byte[][] s = new byte[4][4]; + + for (int i = 0; i < 16; i++) { + s[i & 3][i >> 2] = input[i]; + } + + addRoundKey(s, 0); + + for (int r = 1; r < m; r++) { + sub(s); + shift(s); + mixColumns(s); + addRoundKey(s, r); + } + + sub(s); + shift(s); + addRoundKey(s, m); + + byte[] o = new byte[16]; + + for (int i = 0; i < 16; i++) { + o[i] = s[i & 3][i >> 2]; + } + + return o; + } + +// private void sub(byte[][] s) { +// for (int c = 0; c < 4; c++) { +// for (int r = 0; r < 4; r++) { +// s[r][c] = (byte)(s[r][c] ^ keys[r * 4 + c][r]); +// } +// } +// } + + private static void sub(byte[][] s) { + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + s[r][c] = SBOX[s[r][c] & 0xFF]; + } + } + } + + private static void shift(byte[][] s) { + byte t; + + t = s[1][0]; + s[1][0] = s[1][1]; + s[1][1] = s[1][2]; + s[1][2] = s[1][3]; + s[1][3] = t; + + t = s[2][0]; + byte t2 = s[2][1]; + s[2][0] = s[2][2]; + s[2][1] = s[2][3]; + s[2][2] = t; + s[2][3] = t2; + + t = s[3][3]; + s[3][3] = s[3][2]; + s[3][2] = s[3][1]; + s[3][1] = s[3][0]; + s[3][0] = t; + } + private static void mixColumns(byte[][] s) { + + for (int c = 0; c < 4; c++) { + + byte a0 = s[0][c]; + byte a1 = s[1][c]; + byte a2 = s[2][c]; + byte a3 = s[3][c]; + + s[0][c] = (byte)(gm2(a0) ^ gm3(a1) ^ a2 ^ a3); + s[1][c] = (byte)(a0 ^ gm2(a1) ^ gm3(a2) ^ a3); + s[2][c] = (byte)(a0 ^ a1 ^ gm2(a2) ^ gm3(a3)); + s[3][c] = (byte)(gm3(a0) ^ a1 ^ a2 ^ gm2(a3)); + } + } + + private static byte gm2(byte b) { + int x = b & 0xFF; + return (byte)((((x << 1) & 0xFF) ^ ((x & 0x80) != 0 ? 0x1b : 0))); + } + + private static byte gm3(byte b) { + return (byte)(gm2(b) ^ b); + } + + private void addRoundKey(byte[][] s, int round) { + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + s[r][c] ^= keys[round * 4 + c][r]; + } + } + } + + private void keyExpansion(byte[] key) throws XACryptoException { + int tem = switch(bits) { + case 128 -> 4; + case 192 -> 6; + case 256 -> 8; + default -> throw new XACryptoException( + "only 128, 192 and 256 bit ciphers are supported atm. sorry :%" + ); + }; + + if (key.length != 16 && key.length != 24 && key.length != 32) + throw new XACryptoException("aes keys must be 16, 24, or 32 in length."); + + for (int i = 0; i < tem; i++) { + System.arraycopy(key, i * 4, keys[i], 0, 4); + } + + for (int i = tem; i < kc; i++) { + + byte[] temp = Arrays.copyOf(keys[i - 1], 4); + + if (i % tem == 0) { + + byte t = temp[0]; + temp[0] = temp[1]; + temp[1] = temp[2]; + temp[2] = temp[3]; + temp[3] = t; + + for (int j = 0; j < 4; j++) { + temp[j] = SBOX[temp[j] & 0xFF]; + } + + temp[0] ^= (byte) RCON[i / tem]; + } + + if (bits == 256 && i % 8 == 4) for (int j = 0 ; j < 4 ; j++) temp[j] = SBOX[temp[j] & 0xFF]; + + for (int j = 0; j < 4; j++) keys[i][j] = (byte)(keys[i - tem][j] ^ temp[j]); + } + } + + public byte[] encryptCBC(byte[] ln, byte[] iv) throws XACryptoException { + + int padLen = 16 - (ln.length % 16); + if (padLen == 0) padLen = 16; + + byte[] pln = Arrays.copyOf(ln, ln.length + padLen); + for (int i = ln.length; i < pln.length; i++) { + pln[i] = (byte) padLen; + } + + byte[] o = new byte[pln.length]; + byte[] prev = Arrays.copyOf(iv, 16); + + byte[] block = new byte[16]; + + for (int i = 0; i < pln.length; i += 16) { + + for (int j = 0; j < 16; j++) { + block[j] = (byte)(pln[i + j] ^ prev[j]); + } + + byte[] enc = encryptBlock(block); + + System.arraycopy(enc, 0, o, i, 16); + + prev = enc; + } + + return o; + } + + public byte[] encryptCTR(byte[] ln, byte[] nonce) throws XACryptoException { + + byte[] o = new byte[ln.length]; + + byte[] cnt = Arrays.copyOf(nonce, 16); + byte[] ks; + + for (int i = 0; i < ln.length; i += 16) { + + int ctr = i >>> 4; + + cnt[12] = (byte)(ctr >>> 24); + cnt[13] = (byte)(ctr >>> 16); + cnt[14] = (byte)(ctr >>> 8); + cnt[15] = (byte)(ctr); + + ks = encryptBlock(cnt); + + int limit = Math.min(16, ln.length - i); + for (int j = 0; j < limit; j++) { + o[i + j] = (byte) (ln[i + j] ^ ks[j]); + } + } + + return o; + } +} \ No newline at end of file diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/BlockCipher.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/BlockCipher.java new file mode 100644 index 0000000..8bcd103 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/BlockCipher.java @@ -0,0 +1,7 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM; + +import de.caydenno1.xacrypto.misc.XACryptoException; + +public interface BlockCipher { + byte[] encryptBlock(byte[] pln) throws XACryptoException; +} \ No newline at end of file diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/GCM.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/GCM.java new file mode 100644 index 0000000..a73a388 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/GCM.java @@ -0,0 +1,111 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM; +import de.caydenno1.xacrypto.misc.ToM; +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.misc.isNull; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Objects; + +public class GCM { + private final Object cip; + private final GHASH gh; + private final Method encryptor; + + public GCM(Object cip) throws XACryptoException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + this.cip = cip; + this.encryptor = cip.getClass().getMethod("encryptBlock", byte[].class); + + byte[] H = (byte[]) this.encryptor.invoke(cip, (Object) new byte[16]); + + this.gh = new GHASH(H); + } + + public Result encrypt(byte[] pln, byte[] aad, byte[] iv) throws XACryptoException, InvocationTargetException, IllegalAccessException { + if (iv.length <= 0) throw new XACryptoException("iv does not have data or is corrupted :["); + if (pln == null) pln = new byte[0]; + if (aad == null) aad = new byte[0]; + + byte[] J0 = j0(iv); + byte[] ct = gctr(inc32(J0), pln); + byte[] tag = tag(aad, ct, J0); + + byte[] packaged = new byte[iv.length+ct.length]; + System.arraycopy(iv,0,packaged,0,12); + System.arraycopy(ct, 0, packaged, 12, ct.length); + + return new Result(packaged,tag); + } + + public byte[] decrypt(Result res, byte[] aad) throws XACryptoException, InvocationTargetException, IllegalAccessException { + return dec(res, aad, 12, false); + } + + public byte[] decrypt(Result res, byte[] aad, String optflag) throws XACryptoException, InvocationTargetException, IllegalAccessException { + return dec(res, aad, 12, Objects.equals(optflag, "-override")); + } + + public byte[] decrypt(Result res, byte[] aad, int ivLen) throws XACryptoException, InvocationTargetException, IllegalAccessException { + return dec(res, aad, ivLen, false); + } + + public byte[] decrypt(Result res, byte[] aad, int ivLen, String optflag) throws XACryptoException, InvocationTargetException, IllegalAccessException { + return dec(res, aad, ivLen, Objects.equals(optflag, "-override")); + } + + private byte[] dec(Result res, byte[] aad, int ivLen, boolean override) throws XACryptoException, InvocationTargetException, IllegalAccessException { + if (res.cip().length < 12) throw new XACryptoException("ciptext min len is 12 char"); + if (isNull.isNull(aad)) aad = new byte[0]; + + byte[] iv = Arrays.copyOfRange(res.cip(), 0, 12); + byte[] ct = Arrays.copyOfRange(res.cip(), 12, res.cip().length); + + byte[] J0 = j0(iv); + byte[] expectedTag = tag(aad, ct, J0); + + boolean corr = ToM.ToM(res.tag(), expectedTag); + + if (!corr && !override) { + throw new XACryptoException("GCM tag does not match. Use Flag -override to ignore this.",(byte)-1); + } else if (!corr) { + System.out.println("GCM tag does not match. Overriding..."); + } + + return gctr(inc32(J0), ct); + } + + private byte[] j0(byte[] iv) { + if (iv.length == 12) { + byte[] J0 = new byte[16]; + System.arraycopy(iv, 0, J0, 0, 12); + J0[15] = 0x01; + return J0; + } else { + return gh.compute(new byte[0], iv); + } + } + + private byte[] tag(byte[] aad, byte[] ct, byte[] J0) throws InvocationTargetException, IllegalAccessException { + byte[] S = gh.compute(aad, ct); + byte[] EJ0 = (byte[]) this.encryptor.invoke(J0); + for (int i = 0; i < 16; i++) S[i] ^= EJ0[i]; + return S; + } + private byte[] gctr(byte[] icb, byte[] in) throws InvocationTargetException, IllegalAccessException { + byte[] o = new byte[in.length]; + byte[] cnt = Arrays.copyOf(icb, 16); + for (int i = 0 ; i < in.length ; i += 16){ + byte[] ks = (byte[]) this.encryptor.invoke(cnt); + int len = Math.min(16, in.length -i); + for (int j = 0; j < len; j++) o[i + j] = (byte)(in[i + j] ^ ks[j]); + if (i+16= 12; i--) if (++o[i] != 0) break; + return o; + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/GHASH.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/GHASH.java new file mode 100644 index 0000000..1a7db74 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/GHASH.java @@ -0,0 +1,110 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM; + +import de.caydenno1.xacrypto.zekerrijndael.UnchangingData; + +import java.nio.ByteBuffer; +import java.util.Arrays; + + +public class GHASH { + private final byte[] H; + + public GHASH(byte[] H) { + this.H = Arrays.copyOf(H, 16); + } + + public byte[] compute(byte[] aad, byte[] cip){ + byte[] Y = new byte[16]; + Y = proc(Y, aad); + Y = proc(Y, cip); + byte[] lenBlock = buildLenBlock( + aad.length * 8L, + cip.length * 8L + ); + Y = xor(Y, lenBlock); + + Y = multi(Y,H); + return Y; + } + private byte[] proc(byte[] Y, byte[] in) { + for (int off = 0; off < in.length; off+=16){ + byte[] bloc = new byte[16]; + int len = Math.min(16, in.length - off); + System.arraycopy(in, off, bloc, 0, len); + Y = xor(Y, bloc); + Y = multi(Y, H); + } + return Y; + } + private byte[] multi(byte[] X, byte[] Y) { + byte[] Z = new byte[16]; + byte[] V = Arrays.copyOf(Y, 16); + for (int bit = 0 ; bit < 128 ; bit++) { + int byindex = bit / 8; + int biindex = 7 - (bit%8); + + if (((X[byindex] >> biindex) & 1) == 1) Z = xor(Z,V); + + boolean isLSB1 = (V[15] & 1) != 0; + + RS(V); + + if (isLSB1) V = xor(V,UnchangingData.R); + } + return Z; + } + private void RS(byte[] bloc) { + int c = 0 ; + for (int i = 0 ; i < 16 ; i++){ + int val = bloc[i] & 0xFF; + int next = val & 1; + bloc[i] = (byte) ((val >>> 1) | (c << 7)); + + c = next; + } + } + public byte[] compNonce(byte[] H, byte[] nonce){ + byte[] Y = new byte[16]; + byte[] blk = new byte[16]; + + int len = nonce.length; + int off = 0; + + while (len > 0) { + Arrays.fill(blk, (byte)0); + + int cl = Math.min(16, len);//count + System.arraycopy(nonce, off, blk, 0, cl); + + multi(blk, H); + + off += cl; + len -= cl; + } + + Arrays.fill(blk, (byte)0); + + long bL = (long)nonce.length *8; + + for (int i = 8 ; i < 16 ; i++){ + blk[i] = (byte) ((bL >>> (8 * (15-i))) & 0xFF); + } + + Y = xor(Y,blk); + Y = multi(Y, H); + return Y; + } + private byte[] buildLenBlock(long aad, long cipb) { + ByteBuffer buf = ByteBuffer.allocate(16); + + buf.putLong(aad); + buf.putLong(cipb); + + return buf.array(); + } + public byte[] xor(byte[] a, byte[] b){ + byte[] res = new byte[16]; + for (int i = 0 ; i < 16 ; i++) res[i] = (byte) (a[i] ^ b[i]); + return res; + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Result.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Result.java new file mode 100644 index 0000000..6171d74 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Result.java @@ -0,0 +1,3 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM; + +public record Result(byte[] cip, byte[] tag) {} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Serpent.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Serpent.java new file mode 100644 index 0000000..3b9fe8b --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Serpent.java @@ -0,0 +1,142 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM; + +import de.caydenno1.xacrypto.misc.XACryptoException; + +import java.lang.reflect.InvocationTargetException; + +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.SERPENT_SBOX; +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.SERPENT_IBOX; +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.SERPENT_PHI; + +interface SerpentCipher extends BlockCipher { + byte[] encryptBlock(byte[] in) throws XACryptoException; + byte[] decryptBlock(byte[] in) throws XACryptoException; +} + +public class Serpent implements SerpentCipher { + private final int[][] K; + + public Serpent(byte[] key) throws XACryptoException { + if (key.length != 16 && key.length != 24 && key.length != 32) throw new XACryptoException("serpent key must be either 16,24 or 32 bytes in length", key.length); + this.K = new int[33][4]; + keyExpansion(key); + } + + @Override + public byte[] encryptBlock(byte[] in) throws XACryptoException { + int[] X = new int[4]; + pack(in, X); + + for (int r = 0 ; r < 32 ; r++) { + illumFOR(X, r); + SBOXify(r % 8, X, SERPENT_SBOX); + if (r==31) illumFOR(X, 32); + else LT(X); + } + + byte[] o = new byte[16]; + unpack(X, o); + return o; + } + + public byte[] decryptBlock(byte[] in) { + int[] X = new int[4]; + pack(in, X); + + illumFOR(X, 32); + + for (int r = 31 ; r >= 0; r--) { + if (r != 31) invLT(X); + SBOXify(r % 8, X, SERPENT_IBOX); + illumFOR(X, r); + } + byte[] o = new byte[16]; + unpack(X, o); + return o; + } + private void illumFOR(int[] X, int N) { + for (int i = 0 ; i < 4 ; i++) X[i] ^= K[N][i]; + } + private void keyExpansion(byte[] key){ + byte[] pk = new byte[32]; + System.arraycopy(key, 0, pk, 0, key.length); + if (key.length < 32) pk[key.length] = (byte) 0x01; + + int[] w = new int[104]; + for (int i = 0 ; i < 8 ; i++) { + w[i] = (pk[i * 4] & 0xFF) | ((pk[i * 4 + 1] & 0xFF) << 8) | + ((pk[i * 4 + 2] & 0xFF) << 16) | ((pk[i * 4 + 3] & 0xFF) << 24); + } + + for (int i = 8 ; i < 140 ; i++) { + int _0 = w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ SERPENT_PHI ^ (i - 8); + w[i] = Integer.rotateLeft(_0, 11); + } + + for (int i = 0 ; i < 33 ; i++) { + int[] group = { w[8 + 4 * i], w[8 + 4 * i + 1], w[8 + 4 * i + 2], w[8 + 4 * i + 3] }; + + int index = (3 - i) % 8; + + SBOXify(index, group, SERPENT_SBOX); + + byte[] kb = new byte[16]; + for (int j = 0 ; j < 4 ; j++) for (int k = 0; k < 4; k++) kb[j * 4 + k] = (byte) (group[j] >>> (k * 8)); + + pack(kb, K[i]); + } + } + private void SBOXify(int box, int[] X, byte[][] table) { + int[] Y = new int[4]; + byte[] sbox = table[box]; + for (int i = 0 ; i < 32 ; i++) { + int nibble = ((X[0] >>> i) & 1) | (((X[1] >>> i) & 1) << 1) | + (((X[2] >>> i) & 1) << 2) | (((X[3] >>> i) & 1) << 3); + int out = sbox[nibble]; + Y[0] |= (out & 1) << i; + Y[1] |= ((out >>> 1) & 1) << i; + Y[2] |= ((out >>> 2) & 1) << i; + Y[3] |= ((out >>> 3) & 1) << i; + } + for (int i = 0 ; i < 4 ; i++) X[i] = Y[i]; + } + + private void LT(int[] X) { + X[0] = Integer.rotateLeft(X[0], 13); + X[2] = Integer.rotateLeft(X[2], 3); + X[1] ^= X[0] ^ X[2]; + X[3] ^= X[2] ^ (X[0] << 3); + X[1] = Integer.rotateLeft(X[1], 1); + X[3] = Integer.rotateLeft(X[3], 7); + X[0] ^= X[1] ^ X[3]; + X[2] ^= X[3] ^ (X[1] << 7); + X[0] = Integer.rotateLeft(X[0], 5); + X[2] = Integer.rotateLeft(X[2], 22); + + } + private void invLT(int[] X) { + X[2] = Integer.rotateRight(X[2], 22); + X[0] = Integer.rotateRight(X[0], 5); + X[2] ^= X[3] ^ (X[1] << 7); + X[0] ^= X[1] ^ X[3]; + X[3] = Integer.rotateRight(X[3], 7); + X[1] = Integer.rotateRight(X[1], 1); + X[3] ^= X[2] ^ (X[0] << 3); + X[1] ^= X[0] ^ X[2]; + X[2] = Integer.rotateRight(X[2], 3); + X[0] = Integer.rotateRight(X[0], 13); + } + private void pack(byte[] src, int[] dest) { + dest[0] = dest[1] = dest[2] = dest[3] = 0; + for (int i = 0 ; i < 128 ; i++) { + int b = (src[i / 8] >>> (i % 8)) & 1; + dest[i % 4] |= (b << (i / 4)); + } + } + private void unpack(int[] src, byte[] dest) { + for (int i = 0 ; i < 128 ; i++) { + int b = (src[i % 4] >>> (i / 4)) & 1; + dest[i / 8] |= (b << (i % 8)); + } + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Twofish.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Twofish.java new file mode 100644 index 0000000..3212557 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/Twofish.java @@ -0,0 +1,134 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.UnchangingData; + +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.TWOFISH_Q0; +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.TWOFISH_Q1; + +public class Twofish { + private final int[] K = new int[40]; + private final int[] S; + private final int kW; + + public Twofish(byte[] key) throws XACryptoException { + if (key.length != 16 && key.length != 24 && key.length != 32) + throw new XACryptoException("16,24,32 bit key is required :-("); + + kW = key.length / 8; + S = new int[kW]; + + int[] M = new int[2 * kW]; + for (int i = 0; i < 2 * kW; i++) M[i] = read32LE(key, i * 4); + + int[] Me = new int[kW]; + int[] Mo = new int[kW]; + for (int i = 0; i < kW; i++) { + Me[i] = M[2 * i]; + Mo[i] = M[2 * i + 1]; + + byte[] m = new byte[8]; + for (int j = 0; j < 8; j++) m[j] = key[8 * i + j]; + int s0 = gfMultRS(0x01, m[0] & 0xFF) ^ gfMultRS(0xA4, m[1] & 0xFF) ^ gfMultRS(0x55, m[2] & 0xFF) ^ gfMultRS(0x87, m[3] & 0xFF) ^ + gfMultRS(0x5A, m[4] & 0xFF) ^ gfMultRS(0x58, m[5] & 0xFF) ^ gfMultRS(0xDB, m[6] & 0xFF) ^ gfMultRS(0x9E, m[7] & 0xFF); + int s1 = gfMultRS(0xA4, m[0] & 0xFF) ^ gfMultRS(0x56, m[1] & 0xFF) ^ gfMultRS(0x82, m[2] & 0xFF) ^ gfMultRS(0xF3, m[3] & 0xFF) ^ + gfMultRS(0x1E, m[4] & 0xFF) ^ gfMultRS(0xC6, m[5] & 0xFF) ^ gfMultRS(0x68, m[6] & 0xFF) ^ gfMultRS(0xE5, m[7] & 0xFF); + int s2 = gfMultRS(0x02, m[0] & 0xFF) ^ gfMultRS(0xA1, m[1] & 0xFF) ^ gfMultRS(0xFC, m[2] & 0xFF) ^ gfMultRS(0xC1, m[3] & 0xFF) ^ + gfMultRS(0x47, m[4] & 0xFF) ^ gfMultRS(0xAE, m[5] & 0xFF) ^ gfMultRS(0x3D, m[6] & 0xFF) ^ gfMultRS(0x19, m[7] & 0xFF); + int s3 = gfMultRS(0xA4, m[0] & 0xFF) ^ gfMultRS(0x55, m[1] & 0xFF) ^ gfMultRS(0x87, m[2] & 0xFF) ^ gfMultRS(0x5A, m[3] & 0xFF) ^ + gfMultRS(0x58, m[4] & 0xFF) ^ gfMultRS(0xDB, m[5] & 0xFF) ^ gfMultRS(0x9E, m[6] & 0xFF) ^ gfMultRS(0x03, m[7] & 0xFF); + S[i] = (s0 & 0xFF) | ((s1 & 0xFF) << 8) | ((s2 & 0xFF) << 16) | ((s3 & 0xFF) << 24); + } + for (int i = 0; i < 20; i++) { + int A = h(2 * i * 0x01010101, Me, kW); + int B = Integer.rotateLeft(h((2 * i + 1) * 0x01010101, Mo, kW), 8); + K[2 * i] = A * B; + K[2 * i + 1] = Integer.rotateLeft(A + 2 * B, 9); + } + } + private int h(int x, int[] L, int k) { + int[] B = new int[4]; + for (int i = 0 ; i < 4 ; i++) B[i] = (int) ((x >>> (8 * i)) & 0xFF); + + if (k == 4) { + for (int i = 0; i < 4; i++) { + byte[] q = (i == 0 || i == 3) ? TWOFISH_Q1 : TWOFISH_Q0; + B[i] = (q[B[i]] & 0xFF) ^ ((L[3] >>> (8 * i)) & 0xFF); + } + } + if (k >= 3) { + for (int i = 0; i < 4; i++) { + byte[] q = (i < 2) ? TWOFISH_Q1 : TWOFISH_Q0; + B[i] = (q[B[i]] & 0xFF) ^ ((L[2] >>> (8 * i)) & 0xFF); + } + } + + B[0] = (TWOFISH_Q1[ (TWOFISH_Q0[ (TWOFISH_Q0[B[0]] & 0xFF) ^ (L[1] & 0xFF) ] & 0xFF) ^ (L[0] & 0xFF) ] & 0xFF); + B[1] = (TWOFISH_Q0[ (TWOFISH_Q0[ (TWOFISH_Q1[B[1]] & 0xFF) ^ ((L[1] >>> 8) & 0xFF) ] & 0xFF) ^ ((L[0] >>> 8) & 0xFF) ] & 0xFF); + B[2] = (TWOFISH_Q1[ (TWOFISH_Q1[ (TWOFISH_Q0[B[2]] & 0xFF) ^ ((L[1] >>> 16) & 0xFF) ] & 0xFF) ^ ((L[0] >>> 16) & 0xFF) ] & 0xFF); + B[3] = (TWOFISH_Q0[ (TWOFISH_Q1[ (TWOFISH_Q1[B[3]] & 0xFF) ^ ((L[1] >>> 24) & 0xFF) ] & 0xFF) ^ ((L[0] >>> 24) & 0xFF) ] & 0xFF); + + int[] Z = new int[4]; + Z[0] = gfMultMDS(0x01, B[0]) ^ gfMultMDS(0xEF, B[1]) ^ gfMultMDS(0x5B, B[2]) ^ gfMultMDS(0x5B, B[3]); + Z[1] = gfMultMDS(0x5B, B[0]) ^ gfMultMDS(0xEF, B[1]) ^ gfMultMDS(0xEF, B[2]) ^ gfMultMDS(0x01, B[3]); + Z[2] = gfMultMDS(0xEF, B[0]) ^ gfMultMDS(0x5B, B[1]) ^ gfMultMDS(0x01, B[2]) ^ gfMultMDS(0xEF, B[3]); + Z[3] = gfMultMDS(0xEF, B[0]) ^ gfMultMDS(0x01, B[1]) ^ gfMultMDS(0xEF, B[2]) ^ gfMultMDS(0x5B, B[3]); + + return Z[0] | (Z[1] << 8) | (Z[2] << 16) | (Z[3] << 24); + } + + public byte[] encryptBlock(byte[] in) throws XACryptoException { + if (in.length != 16) throw new XACryptoException("twofish's minimum block size is 16 bytes :(", (int)16); + int[] X = new int[4]; + for (int i = 0; i < 4; i++) { + X[i] = read32LE(in, i * 4) ^ K[i]; + } + + for (int r = 0 ; r < 16 ; r += 2) { + int t0 = h(X[0], S, kW); + int t1 = h(Integer.rotateLeft(X[1], 8), S, kW); + int f0 = t0 + t1 + K[2 * r + 8]; + int f1 = t0 + 2 * t1 + K[2 * r + 9]; + X[2] = Integer.rotateRight(X[2] ^ f0, 1); + X[3] = Integer.rotateLeft(X[2], 1) ^ f1; + + t0 = h(X[2], S, kW); + t1 = h(Integer.rotateLeft(X[3], 8), S, kW); + f0 = t0 + t1 + K[2 * r + 10]; + f1 = t0 + 2 * t1 + K[2 * r + 11]; + X[0] = Integer.rotateRight(X[0] ^ f0, 1); + X[1] = Integer.rotateLeft(X[1], 1) ^ f1; + } + + byte[] o = new byte[16]; + write32LE(X[2] ^ K[4], o, 0); + write32LE(X[3] ^ K[5], o, 4); + write32LE(X[0] ^ K[6], o, 8); + write32LE(X[1] ^ K[7], o, 12); + return o; + } + private static int read32LE(byte[] b, int off) { return (b[off] & 0xFF) | ((b[off + 1] & 0xFF) << 8) | ((b[off + 2] & 0xFF) << 16) | ((b[off + 3] & 0xFF) << 24); } + private static void write32LE(int v, byte[] b, int off) { for (int i = 0 ; i < 4 ; i++) b[off + i] = (byte) (v >>> (i * 8)); }; + private static int gfMultMDS(int a, int b) { + int p = 0; + for (int i = 0 ; i < 8 ; i++) { + if ((b & 1) != 0) p ^= a; + boolean badia_msg_monosodium_glutamate_28_oz_175_lbs_id_00033844005313 /* lmao */ = (a & 0x80) != 0; + a <<= 1; + if (badia_msg_monosodium_glutamate_28_oz_175_lbs_id_00033844005313) a ^= 0x169; + b >>= 1; + } + return p & 0xFF; + } + private static int gfMultRS(int a, int b) { + int p = 0; + for (int i = 0 ; i < 8; i++) { + if ((b & 1) != 0) p ^= a; + boolean badia_msg_monosodium_glutamate_28_oz_175_lbs_id_00033844005313 = (a & 0x80) != 0; + a <<= 1; + if (badia_msg_monosodium_glutamate_28_oz_175_lbs_id_00033844005313) a ^= 0x14D; + b >>= 1; + } + return p & 0xFF; + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/AESGCM.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/AESGCM.java new file mode 100644 index 0000000..5a96e25 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/AESGCM.java @@ -0,0 +1,94 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.GCM.GHASH; +import de.caydenno1.xacrypto.zekerrijndael.GCM.AES; +import de.caydenno1.xacrypto.zekerrijndael.GCM.Result; + +import java.util.Objects; + +import static de.caydenno1.xacrypto.misc.ToM.ToM; + +interface AESCipher { + Result encryptBlock(byte[] pln, byte[] key, byte[] nonce, byte[] aad) throws XACryptoException; + + byte[] decryptBlock(byte[] cip, byte[] key, byte[] nonce, byte[] aad, byte[] tag) throws XACryptoException; +} + +public class AESGCM implements AESCipher { + public Result encryptBlock(byte[] pln, byte[] key, byte[] nonce, byte[] aad) throws XACryptoException { + System.out.println("WARNING! AESGCM may not be fully functional and partially broken. I am unsure if it fully works or not."); + @SuppressWarnings("DuplicatedCode") + byte[] zbyte = new byte[16]; + byte[] J0 = new byte[16]; + + AES aes = new AES(key, getKeySize(key)); + @SuppressWarnings("DuplicatedCode") + byte[] H = aes.encryptBlock(zbyte); + GHASH gh = new GHASH(H); + + if (nonce.length == 12) { + System.arraycopy(nonce, 0, J0, 0, 12); + J0[15] = 1; + } else { + J0 = gh.compNonce(H, nonce); + } + + byte[] cip = aes.encryptCTR(pln, J0); + + byte[] S = gh.compute(aad, cip); + + byte[] TB = aes.encryptBlock(J0); + + byte[] Tag = gh.xor(TB, S); + + return new Result(cip, Tag); + } + public byte[] decryptBlock(byte[] cip, byte[] key, byte[] nonce, byte[] aad, byte[] tag, String flag) throws XACryptoException { + return decBack(cip, key, nonce, aad, tag, Objects.equals(flag, "-override")); + } + + public byte[] decryptBlock(byte[] cip, byte[] key, byte[] nonce, byte[] aad, byte[] tag) throws XACryptoException { + return decBack(cip, key, nonce, aad, tag, false); + } + + private static int getKeySize(byte[] key) throws XACryptoException { + return switch (key.length) { + case 16 -> 128; + case 24 -> 192; + case 32 -> 256; + default -> throw new XACryptoException( + "Invalid AES key length. Expected 16, 24, or 32 bytes." + ); + }; + } + private byte[] decBack(byte[] cip, byte[] key, byte[] nonce, byte[] aad, byte[] tag, boolean flag) throws XACryptoException { + byte[] zbyte = new byte[16]; + byte[] J0 = new byte[16]; + @SuppressWarnings("DuplicatedCode") + AES aes = new AES(key, getKeySize(key)); + byte[] H = aes.encryptBlock(zbyte); + GHASH gh = new GHASH(H); + + if (nonce.length == 12) { + System.arraycopy(nonce, 0, J0, 0, 12); + J0[15] = 1; + } else { + J0 = gh.compNonce(H, nonce); + } + + byte[] S = gh.compute(aad, cip); + + byte[] TB = aes.encryptBlock(J0); + + byte[] expectedTag = gh.xor(TB, S); + + if (!ToM(tag, expectedTag) && !flag) { + throw new XACryptoException("Tag does not match. USE flag \"-override\" to ignore this."); + } else if (!ToM(tag,expectedTag) && flag) { + System.out.println("Continuing in insecure mode."); + } + + return aes.encryptCTR(cip, J0); + } +} \ No newline at end of file diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/ARIAGCM.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/ARIAGCM.java new file mode 100644 index 0000000..0dbe2d1 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/ARIAGCM.java @@ -0,0 +1,36 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.Global.Aria; +import de.caydenno1.xacrypto.zekerrijndael.GCM.GCM; +import de.caydenno1.xacrypto.zekerrijndael.GCM.Result; + +import java.lang.reflect.InvocationTargetException; + +interface AriaCipher { + Result encrypt(byte[] pln, byte[] aad, byte[] iv) throws XACryptoException, InvocationTargetException, IllegalAccessException; + byte[] decrypt(Result res, byte[] aad) throws XACryptoException, InvocationTargetException, IllegalAccessException; + byte[] decrypt(Result res, byte[] aad, String optflag) throws XACryptoException, InvocationTargetException, IllegalAccessException; +} + +public class ARIAGCM implements AriaCipher { + // comes from south korea (likely Seoul?) + // i basically wrote the entire thing in Aria.java already lol. just inheriting it here + private final GCM engine; + + public ARIAGCM(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + Aria aria = new Aria(true, key); + this.engine = new GCM(aria); + } + + public Result encrypt(byte[] pln, byte[] aad, byte[] iv) throws XACryptoException, InvocationTargetException, IllegalAccessException { + return engine.encrypt(pln, aad,iv); + } + + public byte[] decrypt(Result res, byte[] aad) throws XACryptoException, InvocationTargetException, IllegalAccessException { + return engine.decrypt(res, aad); + } + public byte[] decrypt(Result res, byte[] aad, String optflag) throws XACryptoException, InvocationTargetException, IllegalAccessException { + return engine.decrypt(res, aad, optflag); + } +} \ No newline at end of file diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/Camellia.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/Camellia.java new file mode 100644 index 0000000..01322ce --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/Camellia.java @@ -0,0 +1,358 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.UnchangingData; +import de.caydenno1.xacrypto.hash.ROT; + +interface CamelliaCipher { + byte[] encryptBlock(byte[] in, int inputOffset, byte[] out, int outOffset) throws XACryptoException; + byte[] decryptBlock(byte[] in); +} + +public class Camellia implements CamelliaCipher { + private final long[] subkeys; + + public Camellia(byte[] key) throws XACryptoException { + if (key.length != 16 && key.length != 24 && key.length != 32) throw new XACryptoException("16,24,32 byte key is required"); + this.subkeys = new long[key.length == 16 ? 26 : 34]; + genKeySchedule(key); + } + + @Override + public byte[] encryptBlock(byte[] in, int inputOffset, byte[] out, int outOffset) { + long d1 = bytes2Long(in, inputOffset); + long d2 = bytes2Long(in, inputOffset + 8); + + d1 ^= subkeys[0]; + d2 ^= subkeys[1]; + + d2 ^= F(d1, subkeys[2]); + d1 ^= F(d2, subkeys[3]); + d2 ^= F(d1, subkeys[4]); + d1 ^= F(d2, subkeys[5]); + d2 ^= F(d1, subkeys[6]); + d1 ^= F(d2, subkeys[7]); + + d1 = FL(d1, subkeys[8]); + d2 = FLINV(d2, subkeys[9]); + + d2 ^= F(d1, subkeys[10]); + d1 ^= F(d2, subkeys[11]); + d2 ^= F(d1, subkeys[12]); + d1 ^= F(d2, subkeys[13]); + d2 ^= F(d1, subkeys[14]); + d1 ^= F(d2, subkeys[15]); + + d1 = FL(d1, subkeys[16]); + d2 = FLINV(d2, subkeys[17]); + + d2 ^= F(d1, subkeys[18]); + d1 ^= F(d2, subkeys[19]); + d2 ^= F(d1, subkeys[20]); + d1 ^= F(d2, subkeys[21]); + d2 ^= F(d1, subkeys[22]); + d1 ^= F(d2, subkeys[23]); + + if (subkeys.length == 34) { + d1 = FL(d1, subkeys[24]); + d2 = FLINV(d2, subkeys[25]); + + d2 ^= F(d1, subkeys[26]); + d1 ^= F(d2, subkeys[27]); + d2 ^= F(d1, subkeys[28]); + d1 ^= F(d2, subkeys[29]); + d2 ^= F(d1, subkeys[30]); + d1 ^= F(d2, subkeys[31]); + + d2 ^= subkeys[32]; + d1 ^= subkeys[33]; + } else { + d2 ^= subkeys[24]; + d1 ^= subkeys[25]; + } + + byte[] o = new byte[16]; + long2Bytes(d2, o, outOffset); + long2Bytes(d1, o, outOffset + 8); + return o; + } + + public byte[] decryptBlock(byte[] in) { + long d1 = bytes2Long(in, 0); + long d2 = bytes2Long(in, 8); + + if (subkeys.length == 34) { + d1 ^= subkeys[32]; + d2 ^= subkeys[33]; + + d2 ^= F(d1, subkeys[31]); + d1 ^= F(d2, subkeys[30]); + d2 ^= F(d1, subkeys[29]); + d1 ^= F(d2, subkeys[28]); + d2 ^= F(d1, subkeys[27]); + d1 ^= F(d2, subkeys[26]); + + d1 = FL(d1, subkeys[25]); + d2 = FLINV(d2, subkeys[24]); + } else { + d1 ^= subkeys[24]; + d2 ^= subkeys[25]; + } + + d2 ^= F(d1, subkeys[23]); + d1 ^= F(d2, subkeys[22]); + d2 ^= F(d1, subkeys[21]); + d1 ^= F(d2, subkeys[20]); + d2 ^= F(d1, subkeys[19]); + d1 ^= F(d2, subkeys[18]); + + d1 = FL(d1, subkeys[17]); + d2 = FLINV(d2, subkeys[16]); + + d2 ^= F(d1, subkeys[15]); + d1 ^= F(d2, subkeys[14]); + d2 ^= F(d1, subkeys[13]); + d1 ^= F(d2, subkeys[12]); + d2 ^= F(d1, subkeys[11]); + d1 ^= F(d2, subkeys[10]); + + d1 = FL(d1, subkeys[9]); + d2 = FLINV(d2, subkeys[8]); + + d2 ^= F(d1, subkeys[7]); + d1 ^= F(d2, subkeys[6]); + d2 ^= F(d1, subkeys[5]); + d1 ^= F(d2, subkeys[4]); + d2 ^= F(d1, subkeys[3]); + d1 ^= F(d2, subkeys[2]); + + d2 ^= subkeys[0]; + d1 ^= subkeys[1]; + + byte[] o = new byte[16]; + long2Bytes(d2, o, 0); + long2Bytes(d1, o, 8); + return o; + } + + private void genKeySchedule(byte[] key) { + long KL1 = bytes2Long(key, 0); + long KL2 = bytes2Long(key, 8); + long KR1=0,KR2 = 0; + + if (key.length == 24) { + KR1 = bytes2Long(key, 16); + KR2 = ~KR1; + } else if (key.length == 32) { + KR1 = bytes2Long(key, 16); + KR2 = bytes2Long(key, 24); + } + + long d1 = KL1 ^ KR1; + long d2 = KL2 ^ KR2; + d2 ^= F(d1, UnchangingData.CAMELLIA_SIGMA[1]); + d1 ^= F(d2, UnchangingData.CAMELLIA_SIGMA[2]); + d1 ^= KL1; + d2 ^= KL2; + d2 ^= F(d1, UnchangingData.CAMELLIA_SIGMA[3]); + d1 ^= F(d2, UnchangingData.CAMELLIA_SIGMA[4]); + long KA1 = d1; + long KA2 = d2; + + long KB1 = 0, KB2 = 0; + + if (key.length > 16) { + d1 = KA1 ^ KR1; + d2 = KA2 ^ KR2; + d2 ^= F(d1, UnchangingData.CAMELLIA_SIGMA[5]); + d1 ^= F(d2, UnchangingData.CAMELLIA_SIGMA[6]); + KB1 = d1; + KB2 = d2; + } + + if (key.length == 16) { + subkeys[0] = KL1; + subkeys[1] = KL2; + subkeys[2] = KA1; + subkeys[3] = KA2; + + long[] KLrot15 = ROT.ROTL64(KL1, KL2, 15); + subkeys[4] = KLrot15[0]; + subkeys[5] = KLrot15[1]; + + long[] KArot15 = ROT.ROTL64(KA1, KA2, 15); + subkeys[6] = KArot15[0]; + subkeys[7] = KArot15[1]; + + long[] KArot30 = ROT.ROTL64(KA1, KA2, 30); + subkeys[8] = (KArot30[0] >>> 32) | (KArot30[0] << 32); + subkeys[9] = (KArot30[1] >>> 32) | (KArot30[1] << 32); + + long[] KLrot45 = ROT.ROTL64(KL1, KL2, 45); + subkeys[10] = KLrot45[0]; + subkeys[11] = KLrot45[1]; + + long[] KArot45 = ROT.ROTL64(KA1, KA2, 45); + subkeys[12] = KArot45[0]; + subkeys[13] = KArot45[1]; + + long[] KLrot60 = ROT.ROTL64(KL1, KL2, 60); + subkeys[14] = KLrot60[0]; + subkeys[15] = KLrot60[1]; + + long[] KLrot77 = ROT.ROTL64(KL1, KL2, 77); + subkeys[16] = (KLrot77[0] >>> 32) | (KLrot77[0] << 32); + subkeys[17] = (KLrot77[1] >>> 32) | (KLrot77[1] << 32); + + long[] KLrot94 = ROT.ROTL64(KL1, KL2, 94); + subkeys[18] = KLrot94[0]; + subkeys[19] = KLrot94[1]; + + long[] KArot94 = ROT.ROTL64(KA1, KA2, 94); + subkeys[20] = KArot94[0]; + subkeys[21] = KArot94[1]; + + long[] KLrot111 = ROT.ROTL64(KL1, KL2, 111); + subkeys[22] = KLrot111[0]; + subkeys[23] = KLrot111[1]; + + long[] KArot111 = ROT.ROTL64(KA1, KA2, 111); + subkeys[24] = KArot111[0]; + subkeys[25] = KArot111[1]; + } else { + subkeys[0] = KL1; + subkeys[1] = KL2; + + subkeys[2] = KB1; + subkeys[3] = KB2; + + long[] KRrot15 = ROT.ROTL64(KR1, KR2, 15); + subkeys[4] = KRrot15[0]; + subkeys[5] = KRrot15[1]; + + long[] KArot15 = ROT.ROTL64(KA1, KA2, 15); + subkeys[6] = KArot15[0]; + subkeys[7] = KArot15[1]; + + long[] KRrot30 = ROT.ROTL64(KR1, KR2, 30); + subkeys[8] = (KRrot30[0] >>> 32) | (KRrot30[0] << 32); + subkeys[9] = (KRrot30[1] >>> 32) | (KRrot30[1] << 32); + + long[] KBrot30 = ROT.ROTL64(KB1, KB2, 30); + subkeys[10] = KBrot30[0]; + subkeys[11] = KBrot30[1]; + + long[] KLrot45 = ROT.ROTL64(KL1, KL2, 45); + subkeys[12] = KLrot45[0]; + subkeys[13] = KLrot45[1]; + + long[] KArot45 = ROT.ROTL64(KA1, KA2, 45); + subkeys[14] = KArot45[0]; + subkeys[15] = KArot45[1]; + + long[] KLrot60 = ROT.ROTL64(KL1, KL2, 60); + subkeys[16] = (KLrot60[0] >>> 32) | (KLrot60[0] << 32); + subkeys[17] = (KLrot60[1] >>> 32) | (KLrot60[1] << 32); + + long[] KRrot60 = ROT.ROTL64(KR1, KR2, 60); + subkeys[18] = KRrot60[0]; + subkeys[19] = KRrot60[1]; + + long[] KBrot60 = ROT.ROTL64(KB1, KB2, 60); + subkeys[20] = KBrot60[0]; + subkeys[21] = KBrot60[1]; + + long[] KLrot77 = ROT.ROTL64(KL1, KL2, 77); + subkeys[22] = KLrot77[0]; + subkeys[23] = KLrot77[1]; + + long[] KArot77 = ROT.ROTL64(KA1, KA2, 77); + subkeys[24] = (KArot77[0] >>> 32) | (KArot77[0] << 32); + subkeys[25] = (KArot77[1] >>> 32) | (KArot77[1] << 32); + + long[] KRrot94 = ROT.ROTL64(KR1, KR2, 94); + subkeys[26] = KRrot94[0]; + subkeys[27] = KRrot94[1]; + + long[] KArot94 = ROT.ROTL64(KA1, KA2, 94); + subkeys[28] = KArot94[0]; + subkeys[29] = KArot94[1]; + + long[] KLrot111 = ROT.ROTL64(KL1, KL2, 111); + subkeys[30] = KLrot111[0]; + subkeys[31] = KLrot111[1]; + + long[] KBrot111 = ROT.ROTL64(KB1, KB2, 111); + subkeys[32] = KBrot111[0]; + subkeys[33] = KBrot111[1]; + } + } + + private long F(long F_IN, long KE) { + long x = F_IN ^ KE; + + int[] t = new int[9]; + for (int i = 1; i <= 8; i++) t[i] = (int) ((x >>> (64 - (i * 8))) & 0xFFL); + + t[1] = UnchangingData.CAMELLIA_SBOX1[t[1]]; + t[2] = UnchangingData.CAMELLIA_SBOX2[t[2]]; + t[3] = UnchangingData.CAMELLIA_SBOX3[t[3]]; + t[4] = UnchangingData.CAMELLIA_SBOX4[t[4]]; + t[5] = UnchangingData.CAMELLIA_SBOX2[t[5]]; + t[6] = UnchangingData.CAMELLIA_SBOX3[t[6]]; + t[7] = UnchangingData.CAMELLIA_SBOX4[t[7]]; + t[8] = UnchangingData.CAMELLIA_SBOX1[t[8]]; + + int y1 = t[1] ^ t[3] ^ t[4] ^ t[6] ^ t[7] ^ t[8]; + int y2 = t[1] ^ t[2] ^ t[4] ^ t[5] ^ t[7] ^ t[8]; + int y3 = t[1] ^ t[2] ^ t[3] ^ t[5] ^ t[6] ^ t[8]; + int y4 = t[2] ^ t[3] ^ t[4] ^ t[5] ^ t[6] ^ t[7]; + int y5 = t[1] ^ t[2] ^ t[6] ^ t[7] ^ t[8]; + int y6 = t[2] ^ t[3] ^ t[5] ^ t[7] ^ t[8]; + int y7 = t[3] ^ t[4] ^ t[5] ^ t[6] ^ t[8]; + int y8 = t[1] ^ t[4] ^ t[5] ^ t[6] ^ t[7]; + + return ((long) y1 << 56) | ((long) y2 << 48) | ((long) y3 << 40) | ((long) y4 << 32) | + ((long) y5 << 24) | ((long) y6 << 16) | ((long) y7 << 8) | (long) y8; + } + + private long FL(long FL_IN, long KE) { + long x1 = FL_IN >>> 32; + long x2 = FL_IN & 0xFFFFFFFFL; + long k1 = KE >>> 32; + long k2 = KE & 0xFFFFFFFFL; + + long y2 = x2 ^ ROT.ROTL32((x1 & k1), 1); + long y1 = x1 ^ (y2 | k2); + + return (y1 << 32) | (y2 & 0xFFFFFFFFL); + } + + // <- im expert dev/programmer! + private long FLINV(long FLINV_IN, long KE) { + long y1 = FLINV_IN >>> 32; + long y2 = FLINV_IN & 0xFFFFFFFFL; + long k1 = KE >>> 32; + long k2 = KE & 0xFFFFFFFFL; + + long x1 = y1 ^ (y2 | k2); + long x2 = y2 ^ ROT.ROTL32((x1 & k1), 1); + + return (x1 << 32) | (x2 & 0xFFFFFFFFL); + } + + private long bytes2Long(byte[] b, int off) { + long res = 0; + for (int i = 0; i < 8; i++) { + int shift = 56 - (i * 8); + res |= ((long) (b[off + i] & 0xFF) << shift); + } + return res; + } + + private void long2Bytes(long v, byte[] b, int off) { + for (int i = 0; i < 8; i++) { + b[off + i] = (byte) (v >>> (56 - (i * 8))); + } + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/SM4GCM.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/SM4GCM.java new file mode 100644 index 0000000..d838c18 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/SM4GCM.java @@ -0,0 +1,68 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.GCM.BlockCipher; +import de.caydenno1.xacrypto.zekerrijndael.UnchangingData; + +public final class SM4GCM implements BlockCipher { + // mainland china encryption! ;) (GB/T 32907-2016) + private final int[] rk = new int[32]; + + public SM4GCM(byte[] key) throws XACryptoException { + if (key.length != 16) throw new XACryptoException("16 byte key required. :L"); + int[] K = new int[4]; + for (int i = 0 ; i < 4 ; i++)K[i] = read(key, i*4) ^ UnchangingData.SM4_FK[i]; + for (int i = 0 ; i < 32 ; i++) { + K[i&3] ^= p(K[(i+1)&3] + ^ K[(i+2)&3] + ^ K[(i+3)&3] + ^ UnchangingData.SM4_CK[i] + ); + rk[i] = K[i & 3]; + } + } + + @Override + public byte[] encryptBlock(byte[] in) { + int[] x = new int[4]; + for (int i = 0 ; i < 4 ; i++) x[i] = read(in, i*4); + for (int i = 0 ; i < 32 ; i++) { + int nx = x[0] ^ t(x[1] ^ x[2] ^ x[3] ^ rk[i]); + for (int j = 0 ; j < 3 ; j++) x[j] = x[j+1]; + x[3] = nx; + } + byte[] o = new byte[16]; + for (int i = 0 ; i < 4 ; i++) write(x[3-i], o, i*4); + return o; + } + + private static int tau(int w) { + return (UnchangingData.SBOX[(w >>> 24) & 0xFF] << 24) | + (UnchangingData.SBOX[(w >>> 8) & 0xFF] << 24); + } + + private static int t(int w) { + int b = tau(w); + return b ^ de.caydenno1.xacrypto.hash.ROT.ROTL(b, 2) + ^ de.caydenno1.xacrypto.hash.ROT.ROTL(b, 10) + ^ de.caydenno1.xacrypto.hash.ROT.ROTL(b, 18) + ^ de.caydenno1.xacrypto.hash.ROT.ROTL(b, 24); + } + + private static int p(int w) { + int b = tau(w); + return b ^ de.caydenno1.xacrypto.hash.ROT.ROTL(b,13) + ^ de.caydenno1.xacrypto.hash.ROT.ROTL(b,23); + } + + private static int read(byte[] b, int o){ + return ((b[o] & 0xFF) << 24) + | ((b[o+1]&0xFF)<<16) + | ((b[o+2]&0xFF)<<8) + | (b[o+3]&0xFF); + } + + private static void write(int v, byte[] b, int o) { + for (int i = 0; i < 4; i++) b[o + i] = (byte)(v >>> (24 - i * 8)); + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/SerpentGCM.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/SerpentGCM.java new file mode 100644 index 0000000..8b4f02a --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/SerpentGCM.java @@ -0,0 +1,14 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.GCM.BlockCipher; +import de.caydenno1.xacrypto.zekerrijndael.GCM.GCM; +import de.caydenno1.xacrypto.zekerrijndael.GCM.Serpent; + +import java.lang.reflect.InvocationTargetException; + +import static de.caydenno1.xacrypto.zekerrijndael.UnchangingData.SERPENT_SBOX; + +public class SerpentGCM extends GCM { + public SerpentGCM(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { super(new Serpent(key)); } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/TwofishGCM.java b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/TwofishGCM.java new file mode 100644 index 0000000..1b24d58 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/GCM/ciphers/TwofishGCM.java @@ -0,0 +1,13 @@ +package de.caydenno1.xacrypto.zekerrijndael.GCM.ciphers; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.GCM.GCM; +import de.caydenno1.xacrypto.zekerrijndael.GCM.Twofish; + +import java.lang.reflect.InvocationTargetException; + +public class TwofishGCM extends GCM { + public TwofishGCM(byte[] key) throws XACryptoException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + super(new Twofish(key)); + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/Global/Aria.java b/src/de/caydenno1/xacrypto/zekerrijndael/Global/Aria.java new file mode 100644 index 0000000..c8f86a1 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/Global/Aria.java @@ -0,0 +1,270 @@ +package de.caydenno1.xacrypto.zekerrijndael.Global; + +import de.caydenno1.xacrypto.misc.XACryptoException; +import de.caydenno1.xacrypto.zekerrijndael.UnchangingData; + +public class Aria { + // this is NOT a cipher, more or less and encryption type/method + private final int round; + private final byte[][] encRK; + private final byte[][] decRK; + + public Aria(boolean enc, byte[] key) throws XACryptoException { + int kb = key.length * 8; + round = switch(kb) { + case 128 -> 12; + case 192 -> 14; + case 256 -> 16; + default -> throw new XACryptoException( + "only 128, 192 and 256 bit ciphers are supported atm. sorry :%" + ); + }; + + encRK = new byte[round + 1][16]; + decRK = new byte[round + 1][16]; + + encRK(key); + SDK(); + } + + public byte[] encryptBlock(byte[] in) throws XACryptoException{ + if (in.length != 16) throw new XACryptoException("input length is required to be 16 bytes", (byte)0x16); + + byte[] currentState = new byte[16]; + System.arraycopy(in, 0, currentState, 0, 16); + + procRound(currentState, this.encRK); + + return currentState; + } + + public byte[] decryptBlock(byte[] in) throws XACryptoException { + if (in.length != 16) throw new XACryptoException("input length is required to be 16 bytes", (byte)0x16); + + byte[] state = new byte[16]; + System.arraycopy(in,0,state,0,16); + + procRound(state, this.decRK); + + return state; + } + + private void SDK() { + System.arraycopy(encRK[0], 0, decRK[round], 0, 16); + System.arraycopy(encRK[round], 0, decRK[0], 0, 16); + + byte[] _0 = new byte[16]; + for (int i = 1 ; i < round ; i++){ + A(encRK[i], _0); + System.arraycopy(_0, 0, decRK[round-i], 0, 16); + } + } + private void procRound(byte[] state, byte[][] rK){ + byte[] _0 = new byte[16]; + + for (int r = 0 ; r < round - 1 ; r++){ + AriaXOR(state,rK[r]); + if (r % 2 == 0) { + SL1(state, _0); + } else { + SL2(state, _0); + } + A(_0, state); + } + + AriaXOR(state, rK[round-1]); + if((round-1) % 2 == 0) { + SL1(state, _0); + } else { + SL2(state, _0); + } + + AriaXOR(_0, rK[round], state); + } + + private void AriaXOR(byte[] blk, byte[] key){ + for (int i = 0 ; i < 16 ; i++){ + blk[i] ^= key[i]; + } + } + + private void AriaXOR(byte[] a, byte[] b, byte[] o) { + for (int i = 0 ; i < 16 ; i++) { + o[i] = (byte)(a[i] ^ b[i]); + } + } + + public void SROTR128(byte[] i, int bits, byte[] o) { + int byS = bits / 8; + int biS = bits % 8; + + for (int _0 = 0 ; _0 < 16 ; _0++){ + int idx1 = (_0 - byS + 32) % 16; + int idx2 = (_0 - byS - 1 + 32) % 16; + + int b1 = i[idx1] & 0xFF; + int b2 = i[idx2] & 0xFF; + + o[_0] = (byte) ((b1 >>> biS) | (b2 << (8 - biS))); + } + } + // feistel odd + private void FO(byte[] i, byte[] ck, byte[] o) { + byte[] _0 = new byte[16]; + AriaXOR(i, ck, _0); + SL1(_0, _0); + A(_0, o); + } + //feistel even + private void FE(byte[] i, byte[] ck, byte[] o) { + byte[] _0 = new byte[16]; + AriaXOR(i, ck, _0); + SL2(_0, _0); + A(_0, o); + } + + private void SL1(byte[] i,byte[] o){ + byte[][] tables = { + UnchangingData.ARIA_SB1, + UnchangingData.ARIA_SB2, + UnchangingData.ARIA_XB1, + UnchangingData.ARIA_XB2 + }; + + for (int k = 0; k < 16; k++) { + o[k] = tables[k & 3][i[k] & 0xff]; + } + + } + + private void encRK(byte[] key) throws XACryptoException { + int size = key.length; + + byte[] KL = new byte[16]; + byte[] KR = new byte[16]; + byte[][] CK = new byte[4][16]; + + if (size == 16 || size == 24 || size == 32) System.arraycopy(key,0,KL,0,16); + switch(size) { + case 16: CK[1]=UnchangingData.ARIA_C1;CK[2]=UnchangingData.ARIA_C2;CK[3]=UnchangingData.ARIA_C3; break; + + case 24: { + System.arraycopy(key, 16, KR, 0, 8); + CK[1] = UnchangingData.ARIA_C2; CK[2] = UnchangingData.ARIA_C3; CK[3] = UnchangingData.ARIA_C1; + break; + } + + case 32: { + System.arraycopy(key,16,KR,0,16); + CK[1] = UnchangingData.ARIA_C3; CK[2] = UnchangingData.ARIA_C1 ; CK[3] = UnchangingData.ARIA_C2; + break; + } + + default: throw new XACryptoException("only keysizes of 16,24,32 are supported atm :|"); + } + + byte[][] W = new byte[4][16]; + + System.arraycopy(KL, 0, W[0], 0, 16); + + FO(W[0], CK[1], W[1]); + AriaXOR(W[1], KR, W[1]); // W1 = FO(W0, CK1) ^ KR + + FE(W[1], CK[2], W[2]); + AriaXOR(W[2], W[0], W[2]); // W2 = FE(W1, CK2) ^ W0 + + FO(W[2], CK[3], W[3]); + AriaXOR(W[3], W[1], W[3]); + + byte[] _0 = new byte[16]; + + SROTR128(W[1], 19, _0); + AriaXOR(W[0], _0, encRK[0]); + + SROTR128(W[2], 19, _0); + AriaXOR(W[1], _0, encRK[1]); + + SROTR128(W[3], 19, _0); + AriaXOR(W[2], _0, encRK[2]); + + SROTR128(W[0], 19, _0); + AriaXOR(_0, W[3], encRK[3]); + + SROTR128(W[1], 31, _0); + AriaXOR(W[0], _0, encRK[4]); + + SROTR128(W[2], 31, _0); + AriaXOR(W[1], _0, encRK[5]); + + SROTR128(W[3], 31, _0); + AriaXOR(W[2], _0, encRK[6]); + + SROTR128(W[0], 31, _0); + AriaXOR(_0, W[3], encRK[7]); + + SROTR128(W[1], 128 - 61, _0); + AriaXOR(W[0], _0, encRK[8]); + + SROTR128(W[2], 128 - 61, _0); + AriaXOR(W[1], _0, encRK[9]); + + SROTR128(W[3], 128 - 61, _0); + AriaXOR(W[2], _0, encRK[10]); + + SROTR128(W[0], 128 - 61, _0); + AriaXOR(_0, W[3], encRK[11]); + + SROTR128(W[1], 128 - 31, _0); + AriaXOR(W[0], _0, encRK[12]); + + if (round >= 14) { + SROTR128(W[2], 128 - 31, _0); + AriaXOR(W[1], _0, encRK[13]); + + // ek15 = W2 ^ (W3 <<< 31) + SROTR128(W[3], 128 - 31, _0); + AriaXOR(W[2], _0, encRK[14]); + } + + if (round == 16) { + SROTR128(W[0], 128 - 31, _0); + AriaXOR(_0, W[3], encRK[15]); + + SROTR128(W[1], 128 - 19, _0); + AriaXOR(W[0], _0, encRK[16]); + } + } + + private void SL2(byte[] i,byte[] o){ + byte[][] tables = { + UnchangingData.ARIA_XB1, + UnchangingData.ARIA_XB2, + UnchangingData.ARIA_SB1, + UnchangingData.ARIA_SB2 + }; + + for (int k = 0; k < 16; k++) { + o[k] = tables[k & 3][i[k] & 0xff]; + } + + } + private void A(byte[] i, byte[] o) { + // "diffusion layer" Matrix A + o[0] = (byte)(i[3] ^ i[4] ^ i[9] ^ i[14]); + o[1] = (byte)(i[2] ^ i[5] ^ i[8] ^ i[15]); + o[2] = (byte)(i[1] ^ i[6] ^ i[11] ^ i[12]); + o[3] = (byte)(i[0] ^ i[7] ^ i[10] ^ i[13]); + o[4] = (byte)(i[0] ^ i[5] ^ i[11] ^ i[14]); + o[5] = (byte)(i[1] ^ i[4] ^ i[10] ^ i[15]); + o[6] = (byte)(i[2] ^ i[7] ^ i[9] ^ i[12]); + o[7] = (byte)(i[3] ^ i[6] ^ i[8] ^ i[13]); + o[8] = (byte)(i[1] ^ i[7] ^ i[11] ^ i[13]); + o[9] = (byte)(i[0] ^ i[6] ^ i[10] ^ i[12]); + o[10] = (byte)(i[3] ^ i[5] ^ i[9] ^ i[15]); + o[11] = (byte)(i[2] ^ i[4] ^ i[8] ^ i[14]); + o[12] = (byte)(i[2] ^ i[6] ^ i[9] ^ i[14]); + o[13] = (byte)(i[3] ^ i[7] ^ i[8] ^ i[15]); + o[14] = (byte)(i[0] ^ i[4] ^ i[11] ^ i[12]); + o[15] = (byte)(i[1] ^ i[5] ^ i[10] ^ i[13]); + } +} diff --git a/src/de/caydenno1/xacrypto/zekerrijndael/UnchangingData.java b/src/de/caydenno1/xacrypto/zekerrijndael/UnchangingData.java new file mode 100644 index 0000000..3df3135 --- /dev/null +++ b/src/de/caydenno1/xacrypto/zekerrijndael/UnchangingData.java @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8b3cf3379dbb436a894945a7299a5e3be30844ac377df5a21f1c2cbb0a15b0b +size 52205 diff --git a/src/main/java/de/caydenno1/xacrypto/hash/ROT.java b/src/main/java/de/caydenno1/xacrypto/hash/ROT.java index da265f4..98483e2 100644 --- a/src/main/java/de/caydenno1/xacrypto/hash/ROT.java +++ b/src/main/java/de/caydenno1/xacrypto/hash/ROT.java @@ -7,4 +7,17 @@ public class ROT { public static int ROTL(int x, int n){ return (x << n) | (x >>> (32 - n)); } + public static long ROTL32(long v, int shift) { return ((v & 0xFFFFFFFFL << shift) | (v & 0xFFFFFFFFL >>> (32 - shift))) & 0xFFFFFFFFL; } + public static long[] ROTL64(long l, long r, int shift) { + long[] res = new long[2]; + if (shift < 64) { + res[0] = (l << shift) | (r >>> (64 - shift)); + res[1] = (r << shift) | (l >>> (64 - shift)); + } else { + int s = shift - 64; + res[0] = (r << s) | (l >>> (64 - s)); + res[1] = (l << s) | (r >>> (64 - s)); + } + return res; + } } diff --git a/src/main/java/de/caydenno1/xacrypto/hash/sha/SHA1.java b/src/main/java/de/caydenno1/xacrypto/hash/sha/SHA1.java index 41c1dd8..8438895 100644 --- a/src/main/java/de/caydenno1/xacrypto/hash/sha/SHA1.java +++ b/src/main/java/de/caydenno1/xacrypto/hash/sha/SHA1.java @@ -77,6 +77,4 @@ public class SHA1 { byte[] result = hash(data.getBytes(StandardCharsets.UTF_8)); return hex(result); } - - } \ No newline at end of file diff --git a/src/main/java/de/caydenno1/xacrypto/hash/sha256/SHA256.java b/src/main/java/de/caydenno1/xacrypto/hash/sha256/SHA256.java index 455d413..70a2d77 100644 --- a/src/main/java/de/caydenno1/xacrypto/hash/sha256/SHA256.java +++ b/src/main/java/de/caydenno1/xacrypto/hash/sha256/SHA256.java @@ -9,6 +9,7 @@ import java.util.concurrent.RecursiveTask; import static de.caydenno1.xacrypto.hash.sha256.Hex.hash; import static de.caydenno1.xacrypto.hash.sha256.Hex.doubleHash; import static de.caydenno1.xacrypto.hash.ROT.ROTR; +import de.caydenno1.xacrypto.misc.ToM; public final class SHA256 { private SHA256(){} @@ -88,14 +89,6 @@ public final class SHA256 { return o; } - public static boolean ToM(byte[] a, byte[] b) { - if (a == null || b == null) return a == b; - if (a.length != b.length) return false; - int diff = 0; - for (int i = 0; i < a.length; i++) diff |= (a[i] ^ b[i]); - return diff == 0; - } - public static final class HashTask extends RecursiveTask{ private final java.util.List leaves; private int from,to; diff --git a/src/main/java/de/caydenno1/xacrypto/misc/Constants.java b/src/main/java/de/caydenno1/xacrypto/misc/Constants.java index 36dd26d..b281cbf 100644 --- a/src/main/java/de/caydenno1/xacrypto/misc/Constants.java +++ b/src/main/java/de/caydenno1/xacrypto/misc/Constants.java @@ -1,39 +1,3 @@ -package de.caydenno1.xacrypto.misc; - -public class Constants { - public static final int[] SHA1_K = { - 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 - }; - public static final int[] SHA0_K = { - 0x00000000, - 0x5a827999, - 0x6ed9eba1, - 0x8f1bbcdc - }; - public static final int[] SHA256_K = { - 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b, - 0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01, - 0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7, - 0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc, - 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152, - 0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147, - 0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc, - 0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, - 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819, - 0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08, - 0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f, - 0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208, - 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 - }; - - public static final int[] SHA256_H = { - 0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a, - 0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19 - }; - - public static final int[] SHA_H = { - 0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0 - }; - - // (no longer a placeholder) -} +version https://git-lfs.github.com/spec/v1 +oid sha256:bc4782acf20fb2dfe37bdaf88a22fc42816a72839597a17afed0d6984b5569d5 +size 1549 diff --git a/src/main/java/de/caydenno1/xacrypto/misc/ToM.java b/src/main/java/de/caydenno1/xacrypto/misc/ToM.java new file mode 100644 index 0000000..e8d48cb --- /dev/null +++ b/src/main/java/de/caydenno1/xacrypto/misc/ToM.java @@ -0,0 +1,11 @@ +package de.caydenno1.xacrypto.misc; + +public class ToM { + public static boolean ToM(byte[] a, byte[] b) { + if (a == null || b == null) return a == b; + if (a.length != b.length) return false; + int diff = 0; + for (int i = 0; i < a.length; i++) diff |= (a[i] ^ b[i]); + return diff == 0; + } +} diff --git a/src/main/java/de/caydenno1/xacrypto/misc/XACryptoException.java b/src/main/java/de/caydenno1/xacrypto/misc/XACryptoException.java index ba3cdf9..a04b5a3 100644 --- a/src/main/java/de/caydenno1/xacrypto/misc/XACryptoException.java +++ b/src/main/java/de/caydenno1/xacrypto/misc/XACryptoException.java @@ -15,6 +15,10 @@ public class XACryptoException extends Exception { super(res); this.id = id & 0xFF; } + public XACryptoException(String res, int iid) { + super(res); + this.id = iid; + } public XACryptoException(String[] pnts, byte id){ super(String.join("|", pnts)); this.id = id & 0xFF; diff --git a/src/main/java/de/caydenno1/xacrypto/misc/isNull.java b/src/main/java/de/caydenno1/xacrypto/misc/isNull.java new file mode 100644 index 0000000..4437892 --- /dev/null +++ b/src/main/java/de/caydenno1/xacrypto/misc/isNull.java @@ -0,0 +1,22 @@ +package de.caydenno1.xacrypto.misc; + +import java.util.Objects; + +public class isNull { + public static boolean isNull(Object v) { + return Objects.isNull(v) || v == null; + } + public boolean isNull(Object v, Object alt) { + return Objects.isNull(v) || v == null || v == alt; + } + public static boolean isValidText(String s) { + if (s.isBlank() || s.trim().isEmpty()) return false; + switch (s) { + case "": + case null: + return false; + default: + return true; + } + }; +}