Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/spongycastle/crypto/engines')
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/AESEngine.java537
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/AESFastEngine.java928
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/AESLightEngine.java434
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/AESWrapEngine.java16
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/AESWrapPadEngine.java10
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/BlowfishEngine.java577
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/CAST5Engine.java831
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/CAST6Engine.java296
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/CamelliaEngine.java684
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/CamelliaLightEngine.java592
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/CamelliaWrapEngine.java15
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/ChaChaEngine.java204
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCiphertext.java179
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCoreEngine.java313
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/DESEngine.java495
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/DESedeEngine.java127
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/DESedeWrapEngine.java350
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java217
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/GOST28147Engine.java372
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/Grain128Engine.java304
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/Grainv1Engine.java290
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/HC128Engine.java259
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/HC256Engine.java246
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/IDEAEngine.java357
-rwxr-xr-xcore/src/main/java/org/spongycastle/crypto/engines/IESEngine.java438
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/ISAACEngine.java221
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/NaccacheSternEngine.java437
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/NoekeonEngine.java263
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/NullEngine.java96
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RC2Engine.java317
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RC2WrapEngine.java385
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RC4Engine.java146
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RC532Engine.java287
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RC564Engine.java288
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RC6Engine.java363
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RFC3211WrapEngine.java175
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RFC3394WrapEngine.java177
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RFC5649WrapEngine.java294
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RSABlindedEngine.java126
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RSABlindingEngine.java137
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RSACoreEngine.java203
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RSAEngine.java78
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/RijndaelEngine.java725
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/SEEDEngine.java346
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/SEEDWrapEngine.java15
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/Salsa20Engine.java474
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/SerpentEngine.java783
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/Shacal2Engine.java201
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/SkipjackEngine.java260
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/TEAEngine.java184
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/ThreefishEngine.java1494
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/TwofishEngine.java680
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/VMPCEngine.java142
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/VMPCKSA3Engine.java45
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/XSalsa20Engine.java65
-rw-r--r--core/src/main/java/org/spongycastle/crypto/engines/XTEAEngine.java188
56 files changed, 18666 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/AESEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/AESEngine.java
new file mode 100644
index 00000000..bc7e9176
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/AESEngine.java
@@ -0,0 +1,537 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first.
+ *
+ * The slowest version uses no static tables at all and computes the values in each round.
+ * <p>
+ * This file contains the middle performance version with 2Kbytes of static tables for round precomputation.
+ *
+ */
+public class AESEngine
+ implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
+ (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
+ (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
+ (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
+ (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
+ (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
+ (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
+ (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
+ (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
+ (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
+ (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
+ (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
+ (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
+ (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
+ (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
+ (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
+ (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ // The inverse S-box
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56,
+ (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135,
+ (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61,
+ (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178,
+ (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22,
+ (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
+ (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10,
+ (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2,
+ (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234,
+ (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133,
+ (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137,
+ (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32,
+ (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49,
+ (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13,
+ (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176,
+ (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38,
+ (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ // precomputation tables of calculations for rounds
+ private static final int[] T0 =
+ {
+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
+ 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
+ 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+ 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
+ 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
+ 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
+ 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+ 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
+ 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
+ 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
+ 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
+ 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
+ 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
+ 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+ 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
+ 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
+ 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
+ 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+ 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
+ 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
+ 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
+ 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
+ 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
+ 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+ 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
+ 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
+ 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+ 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
+ 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
+ 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
+ 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
+ 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
+ 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+ 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
+ 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
+ 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
+ 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+ 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
+ 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
+ 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
+ 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
+ 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
+ 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
+ 0x3a16162c};
+
+private static final int[] Tinv0 =
+ {
+ 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
+ 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
+ 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+ 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
+ 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
+ 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
+ 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+ 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
+ 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
+ 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
+ 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
+ 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
+ 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
+ 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+ 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
+ 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
+ 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
+ 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+ 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
+ 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
+ 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+ 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
+ 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
+ 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
+ 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+ 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
+ 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
+ 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+ 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
+ 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
+ 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
+ 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+ 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
+ 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
+ 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+ 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
+ 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
+ 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
+ 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+ 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
+ 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
+ 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
+ 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
+ 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
+ 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
+ 0x4257b8d0};
+
+ private static int shift(int r, int shift)
+ {
+ return (r >>> shift) | (r << -shift);
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private static int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private static int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+ private static int subWord(int x)
+ {
+ return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private int[][] generateWorkingKey(
+ byte[] key,
+ boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS+1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ int i = 0;
+ while (i < key.length)
+ {
+ W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
+ i+=4;
+ t++;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (i = KC; (i < k); i++)
+ {
+ int temp = W[(i-1)>>2][(i-1)&3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC)-1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean forEncryption;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AESEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ C0 = (bytes[index++] & 0xff);
+ C0 |= (bytes[index++] & 0xff) << 8;
+ C0 |= (bytes[index++] & 0xff) << 16;
+ C0 |= bytes[index++] << 24;
+
+ C1 = (bytes[index++] & 0xff);
+ C1 |= (bytes[index++] & 0xff) << 8;
+ C1 |= (bytes[index++] & 0xff) << 16;
+ C1 |= bytes[index++] << 24;
+
+ C2 = (bytes[index++] & 0xff);
+ C2 |= (bytes[index++] & 0xff) << 8;
+ C2 |= (bytes[index++] & 0xff) << 16;
+ C2 |= bytes[index++] << 24;
+
+ C3 = (bytes[index++] & 0xff);
+ C3 |= (bytes[index++] & 0xff) << 8;
+ C3 |= (bytes[index++] & 0xff) << 16;
+ C3 |= bytes[index++] << 24;
+ }
+
+ private void packBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte)C0;
+ bytes[index++] = (byte)(C0 >> 8);
+ bytes[index++] = (byte)(C0 >> 16);
+ bytes[index++] = (byte)(C0 >> 24);
+
+ bytes[index++] = (byte)C1;
+ bytes[index++] = (byte)(C1 >> 8);
+ bytes[index++] = (byte)(C1 >> 16);
+ bytes[index++] = (byte)(C1 >> 24);
+
+ bytes[index++] = (byte)C2;
+ bytes[index++] = (byte)(C2 >> 8);
+ bytes[index++] = (byte)(C2 >> 16);
+ bytes[index++] = (byte)(C2 >> 24);
+
+ bytes[index++] = (byte)C3;
+ bytes[index++] = (byte)(C3 >> 8);
+ bytes[index++] = (byte)(C3 >> 16);
+ bytes[index++] = (byte)(C3 >> 24);
+ }
+
+
+ private void encryptBlock(int[][] KW)
+ {
+ int t0 = this.C0 ^ KW[0][0];
+ int t1 = this.C1 ^ KW[0][1];
+ int t2 = this.C2 ^ KW[0][2];
+
+ int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+ while (r < ROUNDS - 1)
+ {
+ r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
+ r1 = T0[t1&255] ^ shift(T0[(t2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(t0>>24)&255], 8) ^ KW[r][1];
+ r2 = T0[t2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(t0>>16)&255], 16) ^ shift(T0[(t1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[r3&255] ^ shift(T0[(t0>>8)&255], 24) ^ shift(T0[(t1>>16)&255], 16) ^ shift(T0[(t2>>24)&255], 8) ^ KW[r++][3];
+ t0 = T0[r0&255] ^ shift(T0[(r1>>8)&255], 24) ^ shift(T0[(r2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
+ t1 = T0[r1&255] ^ shift(T0[(r2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(r0>>24)&255], 8) ^ KW[r][1];
+ t2 = T0[r2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(r0>>16)&255], 16) ^ shift(T0[(r1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[r3&255] ^ shift(T0[(r0>>8)&255], 24) ^ shift(T0[(r1>>16)&255], 16) ^ shift(T0[(r2>>24)&255], 8) ^ KW[r++][3];
+ }
+
+ r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
+ r1 = T0[t1&255] ^ shift(T0[(t2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(t0>>24)&255], 8) ^ KW[r][1];
+ r2 = T0[t2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(t0>>16)&255], 16) ^ shift(T0[(t1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[r3&255] ^ shift(T0[(t0>>8)&255], 24) ^ shift(T0[(t1>>16)&255], 16) ^ shift(T0[(t2>>24)&255], 8) ^ KW[r++][3];
+
+ // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+ this.C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
+ this.C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
+ this.C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+ this.C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+ }
+
+ private void decryptBlock(int[][] KW)
+ {
+ int t0 = this.C0 ^ KW[ROUNDS][0];
+ int t1 = this.C1 ^ KW[ROUNDS][1];
+ int t2 = this.C2 ^ KW[ROUNDS][2];
+
+ int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+ while (r > 1)
+ {
+ r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
+ r1 = Tinv0[t1&255] ^ shift(Tinv0[(t0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(t2>>24)&255], 8) ^ KW[r][1];
+ r2 = Tinv0[t2&255] ^ shift(Tinv0[(t1>>8)&255], 24) ^ shift(Tinv0[(t0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[r3&255] ^ shift(Tinv0[(t2>>8)&255], 24) ^ shift(Tinv0[(t1>>16)&255], 16) ^ shift(Tinv0[(t0>>24)&255], 8) ^ KW[r--][3];
+ t0 = Tinv0[r0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(r2>>16)&255], 16) ^ shift(Tinv0[(r1>>24)&255], 8) ^ KW[r][0];
+ t1 = Tinv0[r1&255] ^ shift(Tinv0[(r0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(r2>>24)&255], 8) ^ KW[r][1];
+ t2 = Tinv0[r2&255] ^ shift(Tinv0[(r1>>8)&255], 24) ^ shift(Tinv0[(r0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[r3&255] ^ shift(Tinv0[(r2>>8)&255], 24) ^ shift(Tinv0[(r1>>16)&255], 16) ^ shift(Tinv0[(r0>>24)&255], 8) ^ KW[r--][3];
+ }
+
+ r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
+ r1 = Tinv0[t1&255] ^ shift(Tinv0[(t0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(t2>>24)&255], 8) ^ KW[r][1];
+ r2 = Tinv0[t2&255] ^ shift(Tinv0[(t1>>8)&255], 24) ^ shift(Tinv0[(t0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[r3&255] ^ shift(Tinv0[(t2>>8)&255], 24) ^ shift(Tinv0[(t1>>16)&255], 16) ^ shift(Tinv0[(t0>>24)&255], 8) ^ KW[r][3];
+
+ // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+ this.C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+ this.C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
+ this.C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
+ this.C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/AESFastEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/AESFastEngine.java
new file mode 100644
index 00000000..4eb705ba
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/AESFastEngine.java
@@ -0,0 +1,928 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Pack;
+
+/**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values in each round
+ * <p>
+ * This file contains the fast version with 8Kbytes of static tables for round precomputation
+ *
+ */
+public class AESFastEngine
+ implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
+ (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
+ (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
+ (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
+ (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
+ (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
+ (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
+ (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
+ (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
+ (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
+ (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
+ (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
+ (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
+ (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
+ (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
+ (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
+ (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ // The inverse S-box
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56,
+ (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135,
+ (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61,
+ (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178,
+ (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22,
+ (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
+ (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10,
+ (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2,
+ (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234,
+ (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133,
+ (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137,
+ (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32,
+ (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49,
+ (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13,
+ (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176,
+ (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38,
+ (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ // precomputation tables of calculations for rounds
+ private static final int[] T =
+ {
+ // T0
+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
+ 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
+ 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+ 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
+ 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
+ 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
+ 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+ 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
+ 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
+ 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
+ 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
+ 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
+ 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
+ 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+ 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
+ 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
+ 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
+ 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+ 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
+ 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
+ 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
+ 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
+ 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
+ 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+ 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
+ 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
+ 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+ 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
+ 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
+ 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
+ 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
+ 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
+ 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+ 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
+ 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
+ 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
+ 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+ 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
+ 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
+ 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
+ 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
+ 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
+ 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
+ 0x3a16162c,
+
+ // T1
+ 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d,
+ 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203,
+ 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
+ 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
+ 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec,
+ 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7,
+ 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae,
+ 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
+ 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293,
+ 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552,
+ 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f,
+ 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+ 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b,
+ 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2,
+ 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761,
+ 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
+ 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060,
+ 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46,
+ 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8,
+ 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
+ 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
+ 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844,
+ 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0,
+ 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
+ 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030,
+ 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814,
+ 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc,
+ 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
+ 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0,
+ 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
+ 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3,
+ 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
+ 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db,
+ 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e,
+ 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337,
+ 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
+ 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4,
+ 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e,
+ 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
+ 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
+ 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd,
+ 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42,
+ 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701,
+ 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
+ 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938,
+ 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970,
+ 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592,
+ 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+ 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da,
+ 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0,
+ 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6,
+ 0x16162c3a,
+
+ // T2
+ 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2,
+ 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301,
+ 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
+ 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
+ 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad,
+ 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4,
+ 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93,
+ 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
+ 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371,
+ 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7,
+ 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05,
+ 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+ 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09,
+ 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e,
+ 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6,
+ 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
+ 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020,
+ 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb,
+ 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858,
+ 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
+ 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
+ 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c,
+ 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040,
+ 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
+ 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010,
+ 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c,
+ 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44,
+ 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
+ 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060,
+ 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
+ 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8,
+ 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
+ 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49,
+ 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3,
+ 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4,
+ 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
+ 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c,
+ 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a,
+ 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
+ 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
+ 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b,
+ 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e,
+ 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6,
+ 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
+ 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1,
+ 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9,
+ 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287,
+ 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+ 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf,
+ 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099,
+ 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb,
+ 0x162c3a16,
+
+ // T3
+ 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2,
+ 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101,
+ 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
+ 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
+ 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad,
+ 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4,
+ 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393,
+ 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
+ 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171,
+ 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7,
+ 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505,
+ 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+ 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909,
+ 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e,
+ 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6,
+ 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
+ 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020,
+ 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb,
+ 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858,
+ 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
+ 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
+ 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c,
+ 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040,
+ 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
+ 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010,
+ 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c,
+ 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444,
+ 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
+ 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060,
+ 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
+ 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8,
+ 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
+ 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949,
+ 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3,
+ 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4,
+ 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
+ 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c,
+ 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a,
+ 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
+ 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
+ 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b,
+ 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e,
+ 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6,
+ 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
+ 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1,
+ 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9,
+ 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787,
+ 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+ 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf,
+ 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999,
+ 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb,
+ 0x2c3a1616};
+
+ private static final int[] Tinv =
+ {
+ // Tinv0
+ 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
+ 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
+ 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+ 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
+ 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
+ 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
+ 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+ 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
+ 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
+ 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
+ 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
+ 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
+ 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
+ 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+ 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
+ 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
+ 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
+ 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+ 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
+ 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
+ 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+ 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
+ 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
+ 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
+ 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+ 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
+ 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
+ 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+ 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
+ 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
+ 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
+ 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+ 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
+ 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
+ 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+ 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
+ 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
+ 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
+ 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+ 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
+ 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
+ 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
+ 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
+ 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
+ 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
+ 0x4257b8d0,
+
+ // Tinv1
+ 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb,
+ 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6,
+ 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
+ 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1,
+ 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7,
+ 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3,
+ 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b,
+ 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4,
+ 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0,
+ 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19,
+ 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357,
+ 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+ 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b,
+ 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5,
+ 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532,
+ 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51,
+ 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5,
+ 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697,
+ 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738,
+ 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000,
+ 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
+ 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821,
+ 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2,
+ 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16,
+ 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b,
+ 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c,
+ 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5,
+ 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863,
+ 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d,
+ 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
+ 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa,
+ 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef,
+ 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf,
+ 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d,
+ 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e,
+ 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3,
+ 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09,
+ 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e,
+ 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
+ 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0,
+ 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a,
+ 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d,
+ 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8,
+ 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e,
+ 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c,
+ 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735,
+ 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879,
+ 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+ 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672,
+ 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de,
+ 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874,
+ 0x57b8d042,
+
+ // Tinv2
+ 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b,
+ 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d,
+ 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
+ 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0,
+ 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f,
+ 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321,
+ 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e,
+ 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a,
+ 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077,
+ 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd,
+ 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f,
+ 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+ 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c,
+ 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be,
+ 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1,
+ 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110,
+ 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d,
+ 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9,
+ 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b,
+ 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000,
+ 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
+ 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6,
+ 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296,
+ 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a,
+ 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d,
+ 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07,
+ 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b,
+ 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1,
+ 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24,
+ 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
+ 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48,
+ 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90,
+ 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81,
+ 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92,
+ 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7,
+ 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312,
+ 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978,
+ 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6,
+ 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
+ 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066,
+ 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98,
+ 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0,
+ 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f,
+ 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41,
+ 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61,
+ 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9,
+ 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce,
+ 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+ 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3,
+ 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3,
+ 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c,
+ 0xb8d04257,
+
+ // Tinv3
+ 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab,
+ 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76,
+ 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
+ 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe,
+ 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f,
+ 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174,
+ 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58,
+ 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace,
+ 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764,
+ 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45,
+ 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f,
+ 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+ 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf,
+ 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05,
+ 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a,
+ 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e,
+ 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54,
+ 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd,
+ 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19,
+ 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000,
+ 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
+ 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c,
+ 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee,
+ 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12,
+ 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09,
+ 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775,
+ 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66,
+ 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4,
+ 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a,
+ 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
+ 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894,
+ 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033,
+ 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5,
+ 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278,
+ 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739,
+ 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225,
+ 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826,
+ 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff,
+ 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
+ 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2,
+ 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804,
+ 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef,
+ 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c,
+ 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b,
+ 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7,
+ 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961,
+ 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14,
+ 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+ 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d,
+ 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c,
+ 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c,
+ 0xd04257b8};
+
+ private static int shift(int r, int shift)
+ {
+ return (r >>> shift) | (r << -shift);
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private static int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private static int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+ private static int subWord(int x)
+ {
+ int i0 = x, i1 = x >>> 8, i2 = x >>> 16, i3 = x >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ return i0 | i1 << 8 | i2 << 16 | i3 << 24;
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private int[][] generateWorkingKey(
+ byte[] key,
+ boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS+1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ int i = 0;
+ while (i < key.length)
+ {
+ W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
+ i+=4;
+ t++;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (i = KC; (i < k); i++)
+ {
+ int temp = W[(i - 1) >> 2][(i - 1) & 3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean forEncryption;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AESFastEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ unpackBlock(in, inOff);
+
+ if (forEncryption)
+ {
+ encryptBlock(WorkingKey);
+ }
+ else
+ {
+ decryptBlock(WorkingKey);
+ }
+
+ packBlock(out, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(byte[] bytes, int off)
+ {
+ this.C0 = Pack.littleEndianToInt(bytes, off);
+ this.C1 = Pack.littleEndianToInt(bytes, off + 4);
+ this.C2 = Pack.littleEndianToInt(bytes, off + 8);
+ this.C3 = Pack.littleEndianToInt(bytes, off + 12);
+ }
+
+ private void packBlock(byte[] bytes, int off)
+ {
+ Pack.intToLittleEndian(this.C0, bytes, off);
+ Pack.intToLittleEndian(this.C1, bytes, off + 4);
+ Pack.intToLittleEndian(this.C2, bytes, off + 8);
+ Pack.intToLittleEndian(this.C3, bytes, off + 12);
+ }
+
+ private void encryptBlock(int[][] KW)
+ {
+ int t0 = this.C0 ^ KW[0][0];
+ int t1 = this.C1 ^ KW[0][1];
+ int t2 = this.C2 ^ KW[0][2];
+
+ /*
+ * Fast engine has precomputed rotr(T0, 8/16/24) tables T1/T2/T3.
+ *
+ * Placing all precomputes in one array requires offsets additions for 8/16/24 rotations but
+ * avoids additional array range checks on 3 more arrays (which on HotSpot are more
+ * expensive than the offset additions).
+ */
+ int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+ int i0, i1, i2, i3;
+
+ while (r < ROUNDS - 1)
+ {
+ i0 = t0; i1 = t1 >>> 8; i2 = t2 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][0];
+
+ i0 = t1; i1 = t2 >>> 8; i2 = r3 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][1];
+
+ i0 = t2; i1 = r3 >>> 8; i2 = t0 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = t0 >>> 8; i2 = t1 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r++][3];
+
+ i0 = r0; i1 = r1 >>> 8; i2 = r2 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t0 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][0];
+
+ i0 = r1; i1 = r2 >>> 8; i2 = r3 >>> 16; i3 = r0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t1 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][1];
+
+ i0 = r2; i1 = r3 >>> 8; i2 = r0 >>> 16; i3 = r1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t2 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = r0 >>> 8; i2 = r1 >>> 16; i3 = r2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r++][3];
+ }
+
+ i0 = t0; i1 = t1 >>> 8; i2 = t2 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][0];
+
+ i0 = t1; i1 = t2 >>> 8; i2 = r3 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][1];
+
+ i0 = t2; i1 = r3 >>> 8; i2 = t0 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = t0 >>> 8; i2 = t1 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r++][3];
+
+ // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+ i0 = r0; i1 = r1 >>> 8; i2 = r2 >>> 16; i3 = r3 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
+
+ i0 = r1; i1 = r2 >>> 8; i2 = r3 >>> 16; i3 = r0 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
+
+ i0 = r2; i1 = r3 >>> 8; i2 = r0 >>> 16; i3 = r1 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
+
+ i0 = r3; i1 = r0 >>> 8; i2 = r1 >>> 16; i3 = r2 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
+ }
+
+ private void decryptBlock(int[][] KW)
+ {
+ int t0 = this.C0 ^ KW[ROUNDS][0];
+ int t1 = this.C1 ^ KW[ROUNDS][1];
+ int t2 = this.C2 ^ KW[ROUNDS][2];
+
+ int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+ int i0, i1, i2, i3;
+
+ while (r > 1)
+ {
+ i0 = t0; i1 = r3 >>> 8; i2 = t2 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][0];
+
+ i0 = t1; i1 = t0 >>> 8; i2 = r3 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][1];
+
+ i0 = t2; i1 = t1 >>> 8; i2 = t0 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = t2 >>> 8; i2 = t1 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r--][3];
+
+ i0 = r0; i1 = r3 >>> 8; i2 = r2 >>> 16; i3 = r1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t0 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][0];
+
+ i0 = r1; i1 = r0 >>> 8; i2 = r3 >>> 16; i3 = r2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t1 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][1];
+
+ i0 = r2; i1 = r1 >>> 8; i2 = r0 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t2 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = r2 >>> 8; i2 = r1 >>> 16; i3 = r0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r--][3];
+ }
+
+ i0 = t0; i1 = r3 >>> 8; i2 = t2 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][0];
+
+ i0 = t1; i1 = t0 >>> 8; i2 = r3 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][1];
+
+ i0 = t2; i1 = t1 >>> 8; i2 = t0 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][2];
+
+ i0 = r3; i1 = t2 >>> 8; i2 = t1 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][3];
+
+ // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+ i0 = r0; i1 = r3 >>> 8; i2 = r2 >>> 16; i3 = r1 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
+
+ i0 = r1; i1 = r0 >>> 8; i2 = r3 >>> 16; i3 = r2 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
+
+ i0 = r2; i1 = r1 >>> 8; i2 = r0 >>> 16; i3 = r3 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
+
+ i0 = r3; i1 = r2 >>> 8; i2 = r1 >>> 16; i3 = r0 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/AESLightEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/AESLightEngine.java
new file mode 100644
index 00000000..07b8e513
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/AESLightEngine.java
@@ -0,0 +1,434 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * an implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ *
+ * There are three levels of tradeoff of speed vs memory
+ * Because java has no preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
+ * and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
+ * adding 12 rotate operations per round to compute the values contained in the other tables from
+ * the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values
+ * in each round.
+ * <p>
+ * This file contains the slowest performance version with no static tables
+ * for round precomputation, but it has the smallest foot print.
+ *
+ */
+public class AESLightEngine
+ implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
+ (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
+ (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
+ (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
+ (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
+ (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
+ (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
+ (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
+ (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
+ (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
+ (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
+ (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
+ (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
+ (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
+ (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
+ (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
+ (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ // The inverse S-box
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56,
+ (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135,
+ (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61,
+ (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178,
+ (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22,
+ (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218,
+ (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10,
+ (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2,
+ (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234,
+ (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133,
+ (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137,
+ (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32,
+ (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49,
+ (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13,
+ (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176,
+ (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38,
+ (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ private static int shift(int r, int shift)
+ {
+ return (r >>> shift) | (r << -shift);
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private static int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ The following defines provide alternative definitions of FFmulX that might
+ give improved performance if a fast 32-bit multiply is not available.
+
+ private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
+ private static final int m4 = 0x1b1b1b1b;
+ private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
+
+ */
+
+ private static int mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ return f2 ^ shift(x ^ f2, 8) ^ shift(x, 16) ^ shift(x, 24);
+ }
+
+ private static int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+
+ private static int subWord(int x)
+ {
+ return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on key size and block size
+ * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
+ * This code is written assuming those are the only possible values
+ */
+ private int[][] generateWorkingKey(
+ byte[] key,
+ boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS+1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ int i = 0;
+ while (i < key.length)
+ {
+ W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
+ i+=4;
+ t++;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (i = KC; (i < k); i++)
+ {
+ int temp = W[(i-1)>>2][(i-1)&3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC)-1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean forEncryption;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AESLightEngine()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ C0 = (bytes[index++] & 0xff);
+ C0 |= (bytes[index++] & 0xff) << 8;
+ C0 |= (bytes[index++] & 0xff) << 16;
+ C0 |= bytes[index++] << 24;
+
+ C1 = (bytes[index++] & 0xff);
+ C1 |= (bytes[index++] & 0xff) << 8;
+ C1 |= (bytes[index++] & 0xff) << 16;
+ C1 |= bytes[index++] << 24;
+
+ C2 = (bytes[index++] & 0xff);
+ C2 |= (bytes[index++] & 0xff) << 8;
+ C2 |= (bytes[index++] & 0xff) << 16;
+ C2 |= bytes[index++] << 24;
+
+ C3 = (bytes[index++] & 0xff);
+ C3 |= (bytes[index++] & 0xff) << 8;
+ C3 |= (bytes[index++] & 0xff) << 16;
+ C3 |= bytes[index++] << 24;
+ }
+
+ private void packBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte)C0;
+ bytes[index++] = (byte)(C0 >> 8);
+ bytes[index++] = (byte)(C0 >> 16);
+ bytes[index++] = (byte)(C0 >> 24);
+
+ bytes[index++] = (byte)C1;
+ bytes[index++] = (byte)(C1 >> 8);
+ bytes[index++] = (byte)(C1 >> 16);
+ bytes[index++] = (byte)(C1 >> 24);
+
+ bytes[index++] = (byte)C2;
+ bytes[index++] = (byte)(C2 >> 8);
+ bytes[index++] = (byte)(C2 >> 16);
+ bytes[index++] = (byte)(C2 >> 24);
+
+ bytes[index++] = (byte)C3;
+ bytes[index++] = (byte)(C3 >> 8);
+ bytes[index++] = (byte)(C3 >> 16);
+ bytes[index++] = (byte)(C3 >> 24);
+ }
+
+ private void encryptBlock(int[][] KW)
+ {
+ int t0 = this.C0 ^ KW[0][0];
+ int t1 = this.C1 ^ KW[0][1];
+ int t2 = this.C2 ^ KW[0][2];
+
+ int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+ while (r < ROUNDS - 1)
+ {
+ r0 = mcol((S[t0&255]&255) ^ ((S[(t1>>8)&255]&255)<<8) ^ ((S[(t2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
+ r1 = mcol((S[t1&255]&255) ^ ((S[(t2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(t0>>24)&255]<<24)) ^ KW[r][1];
+ r2 = mcol((S[t2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(t0>>16)&255]&255)<<16) ^ (S[(t1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[r3&255]&255) ^ ((S[(t0>>8)&255]&255)<<8) ^ ((S[(t1>>16)&255]&255)<<16) ^ (S[(t2>>24)&255]<<24)) ^ KW[r++][3];
+ t0 = mcol((S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
+ t1 = mcol((S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24)) ^ KW[r][1];
+ t2 = mcol((S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24)) ^ KW[r++][3];
+ }
+
+ r0 = mcol((S[t0&255]&255) ^ ((S[(t1>>8)&255]&255)<<8) ^ ((S[(t2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
+ r1 = mcol((S[t1&255]&255) ^ ((S[(t2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(t0>>24)&255]<<24)) ^ KW[r][1];
+ r2 = mcol((S[t2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(t0>>16)&255]&255)<<16) ^ (S[(t1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[r3&255]&255) ^ ((S[(t0>>8)&255]&255)<<8) ^ ((S[(t1>>16)&255]&255)<<16) ^ (S[(t2>>24)&255]<<24)) ^ KW[r++][3];
+
+ // the final round is a simple function of S
+
+ this.C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
+ this.C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
+ this.C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+ this.C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+ }
+
+ private void decryptBlock(int[][] KW)
+ {
+ int t0 = this.C0 ^ KW[ROUNDS][0];
+ int t1 = this.C1 ^ KW[ROUNDS][1];
+ int t2 = this.C2 ^ KW[ROUNDS][2];
+
+ int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+ while (r > 1)
+ {
+ r0 = inv_mcol((Si[t0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(t2>>16)&255]&255)<<16) ^ (Si[(t1>>24)&255]<<24)) ^ KW[r][0];
+ r1 = inv_mcol((Si[t1&255]&255) ^ ((Si[(t0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(t2>>24)&255]<<24)) ^ KW[r][1];
+ r2 = inv_mcol((Si[t2&255]&255) ^ ((Si[(t1>>8)&255]&255)<<8) ^ ((Si[(t0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(t2>>8)&255]&255)<<8) ^ ((Si[(t1>>16)&255]&255)<<16) ^ (Si[(t0>>24)&255]<<24)) ^ KW[r--][3];
+ t0 = inv_mcol((Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24)) ^ KW[r][0];
+ t1 = inv_mcol((Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24)) ^ KW[r][1];
+ t2 = inv_mcol((Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24)) ^ KW[r--][3];
+ }
+
+ r0 = inv_mcol((Si[t0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(t2>>16)&255]&255)<<16) ^ (Si[(t1>>24)&255]<<24)) ^ KW[r][0];
+ r1 = inv_mcol((Si[t1&255]&255) ^ ((Si[(t0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(t2>>24)&255]<<24)) ^ KW[r][1];
+ r2 = inv_mcol((Si[t2&255]&255) ^ ((Si[(t1>>8)&255]&255)<<8) ^ ((Si[(t0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(t2>>8)&255]&255)<<8) ^ ((Si[(t1>>16)&255]&255)<<16) ^ (Si[(t0>>24)&255]<<24)) ^ KW[r][3];
+
+ // the final round's table is a simple function of Si
+
+ this.C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+ this.C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
+ this.C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
+ this.C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/AESWrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/AESWrapEngine.java
new file mode 100644
index 00000000..d2977948
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/AESWrapEngine.java
@@ -0,0 +1,16 @@
+package org.spongycastle.crypto.engines;
+
+/**
+ * an implementation of the AES Key Wrapper from the NIST Key Wrap
+ * Specification.
+ * <p>
+ * For further details see: <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ */
+public class AESWrapEngine
+ extends RFC3394WrapEngine
+{
+ public AESWrapEngine()
+ {
+ super(new AESEngine());
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/AESWrapPadEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/AESWrapPadEngine.java
new file mode 100644
index 00000000..03170ec6
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/AESWrapPadEngine.java
@@ -0,0 +1,10 @@
+package org.spongycastle.crypto.engines;
+
+public class AESWrapPadEngine
+ extends RFC5649WrapEngine
+{
+ public AESWrapPadEngine()
+ {
+ super(new AESEngine());
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/BlowfishEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/BlowfishEngine.java
new file mode 100644
index 00000000..e2f04779
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/BlowfishEngine.java
@@ -0,0 +1,577 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides Blowfish key encryption operations,
+ * such as encoding data and generating keys.
+ * All the algorithms herein are from Applied Cryptography
+ * and implement a simplified cryptography interface.
+ */
+public final class BlowfishEngine
+implements BlockCipher
+{
+ private final static int[]
+ KP = {
+ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
+ 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
+ 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
+ 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
+ 0x9216D5D9, 0x8979FB1B
+ },
+
+ KS0 = {
+ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
+ 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
+ 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
+ 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
+ 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
+ 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
+ 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
+ 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
+ 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+ 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
+ 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
+ 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
+ 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
+ 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
+ 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
+ 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
+ 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
+ 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+ 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
+ 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
+ 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
+ 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
+ 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
+ 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
+ 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
+ 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
+ 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+ 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
+ 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
+ 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
+ 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
+ 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
+ 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
+ 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
+ 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
+ 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+ 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
+ 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
+ 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
+ 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
+ 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
+ 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
+ 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
+ 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
+ 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+ 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
+ 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
+ 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
+ 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
+ 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
+ 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
+ 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
+ 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
+ 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+ 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
+ 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
+ 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
+ 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
+ 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
+ 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
+ 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
+ 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
+ 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+ 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
+ },
+
+ KS1 = {
+ 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
+ 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
+ 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
+ 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
+ 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
+ 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
+ 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
+ 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
+ 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
+ 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
+ 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
+ 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
+ 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
+ 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
+ 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
+ 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
+ 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
+ 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
+ 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
+ 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
+ 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
+ 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
+ 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
+ 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
+ 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
+ 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
+ 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
+ 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
+ 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
+ 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
+ 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
+ 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
+ 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
+ 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
+ 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
+ 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
+ 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
+ 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
+ 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
+ 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
+ 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
+ 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
+ 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
+ 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
+ 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
+ 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
+ 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
+ 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
+ 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
+ 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
+ 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
+ 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
+ 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
+ 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
+ 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
+ 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
+ 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
+ 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
+ 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
+ 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
+ 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
+ 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
+ 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
+ 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
+ },
+
+ KS2 = {
+ 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
+ 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
+ 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
+ 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
+ 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
+ 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
+ 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
+ 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
+ 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
+ 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
+ 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
+ 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
+ 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
+ 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
+ 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
+ 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
+ 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
+ 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
+ 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
+ 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
+ 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
+ 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
+ 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
+ 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
+ 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
+ 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
+ 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
+ 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
+ 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
+ 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
+ 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
+ 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
+ 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
+ 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
+ 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
+ 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
+ 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
+ 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
+ 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
+ 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
+ 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
+ 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
+ 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
+ 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
+ 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
+ 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
+ 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
+ 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
+ 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
+ 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
+ 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
+ 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
+ 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
+ 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
+ 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
+ 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
+ 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
+ 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
+ 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
+ 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
+ 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
+ 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
+ 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
+ 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
+ },
+
+ KS3 = {
+ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
+ 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
+ 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
+ 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
+ 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
+ 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
+ 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
+ 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
+ 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+ 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
+ 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
+ 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
+ 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
+ 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
+ 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
+ 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
+ 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
+ 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+ 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
+ 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
+ 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
+ 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
+ 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
+ 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
+ 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
+ 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
+ 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+ 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
+ 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
+ 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
+ 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
+ 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
+ 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
+ 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
+ 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
+ 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+ 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
+ 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
+ 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
+ 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
+ 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
+ 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
+ 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
+ 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
+ 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+ 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
+ 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
+ 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
+ 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
+ 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
+ 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
+ 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
+ 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
+ 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+ 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
+ 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
+ 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
+ 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
+ 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
+ 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
+ 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
+ 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
+ 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+ 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
+ };
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private static final int ROUNDS = 16;
+ private static final int BLOCK_SIZE = 8; // bytes = 64 bits
+ private static final int SBOX_SK = 256;
+ private static final int P_SZ = ROUNDS+2;
+
+ private final int[] S0, S1, S2, S3; // the s-boxes
+ private final int[] P; // the p-array
+
+ private boolean encrypting = false;
+
+ private byte[] workingKey = null;
+
+ public BlowfishEngine()
+ {
+ S0 = new int[SBOX_SK];
+ S1 = new int[SBOX_SK];
+ S2 = new int[SBOX_SK];
+ S3 = new int[SBOX_SK];
+ P = new int[P_SZ];
+ }
+
+ /**
+ * initialise a Blowfish cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.workingKey = ((KeyParameter)params).getKey();
+ setKey(this.workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Blowfish init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Blowfish";
+ }
+
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Blowfish not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private int F(int x)
+ {
+ return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff])
+ ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]);
+ }
+
+ /**
+ * apply the encryption cycle to each value pair in the table.
+ */
+ private void processTable(
+ int xl,
+ int xr,
+ int[] table)
+ {
+ int size = table.length;
+
+ for (int s = 0; s < size; s += 2)
+ {
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ table[s] = xr;
+ table[s + 1] = xl;
+
+ xr = xl; // end of cycle swap
+ xl = table[s];
+ }
+ }
+
+ private void setKey(byte[] key)
+ {
+ /*
+ * - comments are from _Applied Crypto_, Schneier, p338
+ * please be careful comparing the two, AC numbers the
+ * arrays from 1, the enclosed code from 0.
+ *
+ * (1)
+ * Initialise the S-boxes and the P-array, with a fixed string
+ * This string contains the hexadecimal digits of pi (3.141...)
+ */
+ System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
+ System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
+ System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
+ System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
+
+ System.arraycopy(KP, 0, P, 0, P_SZ);
+
+ /*
+ * (2)
+ * Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with the
+ * second 32-bits of the key, and so on for all bits of the key
+ * (up to P[17]). Repeatedly cycle through the key bits until the
+ * entire P-array has been XOR-ed with the key bits
+ */
+ int keyLength = key.length;
+ int keyIndex = 0;
+
+ for (int i=0; i < P_SZ; i++)
+ {
+ // get the 32 bits of the key, in 4 * 8 bit chunks
+ int data = 0x0000000;
+ for (int j=0; j < 4; j++)
+ {
+ // create a 32 bit block
+ data = (data << 8) | (key[keyIndex++] & 0xff);
+
+ // wrap when we get to the end of the key
+ if (keyIndex >= keyLength)
+ {
+ keyIndex = 0;
+ }
+ }
+ // XOR the newly created 32 bit chunk onto the P-array
+ P[i] ^= data;
+ }
+
+ /*
+ * (3)
+ * Encrypt the all-zero string with the Blowfish algorithm, using
+ * the subkeys described in (1) and (2)
+ *
+ * (4)
+ * Replace P1 and P2 with the output of step (3)
+ *
+ * (5)
+ * Encrypt the output of step(3) using the Blowfish algorithm,
+ * with the modified subkeys.
+ *
+ * (6)
+ * Replace P3 and P4 with the output of step (5)
+ *
+ * (7)
+ * Continue the process, replacing all elements of the P-array
+ * and then all four S-boxes in order, with the output of the
+ * continuously changing Blowfish algorithm
+ */
+
+ processTable(0, 0, P);
+ processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
+ processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
+ processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
+ processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex+4);
+
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex + 4);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex + 4);
+
+ xl ^= P[ROUNDS + 1];
+
+ for (int i = ROUNDS; i > 0 ; i -= 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i - 1];
+ }
+
+ xr ^= P[0];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex+4);
+ }
+
+ private int BytesTo32bits(byte[] b, int i)
+ {
+ return ((b[i] & 0xff) << 24) |
+ ((b[i+1] & 0xff) << 16) |
+ ((b[i+2] & 0xff) << 8) |
+ ((b[i+3] & 0xff));
+ }
+
+ private void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset + 3] = (byte)in;
+ b[offset + 2] = (byte)(in >> 8);
+ b[offset + 1] = (byte)(in >> 16);
+ b[offset] = (byte)(in >> 24);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/CAST5Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/CAST5Engine.java
new file mode 100644
index 00000000..7b51fa48
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/CAST5Engine.java
@@ -0,0 +1,831 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides CAST key encryption operations,
+ * such as encoding data and generating keys.
+ *
+ * All the algorithms herein are from the Internet RFC's
+ *
+ * RFC2144 - CAST5 (64bit block, 40-128bit key)
+ * RFC2612 - CAST6 (128bit block, 128-256bit key)
+ *
+ * and implement a simplified cryptography interface.
+ */
+public class CAST5Engine
+ implements BlockCipher
+{
+ protected final static int M32 = 0xffffffff;
+
+ protected final static int[]
+ S1 = {
+0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
+0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
+0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
+0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
+0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
+0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
+0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
+0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
+0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
+0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
+0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
+0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
+0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
+0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
+0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
+0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
+0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
+0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
+0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
+0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
+0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
+0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
+0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
+0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
+0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
+0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
+0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
+0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
+0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
+0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
+0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
+0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf
+ },
+ S2 = {
+0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
+0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
+0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
+0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
+0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
+0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
+0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
+0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
+0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
+0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
+0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
+0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
+0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
+0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
+0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
+0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
+0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
+0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
+0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
+0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
+0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
+0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
+0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
+0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
+0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
+0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
+0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
+0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
+0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
+0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
+0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
+0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1
+ },
+ S3 = {
+0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
+0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
+0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
+0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
+0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
+0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
+0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
+0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
+0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
+0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
+0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
+0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
+0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
+0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
+0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
+0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
+0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
+0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
+0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
+0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
+0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
+0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
+0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
+0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
+0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
+0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
+0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
+0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
+0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
+0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
+0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
+0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783
+ },
+ S4 = {
+0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
+0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
+0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
+0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
+0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
+0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
+0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
+0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
+0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
+0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
+0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
+0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
+0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
+0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
+0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
+0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
+0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
+0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
+0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
+0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
+0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
+0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
+0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
+0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
+0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
+0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
+0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
+0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
+0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
+0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
+0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
+0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2
+ },
+ S5 = {
+0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
+0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
+0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
+0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
+0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
+0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
+0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
+0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
+0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
+0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
+0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
+0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
+0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
+0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
+0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
+0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
+0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
+0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
+0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
+0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
+0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
+0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
+0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
+0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
+0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
+0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
+0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
+0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
+0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
+0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
+0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
+0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4
+ },
+ S6 = {
+0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
+0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
+0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
+0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
+0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
+0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
+0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
+0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
+0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
+0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
+0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
+0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
+0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
+0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
+0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
+0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
+0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
+0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
+0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
+0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
+0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
+0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
+0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
+0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
+0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
+0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
+0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
+0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
+0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
+0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
+0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
+0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f
+ },
+ S7 = {
+0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
+0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
+0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
+0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
+0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
+0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
+0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
+0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
+0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
+0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
+0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
+0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
+0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
+0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
+0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
+0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
+0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
+0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
+0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
+0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
+0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
+0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
+0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
+0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
+0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
+0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
+0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
+0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
+0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
+0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
+0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
+0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3
+ },
+ S8 = {
+0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
+0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
+0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
+0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
+0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
+0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
+0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
+0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
+0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
+0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
+0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
+0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
+0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
+0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
+0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
+0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
+0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
+0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
+0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
+0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
+0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
+0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
+0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
+0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
+0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
+0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
+0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
+0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
+0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
+0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
+0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
+0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e
+ };
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ protected static final int MAX_ROUNDS = 16;
+ protected static final int RED_ROUNDS = 12;
+
+ protected static final int BLOCK_SIZE = 8; // bytes = 64 bits
+
+ protected int _Kr[] = new int[17]; // the rotating round key
+ protected int _Km[] = new int[17]; // the masking round key
+
+ private boolean _encrypting = false;
+
+ private byte[] _workingKey = null;
+ private int _rounds = MAX_ROUNDS;
+
+ public CAST5Engine()
+ {
+ }
+
+ /**
+ * initialise a CAST cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ _encrypting = encrypting;
+ _workingKey = ((KeyParameter)params).getKey();
+
+ setKey(_workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("Invalid parameter passed to "+getAlgorithmName()+" init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "CAST5";
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (_workingKey == null)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ int blockSize = getBlockSize();
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
+
+ if (_encrypting)
+ {
+ return encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ return decryptBlock(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ /*
+ * Creates the subkeys using the same nomenclature
+ * as described in RFC2144.
+ *
+ * See section 2.4
+ */
+ protected void setKey(byte[] key)
+ {
+ /*
+ * Determine the key size here, if required
+ *
+ * if keysize <= 80bits, use 12 rounds instead of 16
+ * if keysize < 128bits, pad with 0
+ *
+ * Typical key sizes => 40, 64, 80, 128
+ */
+
+ if (key.length < 11)
+ {
+ _rounds = RED_ROUNDS;
+ }
+
+ int z[] = new int[16];
+ int x[] = new int[16];
+
+ int z03, z47, z8B, zCF;
+ int x03, x47, x8B, xCF;
+
+ /* copy the key into x */
+ for (int i=0; i< key.length; i++)
+ {
+ x[i] = key[i] & 0xff;
+ }
+
+ /*
+ * This will look different because the selection of
+ * bytes from the input key I've already chosen the
+ * correct int.
+ */
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Km[ 1]= S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]];
+ _Km[ 2]= S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]];
+ _Km[ 3]= S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]];
+ _Km[ 4]= S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]];
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Km[ 5]= S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]];
+ _Km[ 6]= S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]];
+ _Km[ 7]= S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]];
+ _Km[ 8]= S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]];
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Km[ 9]= S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]];
+ _Km[10]= S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]];
+ _Km[11]= S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]];
+ _Km[12]= S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]];
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Km[13]= S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]];
+ _Km[14]= S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]];
+ _Km[15]= S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]];
+ _Km[16]= S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]];
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Kr[ 1]=(S5[z[0x8]]^S6[z[0x9]]^S7[z[0x7]]^S8[z[0x6]] ^ S5[z[0x2]])&0x1f;
+ _Kr[ 2]=(S5[z[0xA]]^S6[z[0xB]]^S7[z[0x5]]^S8[z[0x4]] ^ S6[z[0x6]])&0x1f;
+ _Kr[ 3]=(S5[z[0xC]]^S6[z[0xD]]^S7[z[0x3]]^S8[z[0x2]] ^ S7[z[0x9]])&0x1f;
+ _Kr[ 4]=(S5[z[0xE]]^S6[z[0xF]]^S7[z[0x1]]^S8[z[0x0]] ^ S8[z[0xC]])&0x1f;
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Kr[ 5]=(S5[x[0x3]]^S6[x[0x2]]^S7[x[0xC]]^S8[x[0xD]]^S5[x[0x8]])&0x1f;
+ _Kr[ 6]=(S5[x[0x1]]^S6[x[0x0]]^S7[x[0xE]]^S8[x[0xF]]^S6[x[0xD]])&0x1f;
+ _Kr[ 7]=(S5[x[0x7]]^S6[x[0x6]]^S7[x[0x8]]^S8[x[0x9]]^S7[x[0x3]])&0x1f;
+ _Kr[ 8]=(S5[x[0x5]]^S6[x[0x4]]^S7[x[0xA]]^S8[x[0xB]]^S8[x[0x7]])&0x1f;
+
+ x03 = IntsTo32bits(x, 0x0);
+ x47 = IntsTo32bits(x, 0x4);
+ x8B = IntsTo32bits(x, 0x8);
+ xCF = IntsTo32bits(x, 0xC);
+ z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]];
+ Bits32ToInts(z03, z, 0x0);
+ z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]];
+ Bits32ToInts(z47, z, 0x4);
+ z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]];
+ Bits32ToInts(z8B, z, 0x8);
+ zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]];
+ Bits32ToInts(zCF, z, 0xC);
+ _Kr[ 9]=(S5[z[0x3]]^S6[z[0x2]]^S7[z[0xC]]^S8[z[0xD]]^S5[z[0x9]])&0x1f;
+ _Kr[10]=(S5[z[0x1]]^S6[z[0x0]]^S7[z[0xE]]^S8[z[0xF]]^S6[z[0xc]])&0x1f;
+ _Kr[11]=(S5[z[0x7]]^S6[z[0x6]]^S7[z[0x8]]^S8[z[0x9]]^S7[z[0x2]])&0x1f;
+ _Kr[12]=(S5[z[0x5]]^S6[z[0x4]]^S7[z[0xA]]^S8[z[0xB]]^S8[z[0x6]])&0x1f;
+
+ z03 = IntsTo32bits(z, 0x0);
+ z47 = IntsTo32bits(z, 0x4);
+ z8B = IntsTo32bits(z, 0x8);
+ zCF = IntsTo32bits(z, 0xC);
+ x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]];
+ Bits32ToInts(x03, x, 0x0);
+ x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]];
+ Bits32ToInts(x47, x, 0x4);
+ x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]];
+ Bits32ToInts(x8B, x, 0x8);
+ xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]];
+ Bits32ToInts(xCF, x, 0xC);
+ _Kr[13]=(S5[x[0x8]]^S6[x[0x9]]^S7[x[0x7]]^S8[x[0x6]]^S5[x[0x3]])&0x1f;
+ _Kr[14]=(S5[x[0xA]]^S6[x[0xB]]^S7[x[0x5]]^S8[x[0x4]]^S6[x[0x7]])&0x1f;
+ _Kr[15]=(S5[x[0xC]]^S6[x[0xD]]^S7[x[0x3]]^S8[x[0x2]]^S7[x[0x8]])&0x1f;
+ _Kr[16]=(S5[x[0xE]]^S6[x[0xF]]^S7[x[0x1]]^S8[x[0x0]]^S8[x[0xD]])&0x1f;
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+
+ int result[] = new int[2];
+
+ // process the input block
+ // batch the units up into a 32 bit chunk and go for it
+ // the array is in bytes, the increment is 8x8 bits = 64
+
+ int L0 = BytesTo32bits(src, srcIndex);
+ int R0 = BytesTo32bits(src, srcIndex + 4);
+
+ CAST_Encipher(L0, R0, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex + 4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int result[] = new int[2];
+
+ // process the input block
+ // batch the units up into a 32 bit chunk and go for it
+ // the array is in bytes, the increment is 8x8 bits = 64
+ int L16 = BytesTo32bits(src, srcIndex);
+ int R16 = BytesTo32bits(src, srcIndex+4);
+
+ CAST_Decipher(L16, R16, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex+4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * The first of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ protected final int F1(int D, int Kmi, int Kri)
+ {
+ int I = Kmi + D;
+ I = I << Kri | I >>> (32-Kri);
+ return ((S1[(I>>>24)&0xff]^S2[(I>>>16)&0xff])-S3[(I>>> 8)&0xff])+
+ S4[I & 0xff];
+ }
+
+ /**
+ * The second of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ protected final int F2(int D, int Kmi, int Kri)
+ {
+ int I = Kmi ^ D;
+ I = I << Kri | I >>> (32-Kri);
+ return ((S1[(I>>>24)&0xff]-S2[(I>>>16)&0xff])+S3[(I>>> 8)&0xff])^
+ S4[I & 0xff];
+ }
+
+ /**
+ * The third of the three processing functions for the
+ * encryption and decryption.
+ *
+ * @param D the input to be processed
+ * @param Kmi the mask to be used from Km[n]
+ * @param Kri the rotation value to be used
+ *
+ */
+ protected final int F3(int D, int Kmi, int Kri)
+ {
+ int I = Kmi - D;
+ I = I << Kri | I >>> (32-Kri);
+ return ((S1[(I>>>24)&0xff]+S2[(I>>>16)&0xff])^S3[(I>>> 8)&0xff])-
+ S4[I & 0xff];
+ }
+
+ /**
+ * Does the 16 rounds to encrypt the block.
+ *
+ * @param L0 the LH-32bits of the plaintext block
+ * @param R0 the RH-32bits of the plaintext block
+ */
+ protected final void CAST_Encipher(int L0, int R0, int result[])
+ {
+ int Lp = L0; // the previous value, equiv to L[i-1]
+ int Rp = R0; // equivalent to R[i-1]
+
+ /*
+ * numbering consistent with paper to make
+ * checking and validating easier
+ */
+ int Li = L0, Ri = R0;
+
+ for (int i = 1; i<=_rounds ; i++)
+ {
+ Lp = Li;
+ Rp = Ri;
+
+ Li = Rp;
+ switch (i)
+ {
+ case 1:
+ case 4:
+ case 7:
+ case 10:
+ case 13:
+ case 16:
+ Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+ break;
+ case 2:
+ case 5:
+ case 8:
+ case 11:
+ case 14:
+ Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+ break;
+ }
+ }
+
+ result[0] = Ri;
+ result[1] = Li;
+
+ return;
+ }
+
+ protected final void CAST_Decipher(int L16, int R16, int result[])
+ {
+ int Lp = L16; // the previous value, equiv to L[i-1]
+ int Rp = R16; // equivalent to R[i-1]
+
+ /*
+ * numbering consistent with paper to make
+ * checking and validating easier
+ */
+ int Li = L16, Ri = R16;
+
+ for (int i = _rounds; i > 0; i--)
+ {
+ Lp = Li;
+ Rp = Ri;
+
+ Li = Rp;
+ switch (i)
+ {
+ case 1:
+ case 4:
+ case 7:
+ case 10:
+ case 13:
+ case 16:
+ Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+ break;
+ case 2:
+ case 5:
+ case 8:
+ case 11:
+ case 14:
+ Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+ break;
+ }
+ }
+
+ result[0] = Ri;
+ result[1] = Li;
+
+ return;
+ }
+
+ protected final void Bits32ToInts(int in, int[] b, int offset)
+ {
+ b[offset + 3] = (in & 0xff);
+ b[offset + 2] = ((in >>> 8) & 0xff);
+ b[offset + 1] = ((in >>> 16) & 0xff);
+ b[offset] = ((in >>> 24) & 0xff);
+ }
+
+ protected final int IntsTo32bits(int[] b, int i)
+ {
+ int rv = 0;
+
+ rv = ((b[i] & 0xff) << 24) |
+ ((b[i+1] & 0xff) << 16) |
+ ((b[i+2] & 0xff) << 8) |
+ ((b[i+3] & 0xff));
+
+ return rv;
+ }
+
+ protected final void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset + 3] = (byte)in;
+ b[offset + 2] = (byte)(in >>> 8);
+ b[offset + 1] = (byte)(in >>> 16);
+ b[offset] = (byte)(in >>> 24);
+ }
+
+ protected final int BytesTo32bits(byte[] b, int i)
+ {
+ return ((b[i] & 0xff) << 24) |
+ ((b[i+1] & 0xff) << 16) |
+ ((b[i+2] & 0xff) << 8) |
+ ((b[i+3] & 0xff));
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/CAST6Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/CAST6Engine.java
new file mode 100644
index 00000000..7a990756
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/CAST6Engine.java
@@ -0,0 +1,296 @@
+package org.spongycastle.crypto.engines;
+
+
+/**
+ * A class that provides CAST6 key encryption operations,
+ * such as encoding data and generating keys.
+ *
+ * All the algorithms herein are from the Internet RFC
+ *
+ * RFC2612 - CAST6 (128bit block, 128-256bit key)
+ *
+ * and implement a simplified cryptography interface.
+ */
+public final class CAST6Engine extends CAST5Engine
+{
+ //====================================
+ // Useful constants
+ //====================================
+
+ protected static final int ROUNDS = 12;
+
+ protected static final int BLOCK_SIZE = 16; // bytes = 128 bits
+
+ /*
+ * Put the round and mask keys into an array.
+ * Kr0[i] => _Kr[i*4 + 0]
+ */
+ protected int _Kr[] = new int[ROUNDS*4]; // the rotating round key(s)
+ protected int _Km[] = new int[ROUNDS*4]; // the masking round key(s)
+
+ /*
+ * Key setup
+ */
+ protected int _Tr[] = new int[24 * 8];
+ protected int _Tm[] = new int[24 * 8];
+
+ private int[] _workingKey = new int[8];
+
+ public CAST6Engine()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "CAST6";
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ /*
+ * Creates the subkeys using the same nomenclature
+ * as described in RFC2612.
+ *
+ * See section 2.4
+ */
+ protected void setKey(byte[] key)
+ {
+ int Cm = 0x5a827999;
+ int Mm = 0x6ed9eba1;
+ int Cr = 19;
+ int Mr = 17;
+
+ /*
+ * Determine the key size here, if required
+ *
+ * if keysize < 256 bytes, pad with 0
+ *
+ * Typical key sizes => 128, 160, 192, 224, 256
+ */
+ for (int i=0; i< 24; i++)
+ {
+ for (int j=0; j< 8; j++)
+ {
+ _Tm[i*8 + j] = Cm;
+ Cm = (Cm + Mm); // mod 2^32;
+
+ _Tr[i*8 + j] = Cr;
+ Cr = (Cr + Mr) & 0x1f; // mod 32
+ }
+ }
+
+ byte[] tmpKey = new byte[64];
+ int length = key.length;
+ System.arraycopy(key, 0, tmpKey, 0, length);
+
+ // now create ABCDEFGH
+ for (int i=0; i< 8; i++)
+ {
+ _workingKey[i] = BytesTo32bits(tmpKey, i*4);
+ }
+
+ // Generate the key schedule
+ for (int i=0; i< 12; i++)
+ {
+ // KAPPA <- W2i(KAPPA)
+ int i2 = i*2 *8;
+ _workingKey[6] ^= F1(_workingKey[7], _Tm[i2 ], _Tr[i2 ]);
+ _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
+ _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
+ _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
+ _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
+ _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
+ _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
+ _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);
+
+ // KAPPA <- W2i+1(KAPPA)
+ i2 = (i*2 + 1)*8;
+ _workingKey[6] ^= F1(_workingKey[7], _Tm[i2 ], _Tr[i2 ]);
+ _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]);
+ _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]);
+ _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]);
+ _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]);
+ _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]);
+ _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]);
+ _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]);
+
+ // Kr_(i) <- KAPPA
+ _Kr[i*4 ] = _workingKey[0] & 0x1f;
+ _Kr[i*4 + 1] = _workingKey[2] & 0x1f;
+ _Kr[i*4 + 2] = _workingKey[4] & 0x1f;
+ _Kr[i*4 + 3] = _workingKey[6] & 0x1f;
+
+
+ // Km_(i) <- KAPPA
+ _Km[i*4 ] = _workingKey[7];
+ _Km[i*4 + 1] = _workingKey[5];
+ _Km[i*4 + 2] = _workingKey[3];
+ _Km[i*4 + 3] = _workingKey[1];
+ }
+
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+
+ int result[] = new int[4];
+
+ // process the input block
+ // batch the units up into 4x32 bit chunks and go for it
+
+ int A = BytesTo32bits(src, srcIndex);
+ int B = BytesTo32bits(src, srcIndex + 4);
+ int C = BytesTo32bits(src, srcIndex + 8);
+ int D = BytesTo32bits(src, srcIndex + 12);
+
+ CAST_Encipher(A, B, C, D, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex + 4);
+ Bits32ToBytes(result[2], dst, dstIndex + 8);
+ Bits32ToBytes(result[3], dst, dstIndex + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ *
+ * @param src The plaintext buffer
+ * @param srcIndex An offset into src
+ * @param dst The ciphertext buffer
+ * @param dstIndex An offset into dst
+ */
+ protected int decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int result[] = new int[4];
+
+ // process the input block
+ // batch the units up into 4x32 bit chunks and go for it
+ int A = BytesTo32bits(src, srcIndex);
+ int B = BytesTo32bits(src, srcIndex + 4);
+ int C = BytesTo32bits(src, srcIndex + 8);
+ int D = BytesTo32bits(src, srcIndex + 12);
+
+ CAST_Decipher(A, B, C, D, result);
+
+ // now stuff them into the destination block
+ Bits32ToBytes(result[0], dst, dstIndex);
+ Bits32ToBytes(result[1], dst, dstIndex + 4);
+ Bits32ToBytes(result[2], dst, dstIndex + 8);
+ Bits32ToBytes(result[3], dst, dstIndex + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Does the 12 quad rounds rounds to encrypt the block.
+ *
+ * @param A the 00-31 bits of the plaintext block
+ * @param B the 32-63 bits of the plaintext block
+ * @param C the 64-95 bits of the plaintext block
+ * @param D the 96-127 bits of the plaintext block
+ * @param result the resulting ciphertext
+ */
+ protected final void CAST_Encipher(int A, int B, int C, int D,int result[])
+ {
+ int x;
+ for (int i=0; i< 6; i++)
+ {
+ x = i*4;
+ // BETA <- Qi(BETA)
+ C ^= F1(D, _Km[x], _Kr[x]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+
+ }
+
+ for (int i=6; i<12; i++)
+ {
+ x = i*4;
+ // BETA <- QBARi(BETA)
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ C ^= F1(D, _Km[x], _Kr[x]);
+
+ }
+
+ result[0] = A;
+ result[1] = B;
+ result[2] = C;
+ result[3] = D;
+ }
+
+ /**
+ * Does the 12 quad rounds rounds to decrypt the block.
+ *
+ * @param A the 00-31 bits of the ciphertext block
+ * @param B the 32-63 bits of the ciphertext block
+ * @param C the 64-95 bits of the ciphertext block
+ * @param D the 96-127 bits of the ciphertext block
+ * @param result the resulting plaintext
+ */
+ protected final void CAST_Decipher(int A, int B, int C, int D,int result[])
+ {
+ int x;
+ for (int i=0; i< 6; i++)
+ {
+ x = (11-i)*4;
+ // BETA <- Qi(BETA)
+ C ^= F1(D, _Km[x], _Kr[x]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+
+ }
+
+ for (int i=6; i<12; i++)
+ {
+ x = (11-i)*4;
+ // BETA <- QBARi(BETA)
+ D ^= F1(A, _Km[x + 3], _Kr[x + 3]);
+ A ^= F3(B, _Km[x + 2], _Kr[x + 2]);
+ B ^= F2(C, _Km[x + 1], _Kr[x + 1]);
+ C ^= F1(D, _Km[x], _Kr[x]);
+
+ }
+
+ result[0] = A;
+ result[1] = B;
+ result[2] = C;
+ result[3] = D;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/CamelliaEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/CamelliaEngine.java
new file mode 100644
index 00000000..b82af072
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/CamelliaEngine.java
@@ -0,0 +1,684 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * Camellia - based on RFC 3713.
+ */
+public class CamelliaEngine
+ implements BlockCipher
+{
+ private boolean initialised = false;
+ private boolean _keyIs128;
+
+ private static final int BLOCK_SIZE = 16;
+ private static final int MASK8 = 0xff;
+
+ private int[] subkey = new int[24 * 4];
+ private int[] kw = new int[4 * 2]; // for whitening
+ private int[] ke = new int[6 * 2]; // for FL and FL^(-1)
+ private int[] state = new int[4]; // for encryption and decryption
+
+ private static final int SIGMA[] = {
+ 0xa09e667f, 0x3bcc908b,
+ 0xb67ae858, 0x4caa73b2,
+ 0xc6ef372f, 0xe94f82be,
+ 0x54ff53a5, 0xf1d36f1c,
+ 0x10e527fa, 0xde682d1d,
+ 0xb05688c2, 0xb3e6c1fd
+ };
+
+ /*
+ *
+ * S-box data
+ *
+ */
+ private static final int SBOX1_1110[] = {
+ 0x70707000, 0x82828200, 0x2c2c2c00, 0xececec00, 0xb3b3b300, 0x27272700,
+ 0xc0c0c000, 0xe5e5e500, 0xe4e4e400, 0x85858500, 0x57575700, 0x35353500,
+ 0xeaeaea00, 0x0c0c0c00, 0xaeaeae00, 0x41414100, 0x23232300, 0xefefef00,
+ 0x6b6b6b00, 0x93939300, 0x45454500, 0x19191900, 0xa5a5a500, 0x21212100,
+ 0xededed00, 0x0e0e0e00, 0x4f4f4f00, 0x4e4e4e00, 0x1d1d1d00, 0x65656500,
+ 0x92929200, 0xbdbdbd00, 0x86868600, 0xb8b8b800, 0xafafaf00, 0x8f8f8f00,
+ 0x7c7c7c00, 0xebebeb00, 0x1f1f1f00, 0xcecece00, 0x3e3e3e00, 0x30303000,
+ 0xdcdcdc00, 0x5f5f5f00, 0x5e5e5e00, 0xc5c5c500, 0x0b0b0b00, 0x1a1a1a00,
+ 0xa6a6a600, 0xe1e1e100, 0x39393900, 0xcacaca00, 0xd5d5d500, 0x47474700,
+ 0x5d5d5d00, 0x3d3d3d00, 0xd9d9d900, 0x01010100, 0x5a5a5a00, 0xd6d6d600,
+ 0x51515100, 0x56565600, 0x6c6c6c00, 0x4d4d4d00, 0x8b8b8b00, 0x0d0d0d00,
+ 0x9a9a9a00, 0x66666600, 0xfbfbfb00, 0xcccccc00, 0xb0b0b000, 0x2d2d2d00,
+ 0x74747400, 0x12121200, 0x2b2b2b00, 0x20202000, 0xf0f0f000, 0xb1b1b100,
+ 0x84848400, 0x99999900, 0xdfdfdf00, 0x4c4c4c00, 0xcbcbcb00, 0xc2c2c200,
+ 0x34343400, 0x7e7e7e00, 0x76767600, 0x05050500, 0x6d6d6d00, 0xb7b7b700,
+ 0xa9a9a900, 0x31313100, 0xd1d1d100, 0x17171700, 0x04040400, 0xd7d7d700,
+ 0x14141400, 0x58585800, 0x3a3a3a00, 0x61616100, 0xdedede00, 0x1b1b1b00,
+ 0x11111100, 0x1c1c1c00, 0x32323200, 0x0f0f0f00, 0x9c9c9c00, 0x16161600,
+ 0x53535300, 0x18181800, 0xf2f2f200, 0x22222200, 0xfefefe00, 0x44444400,
+ 0xcfcfcf00, 0xb2b2b200, 0xc3c3c300, 0xb5b5b500, 0x7a7a7a00, 0x91919100,
+ 0x24242400, 0x08080800, 0xe8e8e800, 0xa8a8a800, 0x60606000, 0xfcfcfc00,
+ 0x69696900, 0x50505000, 0xaaaaaa00, 0xd0d0d000, 0xa0a0a000, 0x7d7d7d00,
+ 0xa1a1a100, 0x89898900, 0x62626200, 0x97979700, 0x54545400, 0x5b5b5b00,
+ 0x1e1e1e00, 0x95959500, 0xe0e0e000, 0xffffff00, 0x64646400, 0xd2d2d200,
+ 0x10101000, 0xc4c4c400, 0x00000000, 0x48484800, 0xa3a3a300, 0xf7f7f700,
+ 0x75757500, 0xdbdbdb00, 0x8a8a8a00, 0x03030300, 0xe6e6e600, 0xdadada00,
+ 0x09090900, 0x3f3f3f00, 0xdddddd00, 0x94949400, 0x87878700, 0x5c5c5c00,
+ 0x83838300, 0x02020200, 0xcdcdcd00, 0x4a4a4a00, 0x90909000, 0x33333300,
+ 0x73737300, 0x67676700, 0xf6f6f600, 0xf3f3f300, 0x9d9d9d00, 0x7f7f7f00,
+ 0xbfbfbf00, 0xe2e2e200, 0x52525200, 0x9b9b9b00, 0xd8d8d800, 0x26262600,
+ 0xc8c8c800, 0x37373700, 0xc6c6c600, 0x3b3b3b00, 0x81818100, 0x96969600,
+ 0x6f6f6f00, 0x4b4b4b00, 0x13131300, 0xbebebe00, 0x63636300, 0x2e2e2e00,
+ 0xe9e9e900, 0x79797900, 0xa7a7a700, 0x8c8c8c00, 0x9f9f9f00, 0x6e6e6e00,
+ 0xbcbcbc00, 0x8e8e8e00, 0x29292900, 0xf5f5f500, 0xf9f9f900, 0xb6b6b600,
+ 0x2f2f2f00, 0xfdfdfd00, 0xb4b4b400, 0x59595900, 0x78787800, 0x98989800,
+ 0x06060600, 0x6a6a6a00, 0xe7e7e700, 0x46464600, 0x71717100, 0xbababa00,
+ 0xd4d4d400, 0x25252500, 0xababab00, 0x42424200, 0x88888800, 0xa2a2a200,
+ 0x8d8d8d00, 0xfafafa00, 0x72727200, 0x07070700, 0xb9b9b900, 0x55555500,
+ 0xf8f8f800, 0xeeeeee00, 0xacacac00, 0x0a0a0a00, 0x36363600, 0x49494900,
+ 0x2a2a2a00, 0x68686800, 0x3c3c3c00, 0x38383800, 0xf1f1f100, 0xa4a4a400,
+ 0x40404000, 0x28282800, 0xd3d3d300, 0x7b7b7b00, 0xbbbbbb00, 0xc9c9c900,
+ 0x43434300, 0xc1c1c100, 0x15151500, 0xe3e3e300, 0xadadad00, 0xf4f4f400,
+ 0x77777700, 0xc7c7c700, 0x80808000, 0x9e9e9e00
+ };
+
+ private static final int SBOX4_4404[] = {
+ 0x70700070, 0x2c2c002c, 0xb3b300b3, 0xc0c000c0, 0xe4e400e4, 0x57570057,
+ 0xeaea00ea, 0xaeae00ae, 0x23230023, 0x6b6b006b, 0x45450045, 0xa5a500a5,
+ 0xeded00ed, 0x4f4f004f, 0x1d1d001d, 0x92920092, 0x86860086, 0xafaf00af,
+ 0x7c7c007c, 0x1f1f001f, 0x3e3e003e, 0xdcdc00dc, 0x5e5e005e, 0x0b0b000b,
+ 0xa6a600a6, 0x39390039, 0xd5d500d5, 0x5d5d005d, 0xd9d900d9, 0x5a5a005a,
+ 0x51510051, 0x6c6c006c, 0x8b8b008b, 0x9a9a009a, 0xfbfb00fb, 0xb0b000b0,
+ 0x74740074, 0x2b2b002b, 0xf0f000f0, 0x84840084, 0xdfdf00df, 0xcbcb00cb,
+ 0x34340034, 0x76760076, 0x6d6d006d, 0xa9a900a9, 0xd1d100d1, 0x04040004,
+ 0x14140014, 0x3a3a003a, 0xdede00de, 0x11110011, 0x32320032, 0x9c9c009c,
+ 0x53530053, 0xf2f200f2, 0xfefe00fe, 0xcfcf00cf, 0xc3c300c3, 0x7a7a007a,
+ 0x24240024, 0xe8e800e8, 0x60600060, 0x69690069, 0xaaaa00aa, 0xa0a000a0,
+ 0xa1a100a1, 0x62620062, 0x54540054, 0x1e1e001e, 0xe0e000e0, 0x64640064,
+ 0x10100010, 0x00000000, 0xa3a300a3, 0x75750075, 0x8a8a008a, 0xe6e600e6,
+ 0x09090009, 0xdddd00dd, 0x87870087, 0x83830083, 0xcdcd00cd, 0x90900090,
+ 0x73730073, 0xf6f600f6, 0x9d9d009d, 0xbfbf00bf, 0x52520052, 0xd8d800d8,
+ 0xc8c800c8, 0xc6c600c6, 0x81810081, 0x6f6f006f, 0x13130013, 0x63630063,
+ 0xe9e900e9, 0xa7a700a7, 0x9f9f009f, 0xbcbc00bc, 0x29290029, 0xf9f900f9,
+ 0x2f2f002f, 0xb4b400b4, 0x78780078, 0x06060006, 0xe7e700e7, 0x71710071,
+ 0xd4d400d4, 0xabab00ab, 0x88880088, 0x8d8d008d, 0x72720072, 0xb9b900b9,
+ 0xf8f800f8, 0xacac00ac, 0x36360036, 0x2a2a002a, 0x3c3c003c, 0xf1f100f1,
+ 0x40400040, 0xd3d300d3, 0xbbbb00bb, 0x43430043, 0x15150015, 0xadad00ad,
+ 0x77770077, 0x80800080, 0x82820082, 0xecec00ec, 0x27270027, 0xe5e500e5,
+ 0x85850085, 0x35350035, 0x0c0c000c, 0x41410041, 0xefef00ef, 0x93930093,
+ 0x19190019, 0x21210021, 0x0e0e000e, 0x4e4e004e, 0x65650065, 0xbdbd00bd,
+ 0xb8b800b8, 0x8f8f008f, 0xebeb00eb, 0xcece00ce, 0x30300030, 0x5f5f005f,
+ 0xc5c500c5, 0x1a1a001a, 0xe1e100e1, 0xcaca00ca, 0x47470047, 0x3d3d003d,
+ 0x01010001, 0xd6d600d6, 0x56560056, 0x4d4d004d, 0x0d0d000d, 0x66660066,
+ 0xcccc00cc, 0x2d2d002d, 0x12120012, 0x20200020, 0xb1b100b1, 0x99990099,
+ 0x4c4c004c, 0xc2c200c2, 0x7e7e007e, 0x05050005, 0xb7b700b7, 0x31310031,
+ 0x17170017, 0xd7d700d7, 0x58580058, 0x61610061, 0x1b1b001b, 0x1c1c001c,
+ 0x0f0f000f, 0x16160016, 0x18180018, 0x22220022, 0x44440044, 0xb2b200b2,
+ 0xb5b500b5, 0x91910091, 0x08080008, 0xa8a800a8, 0xfcfc00fc, 0x50500050,
+ 0xd0d000d0, 0x7d7d007d, 0x89890089, 0x97970097, 0x5b5b005b, 0x95950095,
+ 0xffff00ff, 0xd2d200d2, 0xc4c400c4, 0x48480048, 0xf7f700f7, 0xdbdb00db,
+ 0x03030003, 0xdada00da, 0x3f3f003f, 0x94940094, 0x5c5c005c, 0x02020002,
+ 0x4a4a004a, 0x33330033, 0x67670067, 0xf3f300f3, 0x7f7f007f, 0xe2e200e2,
+ 0x9b9b009b, 0x26260026, 0x37370037, 0x3b3b003b, 0x96960096, 0x4b4b004b,
+ 0xbebe00be, 0x2e2e002e, 0x79790079, 0x8c8c008c, 0x6e6e006e, 0x8e8e008e,
+ 0xf5f500f5, 0xb6b600b6, 0xfdfd00fd, 0x59590059, 0x98980098, 0x6a6a006a,
+ 0x46460046, 0xbaba00ba, 0x25250025, 0x42420042, 0xa2a200a2, 0xfafa00fa,
+ 0x07070007, 0x55550055, 0xeeee00ee, 0x0a0a000a, 0x49490049, 0x68680068,
+ 0x38380038, 0xa4a400a4, 0x28280028, 0x7b7b007b, 0xc9c900c9, 0xc1c100c1,
+ 0xe3e300e3, 0xf4f400f4, 0xc7c700c7, 0x9e9e009e
+ };
+
+ private static final int SBOX2_0222[] = {
+ 0x00e0e0e0, 0x00050505, 0x00585858, 0x00d9d9d9, 0x00676767, 0x004e4e4e,
+ 0x00818181, 0x00cbcbcb, 0x00c9c9c9, 0x000b0b0b, 0x00aeaeae, 0x006a6a6a,
+ 0x00d5d5d5, 0x00181818, 0x005d5d5d, 0x00828282, 0x00464646, 0x00dfdfdf,
+ 0x00d6d6d6, 0x00272727, 0x008a8a8a, 0x00323232, 0x004b4b4b, 0x00424242,
+ 0x00dbdbdb, 0x001c1c1c, 0x009e9e9e, 0x009c9c9c, 0x003a3a3a, 0x00cacaca,
+ 0x00252525, 0x007b7b7b, 0x000d0d0d, 0x00717171, 0x005f5f5f, 0x001f1f1f,
+ 0x00f8f8f8, 0x00d7d7d7, 0x003e3e3e, 0x009d9d9d, 0x007c7c7c, 0x00606060,
+ 0x00b9b9b9, 0x00bebebe, 0x00bcbcbc, 0x008b8b8b, 0x00161616, 0x00343434,
+ 0x004d4d4d, 0x00c3c3c3, 0x00727272, 0x00959595, 0x00ababab, 0x008e8e8e,
+ 0x00bababa, 0x007a7a7a, 0x00b3b3b3, 0x00020202, 0x00b4b4b4, 0x00adadad,
+ 0x00a2a2a2, 0x00acacac, 0x00d8d8d8, 0x009a9a9a, 0x00171717, 0x001a1a1a,
+ 0x00353535, 0x00cccccc, 0x00f7f7f7, 0x00999999, 0x00616161, 0x005a5a5a,
+ 0x00e8e8e8, 0x00242424, 0x00565656, 0x00404040, 0x00e1e1e1, 0x00636363,
+ 0x00090909, 0x00333333, 0x00bfbfbf, 0x00989898, 0x00979797, 0x00858585,
+ 0x00686868, 0x00fcfcfc, 0x00ececec, 0x000a0a0a, 0x00dadada, 0x006f6f6f,
+ 0x00535353, 0x00626262, 0x00a3a3a3, 0x002e2e2e, 0x00080808, 0x00afafaf,
+ 0x00282828, 0x00b0b0b0, 0x00747474, 0x00c2c2c2, 0x00bdbdbd, 0x00363636,
+ 0x00222222, 0x00383838, 0x00646464, 0x001e1e1e, 0x00393939, 0x002c2c2c,
+ 0x00a6a6a6, 0x00303030, 0x00e5e5e5, 0x00444444, 0x00fdfdfd, 0x00888888,
+ 0x009f9f9f, 0x00656565, 0x00878787, 0x006b6b6b, 0x00f4f4f4, 0x00232323,
+ 0x00484848, 0x00101010, 0x00d1d1d1, 0x00515151, 0x00c0c0c0, 0x00f9f9f9,
+ 0x00d2d2d2, 0x00a0a0a0, 0x00555555, 0x00a1a1a1, 0x00414141, 0x00fafafa,
+ 0x00434343, 0x00131313, 0x00c4c4c4, 0x002f2f2f, 0x00a8a8a8, 0x00b6b6b6,
+ 0x003c3c3c, 0x002b2b2b, 0x00c1c1c1, 0x00ffffff, 0x00c8c8c8, 0x00a5a5a5,
+ 0x00202020, 0x00898989, 0x00000000, 0x00909090, 0x00474747, 0x00efefef,
+ 0x00eaeaea, 0x00b7b7b7, 0x00151515, 0x00060606, 0x00cdcdcd, 0x00b5b5b5,
+ 0x00121212, 0x007e7e7e, 0x00bbbbbb, 0x00292929, 0x000f0f0f, 0x00b8b8b8,
+ 0x00070707, 0x00040404, 0x009b9b9b, 0x00949494, 0x00212121, 0x00666666,
+ 0x00e6e6e6, 0x00cecece, 0x00ededed, 0x00e7e7e7, 0x003b3b3b, 0x00fefefe,
+ 0x007f7f7f, 0x00c5c5c5, 0x00a4a4a4, 0x00373737, 0x00b1b1b1, 0x004c4c4c,
+ 0x00919191, 0x006e6e6e, 0x008d8d8d, 0x00767676, 0x00030303, 0x002d2d2d,
+ 0x00dedede, 0x00969696, 0x00262626, 0x007d7d7d, 0x00c6c6c6, 0x005c5c5c,
+ 0x00d3d3d3, 0x00f2f2f2, 0x004f4f4f, 0x00191919, 0x003f3f3f, 0x00dcdcdc,
+ 0x00797979, 0x001d1d1d, 0x00525252, 0x00ebebeb, 0x00f3f3f3, 0x006d6d6d,
+ 0x005e5e5e, 0x00fbfbfb, 0x00696969, 0x00b2b2b2, 0x00f0f0f0, 0x00313131,
+ 0x000c0c0c, 0x00d4d4d4, 0x00cfcfcf, 0x008c8c8c, 0x00e2e2e2, 0x00757575,
+ 0x00a9a9a9, 0x004a4a4a, 0x00575757, 0x00848484, 0x00111111, 0x00454545,
+ 0x001b1b1b, 0x00f5f5f5, 0x00e4e4e4, 0x000e0e0e, 0x00737373, 0x00aaaaaa,
+ 0x00f1f1f1, 0x00dddddd, 0x00595959, 0x00141414, 0x006c6c6c, 0x00929292,
+ 0x00545454, 0x00d0d0d0, 0x00787878, 0x00707070, 0x00e3e3e3, 0x00494949,
+ 0x00808080, 0x00505050, 0x00a7a7a7, 0x00f6f6f6, 0x00777777, 0x00939393,
+ 0x00868686, 0x00838383, 0x002a2a2a, 0x00c7c7c7, 0x005b5b5b, 0x00e9e9e9,
+ 0x00eeeeee, 0x008f8f8f, 0x00010101, 0x003d3d3d
+ };
+
+ private static final int SBOX3_3033[] = {
+ 0x38003838, 0x41004141, 0x16001616, 0x76007676, 0xd900d9d9, 0x93009393,
+ 0x60006060, 0xf200f2f2, 0x72007272, 0xc200c2c2, 0xab00abab, 0x9a009a9a,
+ 0x75007575, 0x06000606, 0x57005757, 0xa000a0a0, 0x91009191, 0xf700f7f7,
+ 0xb500b5b5, 0xc900c9c9, 0xa200a2a2, 0x8c008c8c, 0xd200d2d2, 0x90009090,
+ 0xf600f6f6, 0x07000707, 0xa700a7a7, 0x27002727, 0x8e008e8e, 0xb200b2b2,
+ 0x49004949, 0xde00dede, 0x43004343, 0x5c005c5c, 0xd700d7d7, 0xc700c7c7,
+ 0x3e003e3e, 0xf500f5f5, 0x8f008f8f, 0x67006767, 0x1f001f1f, 0x18001818,
+ 0x6e006e6e, 0xaf00afaf, 0x2f002f2f, 0xe200e2e2, 0x85008585, 0x0d000d0d,
+ 0x53005353, 0xf000f0f0, 0x9c009c9c, 0x65006565, 0xea00eaea, 0xa300a3a3,
+ 0xae00aeae, 0x9e009e9e, 0xec00ecec, 0x80008080, 0x2d002d2d, 0x6b006b6b,
+ 0xa800a8a8, 0x2b002b2b, 0x36003636, 0xa600a6a6, 0xc500c5c5, 0x86008686,
+ 0x4d004d4d, 0x33003333, 0xfd00fdfd, 0x66006666, 0x58005858, 0x96009696,
+ 0x3a003a3a, 0x09000909, 0x95009595, 0x10001010, 0x78007878, 0xd800d8d8,
+ 0x42004242, 0xcc00cccc, 0xef00efef, 0x26002626, 0xe500e5e5, 0x61006161,
+ 0x1a001a1a, 0x3f003f3f, 0x3b003b3b, 0x82008282, 0xb600b6b6, 0xdb00dbdb,
+ 0xd400d4d4, 0x98009898, 0xe800e8e8, 0x8b008b8b, 0x02000202, 0xeb00ebeb,
+ 0x0a000a0a, 0x2c002c2c, 0x1d001d1d, 0xb000b0b0, 0x6f006f6f, 0x8d008d8d,
+ 0x88008888, 0x0e000e0e, 0x19001919, 0x87008787, 0x4e004e4e, 0x0b000b0b,
+ 0xa900a9a9, 0x0c000c0c, 0x79007979, 0x11001111, 0x7f007f7f, 0x22002222,
+ 0xe700e7e7, 0x59005959, 0xe100e1e1, 0xda00dada, 0x3d003d3d, 0xc800c8c8,
+ 0x12001212, 0x04000404, 0x74007474, 0x54005454, 0x30003030, 0x7e007e7e,
+ 0xb400b4b4, 0x28002828, 0x55005555, 0x68006868, 0x50005050, 0xbe00bebe,
+ 0xd000d0d0, 0xc400c4c4, 0x31003131, 0xcb00cbcb, 0x2a002a2a, 0xad00adad,
+ 0x0f000f0f, 0xca00caca, 0x70007070, 0xff00ffff, 0x32003232, 0x69006969,
+ 0x08000808, 0x62006262, 0x00000000, 0x24002424, 0xd100d1d1, 0xfb00fbfb,
+ 0xba00baba, 0xed00eded, 0x45004545, 0x81008181, 0x73007373, 0x6d006d6d,
+ 0x84008484, 0x9f009f9f, 0xee00eeee, 0x4a004a4a, 0xc300c3c3, 0x2e002e2e,
+ 0xc100c1c1, 0x01000101, 0xe600e6e6, 0x25002525, 0x48004848, 0x99009999,
+ 0xb900b9b9, 0xb300b3b3, 0x7b007b7b, 0xf900f9f9, 0xce00cece, 0xbf00bfbf,
+ 0xdf00dfdf, 0x71007171, 0x29002929, 0xcd00cdcd, 0x6c006c6c, 0x13001313,
+ 0x64006464, 0x9b009b9b, 0x63006363, 0x9d009d9d, 0xc000c0c0, 0x4b004b4b,
+ 0xb700b7b7, 0xa500a5a5, 0x89008989, 0x5f005f5f, 0xb100b1b1, 0x17001717,
+ 0xf400f4f4, 0xbc00bcbc, 0xd300d3d3, 0x46004646, 0xcf00cfcf, 0x37003737,
+ 0x5e005e5e, 0x47004747, 0x94009494, 0xfa00fafa, 0xfc00fcfc, 0x5b005b5b,
+ 0x97009797, 0xfe00fefe, 0x5a005a5a, 0xac00acac, 0x3c003c3c, 0x4c004c4c,
+ 0x03000303, 0x35003535, 0xf300f3f3, 0x23002323, 0xb800b8b8, 0x5d005d5d,
+ 0x6a006a6a, 0x92009292, 0xd500d5d5, 0x21002121, 0x44004444, 0x51005151,
+ 0xc600c6c6, 0x7d007d7d, 0x39003939, 0x83008383, 0xdc00dcdc, 0xaa00aaaa,
+ 0x7c007c7c, 0x77007777, 0x56005656, 0x05000505, 0x1b001b1b, 0xa400a4a4,
+ 0x15001515, 0x34003434, 0x1e001e1e, 0x1c001c1c, 0xf800f8f8, 0x52005252,
+ 0x20002020, 0x14001414, 0xe900e9e9, 0xbd00bdbd, 0xdd00dddd, 0xe400e4e4,
+ 0xa100a1a1, 0xe000e0e0, 0x8a008a8a, 0xf100f1f1, 0xd600d6d6, 0x7a007a7a,
+ 0xbb00bbbb, 0xe300e3e3, 0x40004040, 0x4f004f4f
+ };
+
+ private static int rightRotate(int x, int s)
+ {
+ return (((x) >>> (s)) + ((x) << (32 - s)));
+ }
+
+ private static int leftRotate(int x, int s)
+ {
+ return ((x) << (s)) + ((x) >>> (32 - s));
+ }
+
+ private static void roldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static void roldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private int bytes2int(byte[] src, int offset)
+ {
+ int word = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ word = (word << 8) + (src[i + offset] & MASK8);
+ }
+ return word;
+ }
+
+ private void int2bytes(int word, byte[] dst, int offset)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ dst[(3 - i) + offset] = (byte)word;
+ word >>>= 8;
+ }
+ }
+
+ private void camelliaF2(int[] s, int[] skey, int keyoff)
+ {
+ int t1, t2, u, v;
+
+ t1 = s[0] ^ skey[0 + keyoff];
+ u = SBOX4_4404[t1 & MASK8];
+ u ^= SBOX3_3033[(t1 >>> 8) & MASK8];
+ u ^= SBOX2_0222[(t1 >>> 16) & MASK8];
+ u ^= SBOX1_1110[(t1 >>> 24) & MASK8];
+ t2 = s[1] ^ skey[1 + keyoff];
+ v = SBOX1_1110[t2 & MASK8];
+ v ^= SBOX4_4404[(t2 >>> 8) & MASK8];
+ v ^= SBOX3_3033[(t2 >>> 16) & MASK8];
+ v ^= SBOX2_0222[(t2 >>> 24) & MASK8];
+
+ s[2] ^= u ^ v;
+ s[3] ^= u ^ v ^ rightRotate(u, 8);
+
+ t1 = s[2] ^ skey[2 + keyoff];
+ u = SBOX4_4404[t1 & MASK8];
+ u ^= SBOX3_3033[(t1 >>> 8) & MASK8];
+ u ^= SBOX2_0222[(t1 >>> 16) & MASK8];
+ u ^= SBOX1_1110[(t1 >>> 24) & MASK8];
+ t2 = s[3] ^ skey[3 + keyoff];
+ v = SBOX1_1110[t2 & MASK8];
+ v ^= SBOX4_4404[(t2 >>> 8) & MASK8];
+ v ^= SBOX3_3033[(t2 >>> 16) & MASK8];
+ v ^= SBOX2_0222[(t2 >>> 24) & MASK8];
+
+ s[0] ^= u ^ v;
+ s[1] ^= u ^ v ^ rightRotate(u, 8);
+ }
+
+ private void camelliaFLs(int[] s, int[] fkey, int keyoff)
+ {
+
+ s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1);
+ s[0] ^= fkey[1 + keyoff] | s[1];
+
+ s[2] ^= fkey[3 + keyoff] | s[3];
+ s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1);
+ }
+
+ private void setKey(boolean forEncryption, byte[] key)
+ {
+ int[] k = new int[8];
+ int[] ka = new int[4];
+ int[] kb = new int[4];
+ int[] t = new int[4];
+
+ switch (key.length)
+ {
+ case 16:
+ _keyIs128 = true;
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = k[5] = k[6] = k[7] = 0;
+ break;
+ case 24:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = ~k[4];
+ k[7] = ~k[5];
+ _keyIs128 = false;
+ break;
+ case 32:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = bytes2int(key, 24);
+ k[7] = bytes2int(key, 28);
+ _keyIs128 = false;
+ break;
+ default:
+ throw new
+ IllegalArgumentException("key sizes are only 16/24/32 bytes.");
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] = k[i] ^ k[i + 4];
+ }
+ /* compute KA */
+ camelliaF2(ka, SIGMA, 0);
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] ^= k[i];
+ }
+ camelliaF2(ka, SIGMA, 4);
+
+ if (_keyIs128)
+ {
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldq(15, k, 0, subkey, 4);
+ roldq(30, k, 0, subkey, 12);
+ roldq(15, k, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ roldq(17, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 24);
+ roldq(17, k, 0, subkey, 32);
+ /* KA dependant keys */
+ subkey[0] = ka[0];
+ subkey[1] = ka[1];
+ subkey[2] = ka[2];
+ subkey[3] = ka[3];
+ roldq(15, ka, 0, subkey, 8);
+ roldq(15, ka, 0, ke, 0);
+ roldq(15, ka, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ roldq(15, ka, 0, subkey, 20);
+ roldqo32(34, ka, 0, subkey, 28);
+ roldq(17, ka, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldq(15, k, 0, subkey, 28);
+ decroldq(30, k, 0, subkey, 20);
+ decroldq(15, k, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ decroldq(17, k, 0, ke, 0);
+ decroldq(17, k, 0, subkey, 8);
+ decroldq(17, k, 0, subkey, 0);
+ /* KA dependant keys */
+ subkey[34] = ka[0];
+ subkey[35] = ka[1];
+ subkey[32] = ka[2];
+ subkey[33] = ka[3];
+ decroldq(15, ka, 0, subkey, 24);
+ decroldq(15, ka, 0, ke, 4);
+ decroldq(15, ka, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ decroldq(15, ka, 0, subkey, 12);
+ decroldqo32(34, ka, 0, subkey, 4);
+ roldq(17, ka, 0, kw, 0);
+ }
+ }
+ else
+ { // 192bit or 256bit
+ /* compute KB */
+ for (int i = 0; i < 4; i++)
+ {
+ kb[i] = ka[i] ^ k[i + 4];
+ }
+ camelliaF2(kb, SIGMA, 8);
+
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldqo32(45, k, 0, subkey, 16);
+ roldq(15, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 32);
+ roldqo32(34, k, 0, subkey, 44);
+ /* KR dependant keys */
+ roldq(15, k, 4, subkey, 4);
+ roldq(15, k, 4, ke, 0);
+ roldq(30, k, 4, subkey, 24);
+ roldqo32(34, k, 4, subkey, 36);
+ /* KA dependant keys */
+ roldq(15, ka, 0, subkey, 8);
+ roldq(30, ka, 0, subkey, 20);
+ /* 32bit rotation */
+ ke[8] = ka[1];
+ ke[9] = ka[2];
+ ke[10] = ka[3];
+ ke[11] = ka[0];
+ roldqo32(49, ka, 0, subkey, 40);
+
+ /* KB dependant keys */
+ subkey[0] = kb[0];
+ subkey[1] = kb[1];
+ subkey[2] = kb[2];
+ subkey[3] = kb[3];
+ roldq(30, kb, 0, subkey, 12);
+ roldq(30, kb, 0, subkey, 28);
+ roldqo32(51, kb, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldqo32(45, k, 0, subkey, 28);
+ decroldq(15, k, 0, ke, 4);
+ decroldq(17, k, 0, subkey, 12);
+ decroldqo32(34, k, 0, subkey, 0);
+ /* KR dependant keys */
+ decroldq(15, k, 4, subkey, 40);
+ decroldq(15, k, 4, ke, 8);
+ decroldq(30, k, 4, subkey, 20);
+ decroldqo32(34, k, 4, subkey, 8);
+ /* KA dependant keys */
+ decroldq(15, ka, 0, subkey, 36);
+ decroldq(30, ka, 0, subkey, 24);
+ /* 32bit rotation */
+ ke[2] = ka[1];
+ ke[3] = ka[2];
+ ke[0] = ka[3];
+ ke[1] = ka[0];
+ decroldqo32(49, ka, 0, subkey, 4);
+
+ /* KB dependant keys */
+ subkey[46] = kb[0];
+ subkey[47] = kb[1];
+ subkey[44] = kb[2];
+ subkey[45] = kb[3];
+ decroldq(30, kb, 0, subkey, 32);
+ decroldq(30, kb, 0, subkey, 16);
+ roldqo32(51, kb, 0, kw, 0);
+ }
+ }
+ }
+
+ private int processBlock128(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ private int processBlock192or256(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+ camelliaFLs(state, ke, 8);
+ camelliaF2(state, subkey, 36);
+ camelliaF2(state, subkey, 40);
+ camelliaF2(state, subkey, 44);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+ return BLOCK_SIZE;
+ }
+
+ public CamelliaEngine()
+ {
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("only simple KeyParameter expected.");
+ }
+
+ setKey(forEncryption, ((KeyParameter)params).getKey());
+ initialised = true;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Camellia";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException("Camellia engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (_keyIs128)
+ {
+ return processBlock128(in, inOff, out, outOff);
+ }
+ else
+ {
+ return processBlock192or256(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ // nothing
+
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/CamelliaLightEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/CamelliaLightEngine.java
new file mode 100644
index 00000000..bc8dea4a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/CamelliaLightEngine.java
@@ -0,0 +1,592 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * Camellia - based on RFC 3713, smaller implementation, about half the size of CamelliaEngine.
+ */
+
+public class CamelliaLightEngine
+ implements BlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+ private static final int MASK8 = 0xff;
+ private boolean initialized;
+ private boolean _keyis128;
+
+ private int[] subkey = new int[24 * 4];
+ private int[] kw = new int[4 * 2]; // for whitening
+ private int[] ke = new int[6 * 2]; // for FL and FL^(-1)
+ private int[] state = new int[4]; // for encryption and decryption
+
+ private static final int SIGMA[] = {
+ 0xa09e667f, 0x3bcc908b,
+ 0xb67ae858, 0x4caa73b2,
+ 0xc6ef372f, 0xe94f82be,
+ 0x54ff53a5, 0xf1d36f1c,
+ 0x10e527fa, 0xde682d1d,
+ 0xb05688c2, 0xb3e6c1fd
+ };
+
+ /*
+ *
+ * S-box data
+ *
+ */
+ private static final byte SBOX1[] = {
+ (byte)112, (byte)130, (byte)44, (byte)236,
+ (byte)179, (byte)39, (byte)192, (byte)229,
+ (byte)228, (byte)133, (byte)87, (byte)53,
+ (byte)234, (byte)12, (byte)174, (byte)65,
+ (byte)35, (byte)239, (byte)107, (byte)147,
+ (byte)69, (byte)25, (byte)165, (byte)33,
+ (byte)237, (byte)14, (byte)79, (byte)78,
+ (byte)29, (byte)101, (byte)146, (byte)189,
+ (byte)134, (byte)184, (byte)175, (byte)143,
+ (byte)124, (byte)235, (byte)31, (byte)206,
+ (byte)62, (byte)48, (byte)220, (byte)95,
+ (byte)94, (byte)197, (byte)11, (byte)26,
+ (byte)166, (byte)225, (byte)57, (byte)202,
+ (byte)213, (byte)71, (byte)93, (byte)61,
+ (byte)217, (byte)1, (byte)90, (byte)214,
+ (byte)81, (byte)86, (byte)108, (byte)77,
+ (byte)139, (byte)13, (byte)154, (byte)102,
+ (byte)251, (byte)204, (byte)176, (byte)45,
+ (byte)116, (byte)18, (byte)43, (byte)32,
+ (byte)240, (byte)177, (byte)132, (byte)153,
+ (byte)223, (byte)76, (byte)203, (byte)194,
+ (byte)52, (byte)126, (byte)118, (byte)5,
+ (byte)109, (byte)183, (byte)169, (byte)49,
+ (byte)209, (byte)23, (byte)4, (byte)215,
+ (byte)20, (byte)88, (byte)58, (byte)97,
+ (byte)222, (byte)27, (byte)17, (byte)28,
+ (byte)50, (byte)15, (byte)156, (byte)22,
+ (byte)83, (byte)24, (byte)242, (byte)34,
+ (byte)254, (byte)68, (byte)207, (byte)178,
+ (byte)195, (byte)181, (byte)122, (byte)145,
+ (byte)36, (byte)8, (byte)232, (byte)168,
+ (byte)96, (byte)252, (byte)105, (byte)80,
+ (byte)170, (byte)208, (byte)160, (byte)125,
+ (byte)161, (byte)137, (byte)98, (byte)151,
+ (byte)84, (byte)91, (byte)30, (byte)149,
+ (byte)224, (byte)255, (byte)100, (byte)210,
+ (byte)16, (byte)196, (byte)0, (byte)72,
+ (byte)163, (byte)247, (byte)117, (byte)219,
+ (byte)138, (byte)3, (byte)230, (byte)218,
+ (byte)9, (byte)63, (byte)221, (byte)148,
+ (byte)135, (byte)92, (byte)131, (byte)2,
+ (byte)205, (byte)74, (byte)144, (byte)51,
+ (byte)115, (byte)103, (byte)246, (byte)243,
+ (byte)157, (byte)127, (byte)191, (byte)226,
+ (byte)82, (byte)155, (byte)216, (byte)38,
+ (byte)200, (byte)55, (byte)198, (byte)59,
+ (byte)129, (byte)150, (byte)111, (byte)75,
+ (byte)19, (byte)190, (byte)99, (byte)46,
+ (byte)233, (byte)121, (byte)167, (byte)140,
+ (byte)159, (byte)110, (byte)188, (byte)142,
+ (byte)41, (byte)245, (byte)249, (byte)182,
+ (byte)47, (byte)253, (byte)180, (byte)89,
+ (byte)120, (byte)152, (byte)6, (byte)106,
+ (byte)231, (byte)70, (byte)113, (byte)186,
+ (byte)212, (byte)37, (byte)171, (byte)66,
+ (byte)136, (byte)162, (byte)141, (byte)250,
+ (byte)114, (byte)7, (byte)185, (byte)85,
+ (byte)248, (byte)238, (byte)172, (byte)10,
+ (byte)54, (byte)73, (byte)42, (byte)104,
+ (byte)60, (byte)56, (byte)241, (byte)164,
+ (byte)64, (byte)40, (byte)211, (byte)123,
+ (byte)187, (byte)201, (byte)67, (byte)193,
+ (byte)21, (byte)227, (byte)173, (byte)244,
+ (byte)119, (byte)199, (byte)128, (byte)158
+ };
+
+ private static int rightRotate(int x, int s)
+ {
+ return (((x) >>> (s)) + ((x) << (32 - s)));
+ }
+
+ private static int leftRotate(int x, int s)
+ {
+ return ((x) << (s)) + ((x) >>> (32 - s));
+ }
+
+ private static void roldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[2 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldq(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[0 + ioff] << rot) | (ki[1 + ioff] >>> (32 - rot));
+ ko[3 + ooff] = (ki[1 + ioff] << rot) | (ki[2 + ioff] >>> (32 - rot));
+ ko[0 + ooff] = (ki[2 + ioff] << rot) | (ki[3 + ioff] >>> (32 - rot));
+ ko[1 + ooff] = (ki[3 + ioff] << rot) | (ki[0 + ioff] >>> (32 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private static void roldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[0 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[2 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[0 + ooff];
+ ki[1 + ioff] = ko[1 + ooff];
+ ki[2 + ioff] = ko[2 + ooff];
+ ki[3 + ioff] = ko[3 + ooff];
+ }
+
+ private static void decroldqo32(int rot, int[] ki, int ioff,
+ int[] ko, int ooff)
+ {
+ ko[2 + ooff] = (ki[1 + ioff] << (rot - 32)) | (ki[2 + ioff] >>> (64 - rot));
+ ko[3 + ooff] = (ki[2 + ioff] << (rot - 32)) | (ki[3 + ioff] >>> (64 - rot));
+ ko[0 + ooff] = (ki[3 + ioff] << (rot - 32)) | (ki[0 + ioff] >>> (64 - rot));
+ ko[1 + ooff] = (ki[0 + ioff] << (rot - 32)) | (ki[1 + ioff] >>> (64 - rot));
+ ki[0 + ioff] = ko[2 + ooff];
+ ki[1 + ioff] = ko[3 + ooff];
+ ki[2 + ioff] = ko[0 + ooff];
+ ki[3 + ioff] = ko[1 + ooff];
+ }
+
+ private int bytes2int(byte[] src, int offset)
+ {
+ int word = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ word = (word << 8) + (src[i + offset] & MASK8);
+ }
+ return word;
+ }
+
+ private void int2bytes(int word, byte[] dst, int offset)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ dst[(3 - i) + offset] = (byte)word;
+ word >>>= 8;
+ }
+ }
+
+ private byte lRot8(byte v, int rot)
+ {
+ return (byte)((v << rot) | ((v & 0xff) >>> (8 - rot)));
+ }
+
+ private int sbox2(int x)
+ {
+ return (lRot8(SBOX1[x], 1) & MASK8);
+ }
+
+ private int sbox3(int x)
+ {
+ return (lRot8(SBOX1[x], 7) & MASK8);
+ }
+
+ private int sbox4(int x)
+ {
+ return (SBOX1[((int)lRot8((byte)x, 1) & MASK8)] & MASK8);
+ }
+
+ private void camelliaF2(int[] s, int[] skey, int keyoff)
+ {
+ int t1, t2, u, v;
+
+ t1 = s[0] ^ skey[0 + keyoff];
+ u = sbox4((t1 & MASK8));
+ u |= (sbox3(((t1 >>> 8) & MASK8)) << 8);
+ u |= (sbox2(((t1 >>> 16) & MASK8)) << 16);
+ u |= ((int)(SBOX1[((t1 >>> 24) & MASK8)] & MASK8) << 24);
+
+ t2 = s[1] ^ skey[1 + keyoff];
+ v = (int)SBOX1[(t2 & MASK8)] & MASK8;
+ v |= (sbox4(((t2 >>> 8) & MASK8)) << 8);
+ v |= (sbox3(((t2 >>> 16) & MASK8)) << 16);
+ v |= (sbox2(((t2 >>> 24) & MASK8)) << 24);
+
+ v = leftRotate(v, 8);
+ u ^= v;
+ v = leftRotate(v, 8) ^ u;
+ u = rightRotate(u, 8) ^ v;
+ s[2] ^= leftRotate(v, 16) ^ u;
+ s[3] ^= leftRotate(u, 8);
+
+ t1 = s[2] ^ skey[2 + keyoff];
+ u = sbox4((t1 & MASK8));
+ u |= sbox3(((t1 >>> 8) & MASK8)) << 8;
+ u |= sbox2(((t1 >>> 16) & MASK8)) << 16;
+ u |= ((int)SBOX1[((t1 >>> 24) & MASK8)] & MASK8) << 24;
+
+ t2 = s[3] ^ skey[3 + keyoff];
+ v = ((int)SBOX1[(t2 & MASK8)] & MASK8);
+ v |= sbox4(((t2 >>> 8) & MASK8)) << 8;
+ v |= sbox3(((t2 >>> 16) & MASK8)) << 16;
+ v |= sbox2(((t2 >>> 24) & MASK8)) << 24;
+
+ v = leftRotate(v, 8);
+ u ^= v;
+ v = leftRotate(v, 8) ^ u;
+ u = rightRotate(u, 8) ^ v;
+ s[0] ^= leftRotate(v, 16) ^ u;
+ s[1] ^= leftRotate(u, 8);
+ }
+
+ private void camelliaFLs(int[] s, int[] fkey, int keyoff)
+ {
+
+ s[1] ^= leftRotate(s[0] & fkey[0 + keyoff], 1);
+ s[0] ^= fkey[1 + keyoff] | s[1];
+
+ s[2] ^= fkey[3 + keyoff] | s[3];
+ s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1);
+ }
+
+ private void setKey(boolean forEncryption, byte[] key)
+ {
+ int[] k = new int[8];
+ int[] ka = new int[4];
+ int[] kb = new int[4];
+ int[] t = new int[4];
+
+ switch (key.length)
+ {
+ case 16:
+ _keyis128 = true;
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = k[5] = k[6] = k[7] = 0;
+ break;
+ case 24:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = ~k[4];
+ k[7] = ~k[5];
+ _keyis128 = false;
+ break;
+ case 32:
+ k[0] = bytes2int(key, 0);
+ k[1] = bytes2int(key, 4);
+ k[2] = bytes2int(key, 8);
+ k[3] = bytes2int(key, 12);
+ k[4] = bytes2int(key, 16);
+ k[5] = bytes2int(key, 20);
+ k[6] = bytes2int(key, 24);
+ k[7] = bytes2int(key, 28);
+ _keyis128 = false;
+ break;
+ default:
+ throw new
+ IllegalArgumentException("key sizes are only 16/24/32 bytes.");
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] = k[i] ^ k[i + 4];
+ }
+ /* compute KA */
+ camelliaF2(ka, SIGMA, 0);
+ for (int i = 0; i < 4; i++)
+ {
+ ka[i] ^= k[i];
+ }
+ camelliaF2(ka, SIGMA, 4);
+
+ if (_keyis128)
+ {
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldq(15, k, 0, subkey, 4);
+ roldq(30, k, 0, subkey, 12);
+ roldq(15, k, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ roldq(17, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 24);
+ roldq(17, k, 0, subkey, 32);
+ /* KA dependant keys */
+ subkey[0] = ka[0];
+ subkey[1] = ka[1];
+ subkey[2] = ka[2];
+ subkey[3] = ka[3];
+ roldq(15, ka, 0, subkey, 8);
+ roldq(15, ka, 0, ke, 0);
+ roldq(15, ka, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ roldq(15, ka, 0, subkey, 20);
+ roldqo32(34, ka, 0, subkey, 28);
+ roldq(17, ka, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldq(15, k, 0, subkey, 28);
+ decroldq(30, k, 0, subkey, 20);
+ decroldq(15, k, 0, t, 0);
+ subkey[16] = t[0];
+ subkey[17] = t[1];
+ decroldq(17, k, 0, ke, 0);
+ decroldq(17, k, 0, subkey, 8);
+ decroldq(17, k, 0, subkey, 0);
+ /* KA dependant keys */
+ subkey[34] = ka[0];
+ subkey[35] = ka[1];
+ subkey[32] = ka[2];
+ subkey[33] = ka[3];
+ decroldq(15, ka, 0, subkey, 24);
+ decroldq(15, ka, 0, ke, 4);
+ decroldq(15, ka, 0, t, 0);
+ subkey[18] = t[2];
+ subkey[19] = t[3];
+ decroldq(15, ka, 0, subkey, 12);
+ decroldqo32(34, ka, 0, subkey, 4);
+ roldq(17, ka, 0, kw, 0);
+ }
+ }
+ else
+ { // 192bit or 256bit
+ /* compute KB */
+ for (int i = 0; i < 4; i++)
+ {
+ kb[i] = ka[i] ^ k[i + 4];
+ }
+ camelliaF2(kb, SIGMA, 8);
+
+ if (forEncryption)
+ {
+ /* KL dependant keys */
+ kw[0] = k[0];
+ kw[1] = k[1];
+ kw[2] = k[2];
+ kw[3] = k[3];
+ roldqo32(45, k, 0, subkey, 16);
+ roldq(15, k, 0, ke, 4);
+ roldq(17, k, 0, subkey, 32);
+ roldqo32(34, k, 0, subkey, 44);
+ /* KR dependant keys */
+ roldq(15, k, 4, subkey, 4);
+ roldq(15, k, 4, ke, 0);
+ roldq(30, k, 4, subkey, 24);
+ roldqo32(34, k, 4, subkey, 36);
+ /* KA dependant keys */
+ roldq(15, ka, 0, subkey, 8);
+ roldq(30, ka, 0, subkey, 20);
+ /* 32bit rotation */
+ ke[8] = ka[1];
+ ke[9] = ka[2];
+ ke[10] = ka[3];
+ ke[11] = ka[0];
+ roldqo32(49, ka, 0, subkey, 40);
+
+ /* KB dependant keys */
+ subkey[0] = kb[0];
+ subkey[1] = kb[1];
+ subkey[2] = kb[2];
+ subkey[3] = kb[3];
+ roldq(30, kb, 0, subkey, 12);
+ roldq(30, kb, 0, subkey, 28);
+ roldqo32(51, kb, 0, kw, 4);
+
+ }
+ else
+ { // decryption
+ /* KL dependant keys */
+ kw[4] = k[0];
+ kw[5] = k[1];
+ kw[6] = k[2];
+ kw[7] = k[3];
+ decroldqo32(45, k, 0, subkey, 28);
+ decroldq(15, k, 0, ke, 4);
+ decroldq(17, k, 0, subkey, 12);
+ decroldqo32(34, k, 0, subkey, 0);
+ /* KR dependant keys */
+ decroldq(15, k, 4, subkey, 40);
+ decroldq(15, k, 4, ke, 8);
+ decroldq(30, k, 4, subkey, 20);
+ decroldqo32(34, k, 4, subkey, 8);
+ /* KA dependant keys */
+ decroldq(15, ka, 0, subkey, 36);
+ decroldq(30, ka, 0, subkey, 24);
+ /* 32bit rotation */
+ ke[2] = ka[1];
+ ke[3] = ka[2];
+ ke[0] = ka[3];
+ ke[1] = ka[0];
+ decroldqo32(49, ka, 0, subkey, 4);
+
+ /* KB dependant keys */
+ subkey[46] = kb[0];
+ subkey[47] = kb[1];
+ subkey[44] = kb[2];
+ subkey[45] = kb[3];
+ decroldq(30, kb, 0, subkey, 32);
+ decroldq(30, kb, 0, subkey, 16);
+ roldqo32(51, kb, 0, kw, 0);
+ }
+ }
+ }
+
+ private int processBlock128(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+
+ return BLOCK_SIZE;
+ }
+
+ private int processBlock192or256(byte[] in, int inOff,
+ byte[] out, int outOff)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ state[i] = bytes2int(in, inOff + (i * 4));
+ state[i] ^= kw[i];
+ }
+
+ camelliaF2(state, subkey, 0);
+ camelliaF2(state, subkey, 4);
+ camelliaF2(state, subkey, 8);
+ camelliaFLs(state, ke, 0);
+ camelliaF2(state, subkey, 12);
+ camelliaF2(state, subkey, 16);
+ camelliaF2(state, subkey, 20);
+ camelliaFLs(state, ke, 4);
+ camelliaF2(state, subkey, 24);
+ camelliaF2(state, subkey, 28);
+ camelliaF2(state, subkey, 32);
+ camelliaFLs(state, ke, 8);
+ camelliaF2(state, subkey, 36);
+ camelliaF2(state, subkey, 40);
+ camelliaF2(state, subkey, 44);
+
+ state[2] ^= kw[4];
+ state[3] ^= kw[5];
+ state[0] ^= kw[6];
+ state[1] ^= kw[7];
+
+ int2bytes(state[2], out, outOff);
+ int2bytes(state[3], out, outOff + 4);
+ int2bytes(state[0], out, outOff + 8);
+ int2bytes(state[1], out, outOff + 12);
+ return BLOCK_SIZE;
+ }
+
+ public CamelliaLightEngine()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Camellia";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("only simple KeyParameter expected.");
+ }
+
+ setKey(forEncryption, ((KeyParameter)params).getKey());
+ initialized = true;
+ }
+
+ public int processBlock(byte[] in, int inOff,
+ byte[] out, int outOff)
+ throws IllegalStateException
+ {
+
+ if (!initialized)
+ {
+ throw new IllegalStateException("Camellia is not initialized");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (_keyis128)
+ {
+ return processBlock128(in, inOff, out, outOff);
+ }
+ else
+ {
+ return processBlock192or256(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/CamelliaWrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/CamelliaWrapEngine.java
new file mode 100644
index 00000000..04526eb1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/CamelliaWrapEngine.java
@@ -0,0 +1,15 @@
+package org.spongycastle.crypto.engines;
+
+/**
+ * An implementation of the Camellia key wrapper based on RFC 3657/RFC 3394.
+ * <p>
+ * For further details see: <a href="http://www.ietf.org/rfc/rfc3657.txt">http://www.ietf.org/rfc/rfc3657.txt</a>.
+ */
+public class CamelliaWrapEngine
+ extends RFC3394WrapEngine
+{
+ public CamelliaWrapEngine()
+ {
+ super(new CamelliaEngine());
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/ChaChaEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/ChaChaEngine.java
new file mode 100644
index 00000000..bcceed9c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/ChaChaEngine.java
@@ -0,0 +1,204 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.util.Pack;
+
+/**
+ * Implementation of Daniel J. Bernstein's ChaCha stream cipher.
+ */
+public class ChaChaEngine extends Salsa20Engine
+{
+ /**
+ * Creates a 20 rounds ChaCha engine.
+ */
+ public ChaChaEngine()
+ {
+ super();
+ }
+
+ /**
+ * Creates a ChaCha engine with a specific number of rounds.
+ * @param rounds the number of rounds (must be an even number).
+ */
+ public ChaChaEngine(int rounds)
+ {
+ super(rounds);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "ChaCha" + rounds;
+ }
+
+ protected void advanceCounter()
+ {
+ if (++engineState[12] == 0)
+ {
+ ++engineState[13];
+ }
+ }
+
+ protected void retreatCounter()
+ {
+ if (engineState[12] == 0 && engineState[13] == 0)
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+
+ if (--engineState[12] == -1)
+ {
+ --engineState[13];
+ }
+ }
+
+ protected long getCounter()
+ {
+ return ((long)engineState[13] << 32) | (engineState[12] & 0xffffffffL);
+ }
+
+ protected void resetCounter()
+ {
+ engineState[12] = engineState[13] = 0;
+ }
+
+ protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ if (keyBytes != null)
+ {
+ if ((keyBytes.length != 16) && (keyBytes.length != 32))
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+ }
+
+ // Key
+ engineState[4] = Pack.littleEndianToInt(keyBytes, 0);
+ engineState[5] = Pack.littleEndianToInt(keyBytes, 4);
+ engineState[6] = Pack.littleEndianToInt(keyBytes, 8);
+ engineState[7] = Pack.littleEndianToInt(keyBytes, 12);
+
+ byte[] constants;
+ int offset;
+ if (keyBytes.length == 32)
+ {
+ constants = sigma;
+ offset = 16;
+ }
+ else
+ {
+ constants = tau;
+ offset = 0;
+ }
+
+ engineState[8] = Pack.littleEndianToInt(keyBytes, offset);
+ engineState[9] = Pack.littleEndianToInt(keyBytes, offset + 4);
+ engineState[10] = Pack.littleEndianToInt(keyBytes, offset + 8);
+ engineState[11] = Pack.littleEndianToInt(keyBytes, offset + 12);
+
+ engineState[0] = Pack.littleEndianToInt(constants, 0);
+ engineState[1] = Pack.littleEndianToInt(constants, 4);
+ engineState[2] = Pack.littleEndianToInt(constants, 8);
+ engineState[3] = Pack.littleEndianToInt(constants, 12);
+ }
+
+ // IV
+ engineState[14] = Pack.littleEndianToInt(ivBytes, 0);
+ engineState[15] = Pack.littleEndianToInt(ivBytes, 4);
+ }
+
+ protected void generateKeyStream(byte[] output)
+ {
+ chachaCore(rounds, engineState, x);
+ Pack.intToLittleEndian(x, output, 0);
+ }
+
+ /**
+ * ChacCha function
+ *
+ * @param input input data
+ */
+ public static void chachaCore(int rounds, int[] input, int[] x)
+ {
+ if (input.length != 16)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (x.length != 16)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (rounds % 2 != 0)
+ {
+ throw new IllegalArgumentException("Number of rounds must be even");
+ }
+
+ int x00 = input[ 0];
+ int x01 = input[ 1];
+ int x02 = input[ 2];
+ int x03 = input[ 3];
+ int x04 = input[ 4];
+ int x05 = input[ 5];
+ int x06 = input[ 6];
+ int x07 = input[ 7];
+ int x08 = input[ 8];
+ int x09 = input[ 9];
+ int x10 = input[10];
+ int x11 = input[11];
+ int x12 = input[12];
+ int x13 = input[13];
+ int x14 = input[14];
+ int x15 = input[15];
+
+ for (int i = rounds; i > 0; i -= 2)
+ {
+ x00 += x04; x12 = rotl(x12 ^ x00, 16);
+ x08 += x12; x04 = rotl(x04 ^ x08, 12);
+ x00 += x04; x12 = rotl(x12 ^ x00, 8);
+ x08 += x12; x04 = rotl(x04 ^ x08, 7);
+ x01 += x05; x13 = rotl(x13 ^ x01, 16);
+ x09 += x13; x05 = rotl(x05 ^ x09, 12);
+ x01 += x05; x13 = rotl(x13 ^ x01, 8);
+ x09 += x13; x05 = rotl(x05 ^ x09, 7);
+ x02 += x06; x14 = rotl(x14 ^ x02, 16);
+ x10 += x14; x06 = rotl(x06 ^ x10, 12);
+ x02 += x06; x14 = rotl(x14 ^ x02, 8);
+ x10 += x14; x06 = rotl(x06 ^ x10, 7);
+ x03 += x07; x15 = rotl(x15 ^ x03, 16);
+ x11 += x15; x07 = rotl(x07 ^ x11, 12);
+ x03 += x07; x15 = rotl(x15 ^ x03, 8);
+ x11 += x15; x07 = rotl(x07 ^ x11, 7);
+ x00 += x05; x15 = rotl(x15 ^ x00, 16);
+ x10 += x15; x05 = rotl(x05 ^ x10, 12);
+ x00 += x05; x15 = rotl(x15 ^ x00, 8);
+ x10 += x15; x05 = rotl(x05 ^ x10, 7);
+ x01 += x06; x12 = rotl(x12 ^ x01, 16);
+ x11 += x12; x06 = rotl(x06 ^ x11, 12);
+ x01 += x06; x12 = rotl(x12 ^ x01, 8);
+ x11 += x12; x06 = rotl(x06 ^ x11, 7);
+ x02 += x07; x13 = rotl(x13 ^ x02, 16);
+ x08 += x13; x07 = rotl(x07 ^ x08, 12);
+ x02 += x07; x13 = rotl(x13 ^ x02, 8);
+ x08 += x13; x07 = rotl(x07 ^ x08, 7);
+ x03 += x04; x14 = rotl(x14 ^ x03, 16);
+ x09 += x14; x04 = rotl(x04 ^ x09, 12);
+ x03 += x04; x14 = rotl(x14 ^ x03, 8);
+ x09 += x14; x04 = rotl(x04 ^ x09, 7);
+
+ }
+
+ x[ 0] = x00 + input[ 0];
+ x[ 1] = x01 + input[ 1];
+ x[ 2] = x02 + input[ 2];
+ x[ 3] = x03 + input[ 3];
+ x[ 4] = x04 + input[ 4];
+ x[ 5] = x05 + input[ 5];
+ x[ 6] = x06 + input[ 6];
+ x[ 7] = x07 + input[ 7];
+ x[ 8] = x08 + input[ 8];
+ x[ 9] = x09 + input[ 9];
+ x[10] = x10 + input[10];
+ x[11] = x11 + input[11];
+ x[12] = x12 + input[12];
+ x[13] = x13 + input[13];
+ x[14] = x14 + input[14];
+ x[15] = x15 + input[15];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCiphertext.java b/core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCiphertext.java
new file mode 100644
index 00000000..c5d61aac
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCiphertext.java
@@ -0,0 +1,179 @@
+package org.spongycastle.crypto.engines;
+
+import java.math.BigInteger;
+
+/**
+ * Class, holding Cramer Shoup ciphertexts (u1, u2, e, v)
+ */
+public class CramerShoupCiphertext
+{
+
+ BigInteger u1, u2, e, v;
+
+ public CramerShoupCiphertext()
+ {
+
+ }
+
+ public CramerShoupCiphertext(BigInteger u1, BigInteger u2, BigInteger e, BigInteger v)
+ {
+ this.u1 = u1;
+ this.u2 = u2;
+ this.e = e;
+ this.v = v;
+ }
+
+ public CramerShoupCiphertext(byte[] c)
+ {
+ int off = 0, s;
+ byte[] size = new byte[4];
+ byte[] tmp;
+
+ System.arraycopy(c, off, size, 0, 4);
+ s = byteArrayToInt(size);
+ tmp = new byte[s];
+ off += 4;
+ System.arraycopy(c, off, tmp, 0, s);
+ off += s;
+ u1 = new BigInteger(tmp);
+
+ System.arraycopy(c, off, size, 0, 4);
+ s = byteArrayToInt(size);
+ tmp = new byte[s];
+ off += 4;
+ System.arraycopy(c, off, tmp, 0, s);
+ off += s;
+ u2 = new BigInteger(tmp);
+
+ System.arraycopy(c, off, size, 0, 4);
+ s = byteArrayToInt(size);
+ tmp = new byte[s];
+ off += 4;
+ System.arraycopy(c, off, tmp, 0, s);
+ off += s;
+ e = new BigInteger(tmp);
+
+ System.arraycopy(c, off, size, 0, 4);
+ s = byteArrayToInt(size);
+ tmp = new byte[s];
+ off += 4;
+ System.arraycopy(c, off, tmp, 0, s);
+ off += s;
+ v = new BigInteger(tmp);
+ }
+
+ public BigInteger getU1()
+ {
+ return u1;
+ }
+
+ public void setU1(BigInteger u1)
+ {
+ this.u1 = u1;
+ }
+
+ public BigInteger getU2()
+ {
+ return u2;
+ }
+
+ public void setU2(BigInteger u2)
+ {
+ this.u2 = u2;
+ }
+
+ public BigInteger getE()
+ {
+ return e;
+ }
+
+ public void setE(BigInteger e)
+ {
+ this.e = e;
+ }
+
+ public BigInteger getV()
+ {
+ return v;
+ }
+
+ public void setV(BigInteger v)
+ {
+ this.v = v;
+ }
+
+ public String toString()
+ {
+ StringBuffer result = new StringBuffer();
+
+ result.append("u1: " + u1.toString());
+ result.append("\nu2: " + u2.toString());
+ result.append("\ne: " + e.toString());
+ result.append("\nv: " + v.toString());
+
+ return result.toString();
+ }
+
+ /**
+ * convert the cipher-text in a byte array,
+ * prepending them with 4 Bytes for their length
+ *
+ * @return
+ */
+ public byte[] toByteArray()
+ {
+
+ byte[] u1Bytes = u1.toByteArray();
+ int u1Length = u1Bytes.length;
+ byte[] u2Bytes = u2.toByteArray();
+ int u2Length = u2Bytes.length;
+ byte[] eBytes = e.toByteArray();
+ int eLength = eBytes.length;
+ byte[] vBytes = v.toByteArray();
+ int vLength = vBytes.length;
+
+ int off = 0;
+ byte[] result = new byte[u1Length + u2Length + eLength + vLength + 4 * 4];
+ System.arraycopy(intToByteArray(u1Length), 0, result, 0, 4);
+ off += 4;
+ System.arraycopy(u1Bytes, 0, result, off, u1Length);
+ off += u1Length;
+ System.arraycopy(intToByteArray(u2Length), 0, result, off, 4);
+ off += 4;
+ System.arraycopy(u2Bytes, 0, result, off, u2Length);
+ off += u2Length;
+ System.arraycopy(intToByteArray(eLength), 0, result, off, 4);
+ off += 4;
+ System.arraycopy(eBytes, 0, result, off, eLength);
+ off += eLength;
+ System.arraycopy(intToByteArray(vLength), 0, result, off, 4);
+ off += 4;
+ System.arraycopy(vBytes, 0, result, off, vLength);
+
+ return result;
+ }
+
+ private byte[] intToByteArray(int in)
+ {
+ byte[] bytes = new byte[4];
+ for (int i = 0; i < 4; i++)
+ {
+ bytes[3 - i] = (byte)(in >>> (i * 8));
+ }
+ return bytes;
+ }
+
+ private int byteArrayToInt(byte[] in)
+ {
+ if (in.length != 4)
+ {
+ return -1;
+ }
+ int r = 0;
+ for (int i = 3; i >= 0; i--)
+ {
+ r += (int)in[i] << ((3 - i) * 8);
+ }
+ return r;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCoreEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCoreEngine.java
new file mode 100644
index 00000000..894b13d8
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/CramerShoupCoreEngine.java
@@ -0,0 +1,313 @@
+package org.spongycastle.crypto.engines;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.params.CramerShoupKeyParameters;
+import org.spongycastle.crypto.params.CramerShoupPrivateKeyParameters;
+import org.spongycastle.crypto.params.CramerShoupPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.BigIntegers;
+
+/**
+ * Essentially the Cramer-Shoup encryption / decryption algorithms according to
+ * "A practical public key cryptosystem provably secure against adaptive chosen ciphertext attack." (Crypto 1998)
+ */
+public class CramerShoupCoreEngine
+{
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private CramerShoupKeyParameters key;
+ private SecureRandom random;
+ private boolean forEncryption;
+ private String label = null;
+
+ /**
+ * initialise the CramerShoup engine.
+ *
+ * @param forEncryption whether this engine should encrypt or decrypt
+ * @param param the necessary CramerShoup key parameters.
+ * @param label the label for labelled CS as {@link String}
+ */
+ public void init(boolean forEncryption, CipherParameters param, String label)
+ {
+ init(forEncryption, param);
+
+ this.label = label;
+ }
+
+ /**
+ * initialise the CramerShoup engine.
+ *
+ * @param forEncryption whether this engine should encrypt or decrypt
+ * @param param the necessary CramerShoup key parameters.
+ */
+ public void init(boolean forEncryption, CipherParameters param)
+ {
+ SecureRandom providedRandom = null;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (CramerShoupKeyParameters)rParam.getParameters();
+ providedRandom = rParam.getRandom();
+ }
+ else
+ {
+ key = (CramerShoupKeyParameters)param;
+ }
+
+ this.random = initSecureRandom(forEncryption, providedRandom);
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine. For Cramer
+ * Shoup this is always one byte less than the key size on encryption, and
+ * the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ * <p/>
+ * TODO: correct?
+ */
+ public int getInputBlockSize()
+ {
+ int bitSize = key.getParameters().getP().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ else
+ {
+ return (bitSize + 7) / 8;
+ }
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine. For Cramer
+ * Shoup this is always one byte less than the key size on decryption, and
+ * the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ * <p/>
+ * TODO: correct?
+ */
+ public int getOutputBlockSize()
+ {
+ int bitSize = key.getParameters().getP().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8;
+ }
+ else
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ }
+
+ public BigInteger convertInput(byte[] in, int inOff, int inLen)
+ {
+ if (inLen > (getInputBlockSize() + 1))
+ {
+ throw new DataLengthException("input too large for Cramer Shoup cipher.");
+ }
+ else if (inLen == (getInputBlockSize() + 1) && forEncryption)
+ {
+ throw new DataLengthException("input too large for Cramer Shoup cipher.");
+ }
+
+ byte[] block;
+
+ if (inOff != 0 || inLen != in.length)
+ {
+ block = new byte[inLen];
+
+ System.arraycopy(in, inOff, block, 0, inLen);
+ }
+ else
+ {
+ block = in;
+ }
+
+ BigInteger res = new BigInteger(1, block);
+ if (res.compareTo(key.getParameters().getP()) >= 0)
+ {
+ throw new DataLengthException("input too large for Cramer Shoup cipher.");
+ }
+
+ return res;
+ }
+
+ public byte[] convertOutput(BigInteger result)
+ {
+ byte[] output = result.toByteArray();
+
+ if (!forEncryption)
+ {
+ if (output[0] == 0 && output.length > getOutputBlockSize())
+ { // have ended up with an extra zero byte, copy down.
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ if (output.length < getOutputBlockSize())
+ {// have ended up with less bytes than normal, lengthen
+ byte[] tmp = new byte[getOutputBlockSize()];
+
+ System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
+
+ return tmp;
+ }
+ }
+ else
+ {
+ if (output[0] == 0)
+ { // have ended up with an extra zero byte, copy down.
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+ }
+
+ return output;
+ }
+
+ public CramerShoupCiphertext encryptBlock(BigInteger input)
+ {
+
+ CramerShoupCiphertext result = null;
+
+ if (!key.isPrivate() && this.forEncryption && key instanceof CramerShoupPublicKeyParameters)
+ {
+ CramerShoupPublicKeyParameters pk = (CramerShoupPublicKeyParameters)key;
+ BigInteger p = pk.getParameters().getP();
+ BigInteger g1 = pk.getParameters().getG1();
+ BigInteger g2 = pk.getParameters().getG2();
+
+ BigInteger h = pk.getH();
+
+ if (!isValidMessage(input, p))
+ {
+ return result;
+ }
+
+ BigInteger r = generateRandomElement(p, random);
+
+ BigInteger u1, u2, v, e, a;
+
+ u1 = g1.modPow(r, p);
+ u2 = g2.modPow(r, p);
+ e = h.modPow(r, p).multiply(input).mod(p);
+
+ Digest digest = pk.getParameters().getH();
+ byte[] u1Bytes = u1.toByteArray();
+ digest.update(u1Bytes, 0, u1Bytes.length);
+ byte[] u2Bytes = u2.toByteArray();
+ digest.update(u2Bytes, 0, u2Bytes.length);
+ byte[] eBytes = e.toByteArray();
+ digest.update(eBytes, 0, eBytes.length);
+ if (this.label != null)
+ {
+ byte[] lBytes = this.label.getBytes();
+ digest.update(lBytes, 0, lBytes.length);
+ }
+ byte[] out = new byte[digest.getDigestSize()];
+ digest.doFinal(out, 0);
+ a = new BigInteger(1, out);
+
+ v = pk.getC().modPow(r, p).multiply(pk.getD().modPow(r.multiply(a), p)).mod(p);
+
+ result = new CramerShoupCiphertext(u1, u2, e, v);
+ }
+ return result;
+ }
+
+ public BigInteger decryptBlock(CramerShoupCiphertext input)
+ throws CramerShoupCiphertextException
+ {
+
+ BigInteger result = null;
+
+ if (key.isPrivate() && !this.forEncryption && key instanceof CramerShoupPrivateKeyParameters)
+ {
+ CramerShoupPrivateKeyParameters sk = (CramerShoupPrivateKeyParameters)key;
+
+ BigInteger p = sk.getParameters().getP();
+
+ Digest digest = sk.getParameters().getH();
+ byte[] u1Bytes = input.getU1().toByteArray();
+ digest.update(u1Bytes, 0, u1Bytes.length);
+ byte[] u2Bytes = input.getU2().toByteArray();
+ digest.update(u2Bytes, 0, u2Bytes.length);
+ byte[] eBytes = input.getE().toByteArray();
+ digest.update(eBytes, 0, eBytes.length);
+ if (this.label != null)
+ {
+ byte[] lBytes = this.label.getBytes();
+ digest.update(lBytes, 0, lBytes.length);
+ }
+ byte[] out = new byte[digest.getDigestSize()];
+ digest.doFinal(out, 0);
+
+ BigInteger a = new BigInteger(1, out);
+ BigInteger v = input.u1.modPow(sk.getX1().add(sk.getY1().multiply(a)), p).
+ multiply(input.u2.modPow(sk.getX2().add(sk.getY2().multiply(a)), p)).mod(p);
+
+ // check correctness of ciphertext
+ if (input.v.equals(v))
+ {
+ result = input.e.multiply(input.u1.modPow(sk.getZ(), p).modInverse(p)).mod(p);
+ }
+ else
+ {
+ throw new CramerShoupCiphertextException("Sorry, that ciphertext is not correct");
+ }
+ }
+ return result;
+ }
+
+ private BigInteger generateRandomElement(BigInteger p, SecureRandom random)
+ {
+ return BigIntegers.createRandomInRange(ONE, p.subtract(ONE), random);
+ }
+
+ /**
+ * just checking whether the message m is actually less than the group order p
+ */
+ private boolean isValidMessage(BigInteger m, BigInteger p)
+ {
+ return m.compareTo(p) < 0;
+ }
+
+ protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
+ {
+ return !needed ? null : (provided != null) ? provided : new SecureRandom();
+ }
+
+ /**
+ * CS exception for wrong cipher-texts
+ */
+ public static class CramerShoupCiphertextException
+ extends Exception
+ {
+ private static final long serialVersionUID = -6360977166495345076L;
+
+ public CramerShoupCiphertextException(String msg)
+ {
+ super(msg);
+ }
+
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/DESEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/DESEngine.java
new file mode 100644
index 00000000..8f43249a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/DESEngine.java
@@ -0,0 +1,495 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * a class that provides a basic DES engine.
+ */
+public class DESEngine
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey = null;
+
+ /**
+ * standard constructor.
+ */
+ public DESEngine()
+ {
+ }
+
+ /**
+ * initialise a DES cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ if (((KeyParameter)params).getKey().length > 8)
+ {
+ throw new IllegalArgumentException("DES key too long - should be 8 bytes");
+ }
+
+ workingKey = generateWorkingKey(encrypting,
+ ((KeyParameter)params).getKey());
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to DES init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "DES";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("DES engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ desFunc(workingKey, in, inOff, out, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * what follows is mainly taken from "Applied Cryptography", by
+ * Bruce Schneier, however it also bears great resemblance to Richard
+ * Outerbridge's D3DES...
+ */
+
+// private static final short[] Df_Key =
+// {
+// 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+// 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+// 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
+// };
+
+ private static final short[] bytebit =
+ {
+ 0200, 0100, 040, 020, 010, 04, 02, 01
+ };
+
+ private static final int[] bigbyte =
+ {
+ 0x800000, 0x400000, 0x200000, 0x100000,
+ 0x80000, 0x40000, 0x20000, 0x10000,
+ 0x8000, 0x4000, 0x2000, 0x1000,
+ 0x800, 0x400, 0x200, 0x100,
+ 0x80, 0x40, 0x20, 0x10,
+ 0x8, 0x4, 0x2, 0x1
+ };
+
+ /*
+ * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+ */
+
+ private static final byte[] pc1 =
+ {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
+ };
+
+ private static final byte[] totrot =
+ {
+ 1, 2, 4, 6, 8, 10, 12, 14,
+ 15, 17, 19, 21, 23, 25, 27, 28
+ };
+
+ private static final byte[] pc2 =
+ {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+ };
+
+ private static final int[] SP1 = {
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+ 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+ 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+ 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+ 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+ 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+ 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+ 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+ 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004
+ };
+
+ private static final int[] SP2 = {
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+ 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+ 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+ 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+ 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+ 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+ 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+ 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+ 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000
+ };
+
+ private static final int[] SP3 = {
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+ 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+ 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+ 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+ 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+ 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+ 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+ 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+ 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200
+ };
+
+ private static final int[] SP4 = {
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+ 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+ 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+ 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+ 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+ 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080
+ };
+
+ private static final int[] SP5 = {
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+ 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+ 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+ 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+ 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+ 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+ 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+ 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+ 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100
+ };
+
+ private static final int[] SP6 = {
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+ 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+ 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+ 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+ 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+ 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+ 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+ 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+ 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010
+ };
+
+ private static final int[] SP7 = {
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+ 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+ 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+ 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+ 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+ 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+ 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+ 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+ 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002
+ };
+
+ private static final int[] SP8 = {
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+ 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+ 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+ 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+ 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+ 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+ 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+ 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+ 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000
+ };
+
+ /**
+ * generate an integer based working key based on our secret key
+ * and what we processing we are planning to do.
+ *
+ * Acknowledgements for this routine go to James Gillogly &amp; Phil Karn.
+ * (whoever, and wherever they are!).
+ */
+ protected int[] generateWorkingKey(
+ boolean encrypting,
+ byte[] key)
+ {
+ int[] newKey = new int[32];
+ boolean[] pc1m = new boolean[56],
+ pcr = new boolean[56];
+
+ for (int j = 0; j < 56; j++)
+ {
+ int l = pc1[j];
+
+ pc1m[j] = ((key[l >>> 3] & bytebit[l & 07]) != 0);
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ int l, m, n;
+
+ if (encrypting)
+ {
+ m = i << 1;
+ }
+ else
+ {
+ m = (15 - i) << 1;
+ }
+
+ n = m + 1;
+ newKey[m] = newKey[n] = 0;
+
+ for (int j = 0; j < 28; j++)
+ {
+ l = j + totrot[i];
+ if (l < 28)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 28; j < 56; j++)
+ {
+ l = j + totrot[i];
+ if (l < 56)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 0; j < 24; j++)
+ {
+ if (pcr[pc2[j]])
+ {
+ newKey[m] |= bigbyte[j];
+ }
+
+ if (pcr[pc2[j + 24]])
+ {
+ newKey[n] |= bigbyte[j];
+ }
+ }
+ }
+
+ //
+ // store the processed key
+ //
+ for (int i = 0; i != 32; i += 2)
+ {
+ int i1, i2;
+
+ i1 = newKey[i];
+ i2 = newKey[i + 1];
+
+ newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10)
+ | ((i2 & 0x00fc0000) >>> 10) | ((i2 & 0x00000fc0) >>> 6);
+
+ newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16)
+ | ((i2 & 0x0003f000) >>> 4) | (i2 & 0x0000003f);
+ }
+
+ return newKey;
+ }
+
+ /**
+ * the DES engine.
+ */
+ protected void desFunc(
+ int[] wKey,
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int work, right, left;
+
+ left = (in[inOff + 0] & 0xff) << 24;
+ left |= (in[inOff + 1] & 0xff) << 16;
+ left |= (in[inOff + 2] & 0xff) << 8;
+ left |= (in[inOff + 3] & 0xff);
+
+ right = (in[inOff + 4] & 0xff) << 24;
+ right |= (in[inOff + 5] & 0xff) << 16;
+ right |= (in[inOff + 6] & 0xff) << 8;
+ right |= (in[inOff + 7] & 0xff);
+
+ work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+ right ^= work;
+ left ^= (work << 4);
+ work = ((left >>> 16) ^ right) & 0x0000ffff;
+ right ^= work;
+ left ^= (work << 16);
+ work = ((right >>> 2) ^ left) & 0x33333333;
+ left ^= work;
+ right ^= (work << 2);
+ work = ((right >>> 8) ^ left) & 0x00ff00ff;
+ left ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
+
+ for (int round = 0; round < 8; round++)
+ {
+ int fval;
+
+ work = (right << 28) | (right >>> 4);
+ work ^= wKey[round * 4 + 0];
+ fval = SP7[ work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = right ^ wKey[round * 4 + 1];
+ fval |= SP8[ work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ left ^= fval;
+ work = (left << 28) | (left >>> 4);
+ work ^= wKey[round * 4 + 2];
+ fval = SP7[ work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = left ^ wKey[round * 4 + 3];
+ fval |= SP8[ work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >>> 1);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 31) | (left >>> 1);
+ work = ((left >>> 8) ^ right) & 0x00ff00ff;
+ right ^= work;
+ left ^= (work << 8);
+ work = ((left >>> 2) ^ right) & 0x33333333;
+ right ^= work;
+ left ^= (work << 2);
+ work = ((right >>> 16) ^ left) & 0x0000ffff;
+ left ^= work;
+ right ^= (work << 16);
+ work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+ left ^= work;
+ right ^= (work << 4);
+
+ out[outOff + 0] = (byte)((right >>> 24) & 0xff);
+ out[outOff + 1] = (byte)((right >>> 16) & 0xff);
+ out[outOff + 2] = (byte)((right >>> 8) & 0xff);
+ out[outOff + 3] = (byte)(right & 0xff);
+ out[outOff + 4] = (byte)((left >>> 24) & 0xff);
+ out[outOff + 5] = (byte)((left >>> 16) & 0xff);
+ out[outOff + 6] = (byte)((left >>> 8) & 0xff);
+ out[outOff + 7] = (byte)(left & 0xff);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/DESedeEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/DESedeEngine.java
new file mode 100644
index 00000000..e0c0fb85
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/DESedeEngine.java
@@ -0,0 +1,127 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * a class that provides a basic DESede (or Triple DES) engine.
+ */
+public class DESedeEngine
+ extends DESEngine
+{
+ protected static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey1 = null;
+ private int[] workingKey2 = null;
+ private int[] workingKey3 = null;
+
+ private boolean forEncryption;
+
+ /**
+ * standard constructor.
+ */
+ public DESedeEngine()
+ {
+ }
+
+ /**
+ * initialise a DESede cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to DESede init - " + params.getClass().getName());
+ }
+
+ byte[] keyMaster = ((KeyParameter)params).getKey();
+
+ if (keyMaster.length != 24 && keyMaster.length != 16)
+ {
+ throw new IllegalArgumentException("key size must be 16 or 24 bytes.");
+ }
+
+ this.forEncryption = encrypting;
+
+ byte[] key1 = new byte[8];
+ System.arraycopy(keyMaster, 0, key1, 0, key1.length);
+ workingKey1 = generateWorkingKey(encrypting, key1);
+
+ byte[] key2 = new byte[8];
+ System.arraycopy(keyMaster, 8, key2, 0, key2.length);
+ workingKey2 = generateWorkingKey(!encrypting, key2);
+
+ if (keyMaster.length == 24)
+ {
+ byte[] key3 = new byte[8];
+ System.arraycopy(keyMaster, 16, key3, 0, key3.length);
+ workingKey3 = generateWorkingKey(encrypting, key3);
+ }
+ else // 16 byte key
+ {
+ workingKey3 = workingKey1;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "DESede";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey1 == null)
+ {
+ throw new IllegalStateException("DESede engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ byte[] temp = new byte[BLOCK_SIZE];
+
+ if (forEncryption)
+ {
+ desFunc(workingKey1, in, inOff, temp, 0);
+ desFunc(workingKey2, temp, 0, temp, 0);
+ desFunc(workingKey3, temp, 0, out, outOff);
+ }
+ else
+ {
+ desFunc(workingKey3, in, inOff, temp, 0);
+ desFunc(workingKey2, temp, 0, temp, 0);
+ desFunc(workingKey1, temp, 0, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/DESedeWrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/DESedeWrapEngine.java
new file mode 100644
index 00000000..756fe705
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/DESedeWrapEngine.java
@@ -0,0 +1,350 @@
+package org.spongycastle.crypto.engines;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Wrap keys according to
+ * <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-smime-key-wrap-01.txt">
+ * draft-ietf-smime-key-wrap-01.txt</A>.
+ * <p>
+ * Note:
+ * <ul>
+ * <li>this is based on a draft, and as such is subject to change - don't use this class for anything requiring long term storage.
+ * <li>if you are using this to wrap triple-des keys you need to set the
+ * parity bits on the key and, if it's a two-key triple-des key, pad it
+ * yourself.
+ * </ul>
+ */
+public class DESedeWrapEngine
+ implements Wrapper
+{
+ /** Field engine */
+ private CBCBlockCipher engine;
+
+ /** Field param */
+ private KeyParameter param;
+
+ /** Field paramPlusIV */
+ private ParametersWithIV paramPlusIV;
+
+ /** Field iv */
+ private byte[] iv;
+
+ /** Field forWrapping */
+ private boolean forWrapping;
+
+ /** Field IV2 */
+ private static final byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
+ (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
+ (byte) 0x21, (byte) 0x05 };
+
+ //
+ // checksum digest
+ //
+ Digest sha1 = new SHA1Digest();
+ byte[] digest = new byte[20];
+
+ /**
+ * Method init
+ *
+ * @param forWrapping true if for wrapping, false otherwise.
+ * @param param necessary parameters, may include KeyParameter, ParametersWithRandom, and ParametersWithIV
+ */
+ public void init(boolean forWrapping, CipherParameters param)
+ {
+
+ this.forWrapping = forWrapping;
+ this.engine = new CBCBlockCipher(new DESedeEngine());
+
+ SecureRandom sr;
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom pr = (ParametersWithRandom) param;
+ param = pr.getParameters();
+ sr = pr.getRandom();
+ }
+ else
+ {
+ sr = new SecureRandom();
+ }
+
+ if (param instanceof KeyParameter)
+ {
+ this.param = (KeyParameter)param;
+
+ if (this.forWrapping)
+ {
+
+ // Hm, we have no IV but we want to wrap ?!?
+ // well, then we have to create our own IV.
+ this.iv = new byte[8];
+ sr.nextBytes(iv);
+
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+ }
+ }
+ else if (param instanceof ParametersWithIV)
+ {
+ this.paramPlusIV = (ParametersWithIV)param;
+ this.iv = this.paramPlusIV.getIV();
+ this.param = (KeyParameter)this.paramPlusIV.getParameters();
+
+ if (this.forWrapping)
+ {
+ if ((this.iv == null) || (this.iv.length != 8))
+ {
+ throw new IllegalArgumentException("IV is not 8 octets");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "You should not supply an IV for unwrapping");
+ }
+ }
+ }
+
+ /**
+ * Method getAlgorithmName
+ *
+ * @return the algorithm name "DESede".
+ */
+ public String getAlgorithmName()
+ {
+ return "DESede";
+ }
+
+ /**
+ * Method wrap
+ *
+ * @param in byte array containing the encoded key.
+ * @param inOff off set into in that the data starts at.
+ * @param inLen length of the data.
+ * @return the wrapped bytes.
+ */
+ public byte[] wrap(byte[] in, int inOff, int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("Not initialized for wrapping");
+ }
+
+ byte keyToBeWrapped[] = new byte[inLen];
+
+ System.arraycopy(in, inOff, keyToBeWrapped, 0, inLen);
+
+ // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
+ byte[] CKS = calculateCMSKeyChecksum(keyToBeWrapped);
+
+ // Let WKCKS = WK || CKS where || is concatenation.
+ byte[] WKCKS = new byte[keyToBeWrapped.length + CKS.length];
+
+ System.arraycopy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.length);
+ System.arraycopy(CKS, 0, WKCKS, keyToBeWrapped.length, CKS.length);
+
+ // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ // initialization vector. Call the results TEMP1.
+
+ int blockSize = engine.getBlockSize();
+
+ if (WKCKS.length % blockSize != 0)
+ {
+ throw new IllegalStateException("Not multiple of block length");
+ }
+
+ engine.init(true, paramPlusIV);
+
+ byte TEMP1[] = new byte[WKCKS.length];
+
+ for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize)
+ {
+ engine.processBlock(WKCKS, currentBytePos, TEMP1, currentBytePos);
+ }
+
+ // Let TEMP2 = IV || TEMP1.
+ byte[] TEMP2 = new byte[this.iv.length + TEMP1.length];
+
+ System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
+ System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
+
+ // Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ byte[] TEMP3 = reverse(TEMP2);
+
+ // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
+ // result. It is 40 octets long if a 168 bit key is being wrapped.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(true, param2);
+
+ for (int currentBytePos = 0; currentBytePos != TEMP3.length; currentBytePos += blockSize)
+ {
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ return TEMP3;
+ }
+
+ /**
+ * Method unwrap
+ *
+ * @param in byte array containing the wrapped key.
+ * @param inOff off set into in that the data starts at.
+ * @param inLen length of the data.
+ * @return the unwrapped bytes.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("Not set for unwrapping");
+ }
+
+ if (in == null)
+ {
+ throw new InvalidCipherTextException("Null pointer as ciphertext");
+ }
+
+ final int blockSize = engine.getBlockSize();
+ if (inLen % blockSize != 0)
+ {
+ throw new InvalidCipherTextException("Ciphertext not multiple of " + blockSize);
+ }
+
+ /*
+ // Check if the length of the cipher text is reasonable given the key
+ // type. It must be 40 bytes for a 168 bit key and either 32, 40, or
+ // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported
+ // or inconsistent with the algorithm for which the key is intended,
+ // return error.
+ //
+ // we do not accept 168 bit keys. it has to be 192 bit.
+ int lengthA = (estimatedKeyLengthInBit / 8) + 16;
+ int lengthB = estimatedKeyLengthInBit % 8;
+
+ if ((lengthA != keyToBeUnwrapped.length) || (lengthB != 0)) {
+ throw new XMLSecurityException("empty");
+ }
+ */
+
+ // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
+ // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(false, param2);
+
+ byte TEMP3[] = new byte[inLen];
+
+ for (int currentBytePos = 0; currentBytePos != inLen; currentBytePos += blockSize)
+ {
+ engine.processBlock(in, inOff + currentBytePos, TEMP3, currentBytePos);
+ }
+
+ // Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ byte[] TEMP2 = reverse(TEMP3);
+
+ // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
+ this.iv = new byte[8];
+
+ byte[] TEMP1 = new byte[TEMP2.length - 8];
+
+ System.arraycopy(TEMP2, 0, this.iv, 0, 8);
+ System.arraycopy(TEMP2, 8, TEMP1, 0, TEMP2.length - 8);
+
+ // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
+ // found in the previous step. Call the result WKCKS.
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+
+ this.engine.init(false, this.paramPlusIV);
+
+ byte[] WKCKS = new byte[TEMP1.length];
+
+ for (int currentBytePos = 0; currentBytePos != WKCKS.length; currentBytePos += blockSize)
+ {
+ engine.processBlock(TEMP1, currentBytePos, WKCKS, currentBytePos);
+ }
+
+ // Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are
+ // those octets before the CKS.
+ byte[] result = new byte[WKCKS.length - 8];
+ byte[] CKStoBeVerified = new byte[8];
+
+ System.arraycopy(WKCKS, 0, result, 0, WKCKS.length - 8);
+ System.arraycopy(WKCKS, WKCKS.length - 8, CKStoBeVerified, 0, 8);
+
+ // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare
+ // with the CKS extracted in the above step. If they are not equal, return error.
+ if (!checkCMSKeyChecksum(result, CKStoBeVerified))
+ {
+ throw new InvalidCipherTextException(
+ "Checksum inside ciphertext is corrupted");
+ }
+
+ // WK is the wrapped key, now extracted for use in data decryption.
+ return result;
+ }
+
+ /**
+ * Some key wrap algorithms make use of the Key Checksum defined
+ * in CMS [CMS-Algorithms]. This is used to provide an integrity
+ * check value for the key being wrapped. The algorithm is
+ *
+ * - Compute the 20 octet SHA-1 hash on the key being wrapped.
+ * - Use the first 8 octets of this hash as the checksum value.
+ *
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum.
+ *
+ * @param key the key to check,
+ * @return the CMS checksum.
+ * @throws RuntimeException
+ */
+ private byte[] calculateCMSKeyChecksum(
+ byte[] key)
+ {
+ byte[] result = new byte[8];
+
+ sha1.update(key, 0, key.length);
+ sha1.doFinal(digest, 0);
+
+ System.arraycopy(digest, 0, result, 0, 8);
+
+ return result;
+ }
+
+ /**
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ *
+ * @param key key to be validated.
+ * @param checksum the checksum.
+ * @return true if okay, false otherwise.
+ */
+ private boolean checkCMSKeyChecksum(
+ byte[] key,
+ byte[] checksum)
+ {
+ return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
+ }
+
+ private static byte[] reverse(byte[] bs)
+ {
+ byte[] result = new byte[bs.length];
+ for (int i = 0; i < bs.length; i++)
+ {
+ result[i] = bs[bs.length - (i + 1)];
+ }
+ return result;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java
new file mode 100644
index 00000000..cf4799b0
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java
@@ -0,0 +1,217 @@
+package org.spongycastle.crypto.engines;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ElGamalKeyParameters;
+import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.spongycastle.crypto.params.ElGamalPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.BigIntegers;
+
+/**
+ * this does your basic ElGamal algorithm.
+ */
+public class ElGamalEngine
+ implements AsymmetricBlockCipher
+{
+ private ElGamalKeyParameters key;
+ private SecureRandom random;
+ private boolean forEncryption;
+ private int bitSize;
+
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ /**
+ * initialise the ElGamal engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary ElGamal key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ this.key = (ElGamalKeyParameters)p.getParameters();
+ this.random = p.getRandom();
+ }
+ else
+ {
+ this.key = (ElGamalKeyParameters)param;
+ this.random = new SecureRandom();
+ }
+
+ this.forEncryption = forEncryption;
+
+ BigInteger p = key.getParameters().getP();
+
+ bitSize = p.bitLength();
+
+ if (forEncryption)
+ {
+ if (!(key instanceof ElGamalPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ElGamalPublicKeyParameters are required for encryption.");
+ }
+ }
+ else
+ {
+ if (!(key instanceof ElGamalPrivateKeyParameters))
+ {
+ throw new IllegalArgumentException("ElGamalPrivateKeyParameters are required for decryption.");
+ }
+ }
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For ElGamal this is always one byte less than the size of P on
+ * encryption, and twice the length as the size of P on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return (bitSize - 1) / 8;
+ }
+
+ return 2 * ((bitSize + 7) / 8);
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For ElGamal this is always one byte less than the size of P on
+ * decryption, and twice the length as the size of P on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ if (forEncryption)
+ {
+ return 2 * ((bitSize + 7) / 8);
+ }
+
+ return (bitSize - 1) / 8;
+ }
+
+ /**
+ * Process a single block using the basic ElGamal algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the ElGamal process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ElGamal engine not initialised");
+ }
+
+ int maxLength = forEncryption
+ ? (bitSize - 1 + 7) / 8
+ : getInputBlockSize();
+
+ if (inLen > maxLength)
+ {
+ throw new DataLengthException("input too large for ElGamal cipher.\n");
+ }
+
+ BigInteger p = key.getParameters().getP();
+
+ if (key instanceof ElGamalPrivateKeyParameters) // decryption
+ {
+ byte[] in1 = new byte[inLen / 2];
+ byte[] in2 = new byte[inLen / 2];
+
+ System.arraycopy(in, inOff, in1, 0, in1.length);
+ System.arraycopy(in, inOff + in1.length, in2, 0, in2.length);
+
+ BigInteger gamma = new BigInteger(1, in1);
+ BigInteger phi = new BigInteger(1, in2);
+
+ ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters)key;
+ // a shortcut, which generally relies on p being prime amongst other things.
+ // if a problem with this shows up, check the p and g values!
+ BigInteger m = gamma.modPow(p.subtract(ONE).subtract(priv.getX()), p).multiply(phi).mod(p);
+
+ return BigIntegers.asUnsignedByteArray(m);
+ }
+ else // encryption
+ {
+ byte[] block;
+ if (inOff != 0 || inLen != in.length)
+ {
+ block = new byte[inLen];
+
+ System.arraycopy(in, inOff, block, 0, inLen);
+ }
+ else
+ {
+ block = in;
+ }
+
+ BigInteger input = new BigInteger(1, block);
+
+ if (input.compareTo(p) >= 0)
+ {
+ throw new DataLengthException("input too large for ElGamal cipher.\n");
+ }
+
+ ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters)key;
+
+ int pBitLength = p.bitLength();
+ BigInteger k = new BigInteger(pBitLength, random);
+
+ while (k.equals(ZERO) || (k.compareTo(p.subtract(TWO)) > 0))
+ {
+ k = new BigInteger(pBitLength, random);
+ }
+
+ BigInteger g = key.getParameters().getG();
+ BigInteger gamma = g.modPow(k, p);
+ BigInteger phi = input.multiply(pub.getY().modPow(k, p)).mod(p);
+
+ byte[] out1 = gamma.toByteArray();
+ byte[] out2 = phi.toByteArray();
+ byte[] output = new byte[this.getOutputBlockSize()];
+
+ if (out1.length > output.length / 2)
+ {
+ System.arraycopy(out1, 1, output, output.length / 2 - (out1.length - 1), out1.length - 1);
+ }
+ else
+ {
+ System.arraycopy(out1, 0, output, output.length / 2 - out1.length, out1.length);
+ }
+
+ if (out2.length > output.length / 2)
+ {
+ System.arraycopy(out2, 1, output, output.length - (out2.length - 1), out2.length - 1);
+ }
+ else
+ {
+ System.arraycopy(out2, 0, output, output.length - out2.length, out2.length);
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/GOST28147Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/GOST28147Engine.java
new file mode 100644
index 00000000..f6ecbc36
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/GOST28147Engine.java
@@ -0,0 +1,372 @@
+package org.spongycastle.crypto.engines;
+
+import java.util.Hashtable;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithSBox;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Strings;
+
+/**
+ * implementation of GOST 28147-89
+ */
+public class GOST28147Engine
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 8;
+ private int[] workingKey = null;
+ private boolean forEncryption;
+
+ private byte[] S = Sbox_Default;
+
+ // these are the S-boxes given in Applied Cryptography 2nd Ed., p. 333
+ // This is default S-box!
+ private static byte Sbox_Default[] = {
+ 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3,
+ 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9,
+ 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB,
+ 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3,
+ 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2,
+ 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE,
+ 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC,
+ 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC
+ };
+
+ /*
+ * class content S-box parameters for encrypting
+ * getting from, see: http://tools.ietf.org/id/draft-popov-cryptopro-cpalgs-01.txt
+ * http://tools.ietf.org/id/draft-popov-cryptopro-cpalgs-02.txt
+ */
+ private static byte[] ESbox_Test = {
+ 0x4,0x2,0xF,0x5,0x9,0x1,0x0,0x8,0xE,0x3,0xB,0xC,0xD,0x7,0xA,0x6,
+ 0xC,0x9,0xF,0xE,0x8,0x1,0x3,0xA,0x2,0x7,0x4,0xD,0x6,0x0,0xB,0x5,
+ 0xD,0x8,0xE,0xC,0x7,0x3,0x9,0xA,0x1,0x5,0x2,0x4,0x6,0xF,0x0,0xB,
+ 0xE,0x9,0xB,0x2,0x5,0xF,0x7,0x1,0x0,0xD,0xC,0x6,0xA,0x4,0x3,0x8,
+ 0x3,0xE,0x5,0x9,0x6,0x8,0x0,0xD,0xA,0xB,0x7,0xC,0x2,0x1,0xF,0x4,
+ 0x8,0xF,0x6,0xB,0x1,0x9,0xC,0x5,0xD,0x3,0x7,0xA,0x0,0xE,0x2,0x4,
+ 0x9,0xB,0xC,0x0,0x3,0x6,0x7,0x5,0x4,0x8,0xE,0xF,0x1,0xA,0x2,0xD,
+ 0xC,0x6,0x5,0x2,0xB,0x0,0x9,0xD,0x3,0xE,0x7,0xA,0xF,0x4,0x1,0x8
+ };
+
+ private static byte[] ESbox_A = {
+ 0x9,0x6,0x3,0x2,0x8,0xB,0x1,0x7,0xA,0x4,0xE,0xF,0xC,0x0,0xD,0x5,
+ 0x3,0x7,0xE,0x9,0x8,0xA,0xF,0x0,0x5,0x2,0x6,0xC,0xB,0x4,0xD,0x1,
+ 0xE,0x4,0x6,0x2,0xB,0x3,0xD,0x8,0xC,0xF,0x5,0xA,0x0,0x7,0x1,0x9,
+ 0xE,0x7,0xA,0xC,0xD,0x1,0x3,0x9,0x0,0x2,0xB,0x4,0xF,0x8,0x5,0x6,
+ 0xB,0x5,0x1,0x9,0x8,0xD,0xF,0x0,0xE,0x4,0x2,0x3,0xC,0x7,0xA,0x6,
+ 0x3,0xA,0xD,0xC,0x1,0x2,0x0,0xB,0x7,0x5,0x9,0x4,0x8,0xF,0xE,0x6,
+ 0x1,0xD,0x2,0x9,0x7,0xA,0x6,0x0,0x8,0xC,0x4,0x5,0xF,0x3,0xB,0xE,
+ 0xB,0xA,0xF,0x5,0x0,0xC,0xE,0x8,0x6,0x2,0x3,0x9,0x1,0x7,0xD,0x4
+ };
+
+ private static byte[] ESbox_B = {
+ 0x8,0x4,0xB,0x1,0x3,0x5,0x0,0x9,0x2,0xE,0xA,0xC,0xD,0x6,0x7,0xF,
+ 0x0,0x1,0x2,0xA,0x4,0xD,0x5,0xC,0x9,0x7,0x3,0xF,0xB,0x8,0x6,0xE,
+ 0xE,0xC,0x0,0xA,0x9,0x2,0xD,0xB,0x7,0x5,0x8,0xF,0x3,0x6,0x1,0x4,
+ 0x7,0x5,0x0,0xD,0xB,0x6,0x1,0x2,0x3,0xA,0xC,0xF,0x4,0xE,0x9,0x8,
+ 0x2,0x7,0xC,0xF,0x9,0x5,0xA,0xB,0x1,0x4,0x0,0xD,0x6,0x8,0xE,0x3,
+ 0x8,0x3,0x2,0x6,0x4,0xD,0xE,0xB,0xC,0x1,0x7,0xF,0xA,0x0,0x9,0x5,
+ 0x5,0x2,0xA,0xB,0x9,0x1,0xC,0x3,0x7,0x4,0xD,0x0,0x6,0xF,0x8,0xE,
+ 0x0,0x4,0xB,0xE,0x8,0x3,0x7,0x1,0xA,0x2,0x9,0x6,0xF,0xD,0x5,0xC
+ };
+
+ private static byte[] ESbox_C = {
+ 0x1,0xB,0xC,0x2,0x9,0xD,0x0,0xF,0x4,0x5,0x8,0xE,0xA,0x7,0x6,0x3,
+ 0x0,0x1,0x7,0xD,0xB,0x4,0x5,0x2,0x8,0xE,0xF,0xC,0x9,0xA,0x6,0x3,
+ 0x8,0x2,0x5,0x0,0x4,0x9,0xF,0xA,0x3,0x7,0xC,0xD,0x6,0xE,0x1,0xB,
+ 0x3,0x6,0x0,0x1,0x5,0xD,0xA,0x8,0xB,0x2,0x9,0x7,0xE,0xF,0xC,0x4,
+ 0x8,0xD,0xB,0x0,0x4,0x5,0x1,0x2,0x9,0x3,0xC,0xE,0x6,0xF,0xA,0x7,
+ 0xC,0x9,0xB,0x1,0x8,0xE,0x2,0x4,0x7,0x3,0x6,0x5,0xA,0x0,0xF,0xD,
+ 0xA,0x9,0x6,0x8,0xD,0xE,0x2,0x0,0xF,0x3,0x5,0xB,0x4,0x1,0xC,0x7,
+ 0x7,0x4,0x0,0x5,0xA,0x2,0xF,0xE,0xC,0x6,0x1,0xB,0xD,0x9,0x3,0x8
+ };
+
+ private static byte[] ESbox_D = {
+ 0xF,0xC,0x2,0xA,0x6,0x4,0x5,0x0,0x7,0x9,0xE,0xD,0x1,0xB,0x8,0x3,
+ 0xB,0x6,0x3,0x4,0xC,0xF,0xE,0x2,0x7,0xD,0x8,0x0,0x5,0xA,0x9,0x1,
+ 0x1,0xC,0xB,0x0,0xF,0xE,0x6,0x5,0xA,0xD,0x4,0x8,0x9,0x3,0x7,0x2,
+ 0x1,0x5,0xE,0xC,0xA,0x7,0x0,0xD,0x6,0x2,0xB,0x4,0x9,0x3,0xF,0x8,
+ 0x0,0xC,0x8,0x9,0xD,0x2,0xA,0xB,0x7,0x3,0x6,0x5,0x4,0xE,0xF,0x1,
+ 0x8,0x0,0xF,0x3,0x2,0x5,0xE,0xB,0x1,0xA,0x4,0x7,0xC,0x9,0xD,0x6,
+ 0x3,0x0,0x6,0xF,0x1,0xE,0x9,0x2,0xD,0x8,0xC,0x4,0xB,0xA,0x5,0x7,
+ 0x1,0xA,0x6,0x8,0xF,0xB,0x0,0x4,0xC,0x3,0x5,0x9,0x7,0xD,0x2,0xE
+ };
+
+ //S-box for digest
+ private static byte DSbox_Test[] = {
+ 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3,
+ 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9,
+ 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB,
+ 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3,
+ 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2,
+ 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE,
+ 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC,
+ 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC
+ };
+
+ private static byte DSbox_A[] = {
+ 0xA,0x4,0x5,0x6,0x8,0x1,0x3,0x7,0xD,0xC,0xE,0x0,0x9,0x2,0xB,0xF,
+ 0x5,0xF,0x4,0x0,0x2,0xD,0xB,0x9,0x1,0x7,0x6,0x3,0xC,0xE,0xA,0x8,
+ 0x7,0xF,0xC,0xE,0x9,0x4,0x1,0x0,0x3,0xB,0x5,0x2,0x6,0xA,0x8,0xD,
+ 0x4,0xA,0x7,0xC,0x0,0xF,0x2,0x8,0xE,0x1,0x6,0x5,0xD,0xB,0x9,0x3,
+ 0x7,0x6,0x4,0xB,0x9,0xC,0x2,0xA,0x1,0x8,0x0,0xE,0xF,0xD,0x3,0x5,
+ 0x7,0x6,0x2,0x4,0xD,0x9,0xF,0x0,0xA,0x1,0x5,0xB,0x8,0xE,0xC,0x3,
+ 0xD,0xE,0x4,0x1,0x7,0x0,0x5,0xA,0x3,0xC,0x8,0xF,0x6,0x2,0x9,0xB,
+ 0x1,0x3,0xA,0x9,0x5,0xB,0x4,0xF,0x8,0x6,0x7,0xE,0xD,0x0,0x2,0xC
+ };
+
+ //
+ // pre-defined sbox table
+ //
+ private static Hashtable sBoxes = new Hashtable();
+
+ static
+ {
+ addSBox("Default", Sbox_Default);
+ addSBox("E-TEST", ESbox_Test);
+ addSBox("E-A", ESbox_A);
+ addSBox("E-B", ESbox_B);
+ addSBox("E-C", ESbox_C);
+ addSBox("E-D", ESbox_D);
+ addSBox("D-TEST", DSbox_Test);
+ addSBox("D-A", DSbox_A);
+ }
+
+ private static void addSBox(String sBoxName, byte[] sBox)
+ {
+ sBoxes.put(Strings.toUpperCase(sBoxName), sBox);
+ }
+
+ /**
+ * standard constructor.
+ */
+ public GOST28147Engine()
+ {
+ }
+
+ /**
+ * initialise an GOST28147 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof ParametersWithSBox)
+ {
+ ParametersWithSBox param = (ParametersWithSBox)params;
+
+ //
+ // Set the S-Box
+ //
+ byte[] sBox = param.getSBox();
+ if (sBox.length != Sbox_Default.length)
+ {
+ throw new IllegalArgumentException("invalid S-box passed to GOST28147 init");
+ }
+ this.S = Arrays.clone(sBox);
+
+ //
+ // set key if there is one
+ //
+ if (param.getParameters() != null)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)param.getParameters()).getKey());
+ }
+ }
+ else if (params instanceof KeyParameter)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)params).getKey());
+ }
+ else if (params != null)
+ {
+ throw new IllegalArgumentException("invalid parameter passed to GOST28147 init - " + params.getClass().getName());
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "GOST28147";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("GOST28147 engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ GOST28147Func(workingKey, in, inOff, out, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private int[] generateWorkingKey(
+ boolean forEncryption,
+ byte[] userKey)
+ {
+ this.forEncryption = forEncryption;
+
+ if (userKey.length != 32)
+ {
+ throw new IllegalArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!");
+ }
+
+ int key[] = new int[8];
+ for(int i=0; i!=8; i++)
+ {
+ key[i] = bytesToint(userKey,i*4);
+ }
+
+ return key;
+ }
+
+ private int GOST28147_mainStep(int n1, int key)
+ {
+ int cm = (key + n1); // CM1
+
+ // S-box replacing
+
+ int om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4);
+ om += S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4);
+ om += S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4);
+ om += S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4);
+ om += S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4);
+ om += S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4);
+ om += S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4);
+ om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4);
+
+ return om << 11 | om >>> (32-11); // 11-leftshift
+ }
+
+ private void GOST28147Func(
+ int[] workingKey,
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int N1, N2, tmp; //tmp -> for saving N1
+ N1 = bytesToint(in, inOff);
+ N2 = bytesToint(in, inOff + 4);
+
+ if (this.forEncryption)
+ {
+ for(int k = 0; k < 3; k++) // 1-24 steps
+ {
+ for(int j = 0; j < 8; j++)
+ {
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ for(int j = 7; j > 0; j--) // 25-31 steps
+ {
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ else //decrypt
+ {
+ for(int j = 0; j < 8; j++) // 1-8 steps
+ {
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ for(int k = 0; k < 3; k++) //9-31 steps
+ {
+ for(int j = 7; j >= 0; j--)
+ {
+ if ((k == 2) && (j==0))
+ {
+ break; // break 32 step
+ }
+ tmp = N1;
+ N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
+ N2 = tmp;
+ }
+ }
+ }
+
+ N2 = N2 ^ GOST28147_mainStep(N1, workingKey[0]); // 32 step (N1=N1)
+
+ intTobytes(N1, out, outOff);
+ intTobytes(N2, out, outOff + 4);
+ }
+
+ //array of bytes to type int
+ private int bytesToint(
+ byte[] in,
+ int inOff)
+ {
+ return ((in[inOff + 3] << 24) & 0xff000000) + ((in[inOff + 2] << 16) & 0xff0000) +
+ ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff);
+ }
+
+ //int to array of bytes
+ private void intTobytes(
+ int num,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff + 3] = (byte)(num >>> 24);
+ out[outOff + 2] = (byte)(num >>> 16);
+ out[outOff + 1] = (byte)(num >>> 8);
+ out[outOff] = (byte)num;
+ }
+
+ /**
+ * Return the S-Box associated with SBoxName
+ * @param sBoxName name of the S-Box
+ * @return byte array representing the S-Box
+ */
+ public static byte[] getSBox(
+ String sBoxName)
+ {
+ byte[] sBox = (byte[])sBoxes.get(Strings.toUpperCase(sBoxName));
+
+ if (sBox == null)
+ {
+ throw new IllegalArgumentException("Unknown S-Box - possible types: "
+ + "\"Default\", \"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"D-Test\", \"D-A\".");
+ }
+
+ return Arrays.clone(sBox);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/Grain128Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/Grain128Engine.java
new file mode 100644
index 00000000..eedd26c7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/Grain128Engine.java
@@ -0,0 +1,304 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implementation of Martin Hell's, Thomas Johansson's and Willi Meier's stream
+ * cipher, Grain-128.
+ */
+public class Grain128Engine
+ implements StreamCipher
+{
+
+ /**
+ * Constants
+ */
+ private static final int STATE_SIZE = 4;
+
+ /**
+ * Variables to hold the state of the engine during encryption and
+ * decryption
+ */
+ private byte[] workingKey;
+ private byte[] workingIV;
+ private byte[] out;
+ private int[] lfsr;
+ private int[] nfsr;
+ private int output;
+ private int index = 4;
+
+ private boolean initialised = false;
+
+ public String getAlgorithmName()
+ {
+ return "Grain-128";
+ }
+
+ /**
+ * Initialize a Grain-128 cipher.
+ *
+ * @param forEncryption Whether or not we are for encryption.
+ * @param params The parameters required to set up the cipher.
+ * @throws IllegalArgumentException If the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ /**
+ * Grain encryption and decryption is completely symmetrical, so the
+ * 'forEncryption' is irrelevant.
+ */
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "Grain-128 Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV)params;
+
+ byte[] iv = ivParams.getIV();
+
+ if (iv == null || iv.length != 12)
+ {
+ throw new IllegalArgumentException(
+ "Grain-128 requires exactly 12 bytes of IV");
+ }
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "Grain-128 Init parameters must include a key");
+ }
+
+ KeyParameter key = (KeyParameter)ivParams.getParameters();
+
+ /**
+ * Initialize variables.
+ */
+ workingIV = new byte[key.getKey().length];
+ workingKey = new byte[key.getKey().length];
+ lfsr = new int[STATE_SIZE];
+ nfsr = new int[STATE_SIZE];
+ out = new byte[4];
+
+ System.arraycopy(iv, 0, workingIV, 0, iv.length);
+ System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
+
+ reset();
+ }
+
+ /**
+ * 256 clocks initialization phase.
+ */
+ private void initGrain()
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ output = getOutput();
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0] ^ output);
+ lfsr = shift(lfsr, getOutputLFSR() ^ output);
+ }
+ initialised = true;
+ }
+
+ /**
+ * Get output from non-linear function g(x).
+ *
+ * @return Output from NFSR.
+ */
+ private int getOutputNFSR()
+ {
+ int b0 = nfsr[0];
+ int b3 = nfsr[0] >>> 3 | nfsr[1] << 29;
+ int b11 = nfsr[0] >>> 11 | nfsr[1] << 21;
+ int b13 = nfsr[0] >>> 13 | nfsr[1] << 19;
+ int b17 = nfsr[0] >>> 17 | nfsr[1] << 15;
+ int b18 = nfsr[0] >>> 18 | nfsr[1] << 14;
+ int b26 = nfsr[0] >>> 26 | nfsr[1] << 6;
+ int b27 = nfsr[0] >>> 27 | nfsr[1] << 5;
+ int b40 = nfsr[1] >>> 8 | nfsr[2] << 24;
+ int b48 = nfsr[1] >>> 16 | nfsr[2] << 16;
+ int b56 = nfsr[1] >>> 24 | nfsr[2] << 8;
+ int b59 = nfsr[1] >>> 27 | nfsr[2] << 5;
+ int b61 = nfsr[1] >>> 29 | nfsr[2] << 3;
+ int b65 = nfsr[2] >>> 1 | nfsr[3] << 31;
+ int b67 = nfsr[2] >>> 3 | nfsr[3] << 29;
+ int b68 = nfsr[2] >>> 4 | nfsr[3] << 28;
+ int b84 = nfsr[2] >>> 20 | nfsr[3] << 12;
+ int b91 = nfsr[2] >>> 27 | nfsr[3] << 5;
+ int b96 = nfsr[3];
+
+ return b0 ^ b26 ^ b56 ^ b91 ^ b96 ^ b3 & b67 ^ b11 & b13 ^ b17 & b18
+ ^ b27 & b59 ^ b40 & b48 ^ b61 & b65 ^ b68 & b84;
+ }
+
+ /**
+ * Get output from linear function f(x).
+ *
+ * @return Output from LFSR.
+ */
+ private int getOutputLFSR()
+ {
+ int s0 = lfsr[0];
+ int s7 = lfsr[0] >>> 7 | lfsr[1] << 25;
+ int s38 = lfsr[1] >>> 6 | lfsr[2] << 26;
+ int s70 = lfsr[2] >>> 6 | lfsr[3] << 26;
+ int s81 = lfsr[2] >>> 17 | lfsr[3] << 15;
+ int s96 = lfsr[3];
+
+ return s0 ^ s7 ^ s38 ^ s70 ^ s81 ^ s96;
+ }
+
+ /**
+ * Get output from output function h(x).
+ *
+ * @return Output from h(x).
+ */
+ private int getOutput()
+ {
+ int b2 = nfsr[0] >>> 2 | nfsr[1] << 30;
+ int b12 = nfsr[0] >>> 12 | nfsr[1] << 20;
+ int b15 = nfsr[0] >>> 15 | nfsr[1] << 17;
+ int b36 = nfsr[1] >>> 4 | nfsr[2] << 28;
+ int b45 = nfsr[1] >>> 13 | nfsr[2] << 19;
+ int b64 = nfsr[2];
+ int b73 = nfsr[2] >>> 9 | nfsr[3] << 23;
+ int b89 = nfsr[2] >>> 25 | nfsr[3] << 7;
+ int b95 = nfsr[2] >>> 31 | nfsr[3] << 1;
+ int s8 = lfsr[0] >>> 8 | lfsr[1] << 24;
+ int s13 = lfsr[0] >>> 13 | lfsr[1] << 19;
+ int s20 = lfsr[0] >>> 20 | lfsr[1] << 12;
+ int s42 = lfsr[1] >>> 10 | lfsr[2] << 22;
+ int s60 = lfsr[1] >>> 28 | lfsr[2] << 4;
+ int s79 = lfsr[2] >>> 15 | lfsr[3] << 17;
+ int s93 = lfsr[2] >>> 29 | lfsr[3] << 3;
+ int s95 = lfsr[2] >>> 31 | lfsr[3] << 1;
+
+ return b12 & s8 ^ s13 & s20 ^ b95 & s42 ^ s60 & s79 ^ b12 & b95 & s95 ^ s93
+ ^ b2 ^ b15 ^ b36 ^ b45 ^ b64 ^ b73 ^ b89;
+ }
+
+ /**
+ * Shift array 32 bits and add val to index.length - 1.
+ *
+ * @param array The array to shift.
+ * @param val The value to shift in.
+ * @return The shifted array with val added to index.length - 1.
+ */
+ private int[] shift(int[] array, int val)
+ {
+ array[0] = array[1];
+ array[1] = array[2];
+ array[2] = array[3];
+ array[3] = val;
+
+ return array;
+ }
+
+ /**
+ * Set keys, reset cipher.
+ *
+ * @param keyBytes The key.
+ * @param ivBytes The IV.
+ */
+ private void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ ivBytes[12] = (byte)0xFF;
+ ivBytes[13] = (byte)0xFF;
+ ivBytes[14] = (byte)0xFF;
+ ivBytes[15] = (byte)0xFF;
+ workingKey = keyBytes;
+ workingIV = ivBytes;
+
+ /**
+ * Load NFSR and LFSR
+ */
+ int j = 0;
+ for (int i = 0; i < nfsr.length; i++)
+ {
+ nfsr[i] = ((workingKey[j + 3]) << 24) | ((workingKey[j + 2]) << 16)
+ & 0x00FF0000 | ((workingKey[j + 1]) << 8) & 0x0000FF00
+ | ((workingKey[j]) & 0x000000FF);
+
+ lfsr[i] = ((workingIV[j + 3]) << 24) | ((workingIV[j + 2]) << 16)
+ & 0x00FF0000 | ((workingIV[j + 1]) << 8) & 0x0000FF00
+ | ((workingIV[j]) & 0x000000FF);
+ j += 4;
+ }
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff)
+ throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getKeyStream());
+ }
+
+ return len;
+ }
+
+ public void reset()
+ {
+ index = 4;
+ setKey(workingKey, workingIV);
+ initGrain();
+ }
+
+ /**
+ * Run Grain one round(i.e. 32 bits).
+ */
+ private void oneRound()
+ {
+ output = getOutput();
+ out[0] = (byte)output;
+ out[1] = (byte)(output >> 8);
+ out[2] = (byte)(output >> 16);
+ out[3] = (byte)(output >> 24);
+
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0]);
+ lfsr = shift(lfsr, getOutputLFSR());
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+ return (byte)(in ^ getKeyStream());
+ }
+
+ private byte getKeyStream()
+ {
+ if (index > 3)
+ {
+ oneRound();
+ index = 0;
+ }
+ return out[index++];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/Grainv1Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/Grainv1Engine.java
new file mode 100644
index 00000000..69f78772
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/Grainv1Engine.java
@@ -0,0 +1,290 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * Implementation of Martin Hell's, Thomas Johansson's and Willi Meier's stream
+ * cipher, Grain v1.
+ */
+public class Grainv1Engine
+ implements StreamCipher
+{
+
+ /**
+ * Constants
+ */
+ private static final int STATE_SIZE = 5;
+
+ /**
+ * Variables to hold the state of the engine during encryption and
+ * decryption
+ */
+ private byte[] workingKey;
+ private byte[] workingIV;
+ private byte[] out;
+ private int[] lfsr;
+ private int[] nfsr;
+ private int output;
+ private int index = 2;
+
+ private boolean initialised = false;
+
+ public String getAlgorithmName()
+ {
+ return "Grain v1";
+ }
+
+ /**
+ * Initialize a Grain v1 cipher.
+ *
+ * @param forEncryption Whether or not we are for encryption.
+ * @param params The parameters required to set up the cipher.
+ * @throws IllegalArgumentException If the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ /**
+ * Grain encryption and decryption is completely symmetrical, so the
+ * 'forEncryption' is irrelevant.
+ */
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "Grain v1 Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV)params;
+
+ byte[] iv = ivParams.getIV();
+
+ if (iv == null || iv.length != 8)
+ {
+ throw new IllegalArgumentException(
+ "Grain v1 requires exactly 8 bytes of IV");
+ }
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "Grain v1 Init parameters must include a key");
+ }
+
+ KeyParameter key = (KeyParameter)ivParams.getParameters();
+
+ /**
+ * Initialize variables.
+ */
+ workingIV = new byte[key.getKey().length];
+ workingKey = new byte[key.getKey().length];
+ lfsr = new int[STATE_SIZE];
+ nfsr = new int[STATE_SIZE];
+ out = new byte[2];
+
+ System.arraycopy(iv, 0, workingIV, 0, iv.length);
+ System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
+
+ reset();
+ }
+
+ /**
+ * 160 clocks initialization phase.
+ */
+ private void initGrain()
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ output = getOutput();
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0] ^ output);
+ lfsr = shift(lfsr, getOutputLFSR() ^ output);
+ }
+ initialised = true;
+ }
+
+ /**
+ * Get output from non-linear function g(x).
+ *
+ * @return Output from NFSR.
+ */
+ private int getOutputNFSR()
+ {
+ int b0 = nfsr[0];
+ int b9 = nfsr[0] >>> 9 | nfsr[1] << 7;
+ int b14 = nfsr[0] >>> 14 | nfsr[1] << 2;
+ int b15 = nfsr[0] >>> 15 | nfsr[1] << 1;
+ int b21 = nfsr[1] >>> 5 | nfsr[2] << 11;
+ int b28 = nfsr[1] >>> 12 | nfsr[2] << 4;
+ int b33 = nfsr[2] >>> 1 | nfsr[3] << 15;
+ int b37 = nfsr[2] >>> 5 | nfsr[3] << 11;
+ int b45 = nfsr[2] >>> 13 | nfsr[3] << 3;
+ int b52 = nfsr[3] >>> 4 | nfsr[4] << 12;
+ int b60 = nfsr[3] >>> 12 | nfsr[4] << 4;
+ int b62 = nfsr[3] >>> 14 | nfsr[4] << 2;
+ int b63 = nfsr[3] >>> 15 | nfsr[4] << 1;
+
+ return (b62 ^ b60 ^ b52 ^ b45 ^ b37 ^ b33 ^ b28 ^ b21 ^ b14
+ ^ b9 ^ b0 ^ b63 & b60 ^ b37 & b33 ^ b15 & b9 ^ b60 & b52 & b45
+ ^ b33 & b28 & b21 ^ b63 & b45 & b28 & b9 ^ b60 & b52 & b37
+ & b33 ^ b63 & b60 & b21 & b15 ^ b63 & b60 & b52 & b45 & b37
+ ^ b33 & b28 & b21 & b15 & b9 ^ b52 & b45 & b37 & b33 & b28
+ & b21) & 0x0000FFFF;
+ }
+
+ /**
+ * Get output from linear function f(x).
+ *
+ * @return Output from LFSR.
+ */
+ private int getOutputLFSR()
+ {
+ int s0 = lfsr[0];
+ int s13 = lfsr[0] >>> 13 | lfsr[1] << 3;
+ int s23 = lfsr[1] >>> 7 | lfsr[2] << 9;
+ int s38 = lfsr[2] >>> 6 | lfsr[3] << 10;
+ int s51 = lfsr[3] >>> 3 | lfsr[4] << 13;
+ int s62 = lfsr[3] >>> 14 | lfsr[4] << 2;
+
+ return (s0 ^ s13 ^ s23 ^ s38 ^ s51 ^ s62) & 0x0000FFFF;
+ }
+
+ /**
+ * Get output from output function h(x).
+ *
+ * @return Output from h(x).
+ */
+ private int getOutput()
+ {
+ int b1 = nfsr[0] >>> 1 | nfsr[1] << 15;
+ int b2 = nfsr[0] >>> 2 | nfsr[1] << 14;
+ int b4 = nfsr[0] >>> 4 | nfsr[1] << 12;
+ int b10 = nfsr[0] >>> 10 | nfsr[1] << 6;
+ int b31 = nfsr[1] >>> 15 | nfsr[2] << 1;
+ int b43 = nfsr[2] >>> 11 | nfsr[3] << 5;
+ int b56 = nfsr[3] >>> 8 | nfsr[4] << 8;
+ int b63 = nfsr[3] >>> 15 | nfsr[4] << 1;
+ int s3 = lfsr[0] >>> 3 | lfsr[1] << 13;
+ int s25 = lfsr[1] >>> 9 | lfsr[2] << 7;
+ int s46 = lfsr[2] >>> 14 | lfsr[3] << 2;
+ int s64 = lfsr[4];
+
+ return (s25 ^ b63 ^ s3 & s64 ^ s46 & s64 ^ s64 & b63 ^ s3
+ & s25 & s46 ^ s3 & s46 & s64 ^ s3 & s46 & b63 ^ s25 & s46 & b63 ^ s46
+ & s64 & b63 ^ b1 ^ b2 ^ b4 ^ b10 ^ b31 ^ b43 ^ b56) & 0x0000FFFF;
+ }
+
+ /**
+ * Shift array 16 bits and add val to index.length - 1.
+ *
+ * @param array The array to shift.
+ * @param val The value to shift in.
+ * @return The shifted array with val added to index.length - 1.
+ */
+ private int[] shift(int[] array, int val)
+ {
+ array[0] = array[1];
+ array[1] = array[2];
+ array[2] = array[3];
+ array[3] = array[4];
+ array[4] = val;
+
+ return array;
+ }
+
+ /**
+ * Set keys, reset cipher.
+ *
+ * @param keyBytes The key.
+ * @param ivBytes The IV.
+ */
+ private void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ ivBytes[8] = (byte)0xFF;
+ ivBytes[9] = (byte)0xFF;
+ workingKey = keyBytes;
+ workingIV = ivBytes;
+
+ /**
+ * Load NFSR and LFSR
+ */
+ int j = 0;
+ for (int i = 0; i < nfsr.length; i++)
+ {
+ nfsr[i] = (workingKey[j + 1] << 8 | workingKey[j] & 0xFF) & 0x0000FFFF;
+ lfsr[i] = (workingIV[j + 1] << 8 | workingIV[j] & 0xFF) & 0x0000FFFF;
+ j += 2;
+ }
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff)
+ throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getKeyStream());
+ }
+
+ return len;
+ }
+
+ public void reset()
+ {
+ index = 2;
+ setKey(workingKey, workingIV);
+ initGrain();
+ }
+
+ /**
+ * Run Grain one round(i.e. 16 bits).
+ */
+ private void oneRound()
+ {
+ output = getOutput();
+ out[0] = (byte)output;
+ out[1] = (byte)(output >> 8);
+
+ nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0]);
+ lfsr = shift(lfsr, getOutputLFSR());
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+ return (byte)(in ^ getKeyStream());
+ }
+
+ private byte getKeyStream()
+ {
+ if (index > 1)
+ {
+ oneRound();
+ index = 0;
+ }
+ return out[index++];
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/HC128Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/HC128Engine.java
new file mode 100644
index 00000000..93882825
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/HC128Engine.java
@@ -0,0 +1,259 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * HC-128 is a software-efficient stream cipher created by Hongjun Wu. It
+ * generates keystream from a 128-bit secret key and a 128-bit initialization
+ * vector.
+ * <p>
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf
+ * </p><p>
+ * It is a third phase candidate in the eStream contest, and is patent-free.
+ * No attacks are known as of today (April 2007). See
+ *
+ * http://www.ecrypt.eu.org/stream/hcp3.html
+ * </p>
+ */
+public class HC128Engine
+ implements StreamCipher
+{
+ private int[] p = new int[512];
+ private int[] q = new int[512];
+ private int cnt = 0;
+
+ private static int f1(int x)
+ {
+ return rotateRight(x, 7) ^ rotateRight(x, 18)
+ ^ (x >>> 3);
+ }
+
+ private static int f2(int x)
+ {
+ return rotateRight(x, 17) ^ rotateRight(x, 19)
+ ^ (x >>> 10);
+ }
+
+ private int g1(int x, int y, int z)
+ {
+ return (rotateRight(x, 10) ^ rotateRight(z, 23))
+ + rotateRight(y, 8);
+ }
+
+ private int g2(int x, int y, int z)
+ {
+ return (rotateLeft(x, 10) ^ rotateLeft(z, 23)) + rotateLeft(y, 8);
+ }
+
+ private static int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ private static int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+
+ private int h1(int x)
+ {
+ return q[x & 0xFF] + q[((x >> 16) & 0xFF) + 256];
+ }
+
+ private int h2(int x)
+ {
+ return p[x & 0xFF] + p[((x >> 16) & 0xFF) + 256];
+ }
+
+ private static int mod1024(int x)
+ {
+ return x & 0x3FF;
+ }
+
+ private static int mod512(int x)
+ {
+ return x & 0x1FF;
+ }
+
+ private static int dim(int x, int y)
+ {
+ return mod512(x - y);
+ }
+
+ private int step()
+ {
+ int j = mod512(cnt);
+ int ret;
+ if (cnt < 512)
+ {
+ p[j] += g1(p[dim(j, 3)], p[dim(j, 10)], p[dim(j, 511)]);
+ ret = h1(p[dim(j, 12)]) ^ p[j];
+ }
+ else
+ {
+ q[j] += g2(q[dim(j, 3)], q[dim(j, 10)], q[dim(j, 511)]);
+ ret = h2(q[dim(j, 12)]) ^ q[j];
+ }
+ cnt = mod1024(cnt + 1);
+ return ret;
+ }
+
+ private byte[] key, iv;
+ private boolean initialised;
+
+ private void init()
+ {
+ if (key.length != 16)
+ {
+ throw new java.lang.IllegalArgumentException(
+ "The key must be 128 bits long");
+ }
+
+ idx = 0;
+ cnt = 0;
+
+ int[] w = new int[1280];
+
+ for (int i = 0; i < 16; i++)
+ {
+ w[i >> 2] |= (key[i] & 0xff) << (8 * (i & 0x3));
+ }
+ System.arraycopy(w, 0, w, 4, 4);
+
+ for (int i = 0; i < iv.length && i < 16; i++)
+ {
+ w[(i >> 2) + 8] |= (iv[i] & 0xff) << (8 * (i & 0x3));
+ }
+ System.arraycopy(w, 8, w, 12, 4);
+
+ for (int i = 16; i < 1280; i++)
+ {
+ w[i] = f2(w[i - 2]) + w[i - 7] + f1(w[i - 15]) + w[i - 16] + i;
+ }
+
+ System.arraycopy(w, 256, p, 0, 512);
+ System.arraycopy(w, 768, q, 0, 512);
+
+ for (int i = 0; i < 512; i++)
+ {
+ p[i] = step();
+ }
+ for (int i = 0; i < 512; i++)
+ {
+ q[i] = step();
+ }
+
+ cnt = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "HC-128";
+ }
+
+ /**
+ * Initialise a HC-128 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption. Irrelevant, as
+ * encryption and decryption are the same.
+ * @param params the parameters required to set up the cipher.
+ * @throws IllegalArgumentException if the params argument is
+ * inappropriate (ie. the key is not 128 bit long).
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ CipherParameters keyParam = params;
+
+ if (params instanceof ParametersWithIV)
+ {
+ iv = ((ParametersWithIV)params).getIV();
+ keyParam = ((ParametersWithIV)params).getParameters();
+ }
+ else
+ {
+ iv = new byte[0];
+ }
+
+ if (keyParam instanceof KeyParameter)
+ {
+ key = ((KeyParameter)keyParam).getKey();
+ init();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Invalid parameter passed to HC128 init - "
+ + params.getClass().getName());
+ }
+
+ initialised = true;
+ }
+
+ private byte[] buf = new byte[4];
+ private int idx = 0;
+
+ private byte getByte()
+ {
+ if (idx == 0)
+ {
+ int step = step();
+ buf[0] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[1] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[2] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[3] = (byte)(step & 0xFF);
+ }
+ byte ret = buf[idx];
+ idx = idx + 1 & 0x3;
+ return ret;
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff) throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getByte());
+ }
+
+ return len;
+ }
+
+ public void reset()
+ {
+ init();
+ }
+
+ public byte returnByte(byte in)
+ {
+ return (byte)(in ^ getByte());
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/HC256Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/HC256Engine.java
new file mode 100644
index 00000000..4e42672b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/HC256Engine.java
@@ -0,0 +1,246 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+/**
+ * HC-256 is a software-efficient stream cipher created by Hongjun Wu. It
+ * generates keystream from a 256-bit secret key and a 256-bit initialization
+ * vector.
+ * <p>
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
+ * </p><p>
+ * Its brother, HC-128, is a third phase candidate in the eStream contest.
+ * The algorithm is patent-free. No attacks are known as of today (April 2007).
+ * See
+ *
+ * http://www.ecrypt.eu.org/stream/hcp3.html
+ * </p>
+ */
+public class HC256Engine
+ implements StreamCipher
+{
+ private int[] p = new int[1024];
+ private int[] q = new int[1024];
+ private int cnt = 0;
+
+ private int step()
+ {
+ int j = cnt & 0x3FF;
+ int ret;
+ if (cnt < 1024)
+ {
+ int x = p[(j - 3 & 0x3FF)];
+ int y = p[(j - 1023 & 0x3FF)];
+ p[j] += p[(j - 10 & 0x3FF)]
+ + (rotateRight(x, 10) ^ rotateRight(y, 23))
+ + q[((x ^ y) & 0x3FF)];
+
+ x = p[(j - 12 & 0x3FF)];
+ ret = (q[x & 0xFF] + q[((x >> 8) & 0xFF) + 256]
+ + q[((x >> 16) & 0xFF) + 512] + q[((x >> 24) & 0xFF) + 768])
+ ^ p[j];
+ }
+ else
+ {
+ int x = q[(j - 3 & 0x3FF)];
+ int y = q[(j - 1023 & 0x3FF)];
+ q[j] += q[(j - 10 & 0x3FF)]
+ + (rotateRight(x, 10) ^ rotateRight(y, 23))
+ + p[((x ^ y) & 0x3FF)];
+
+ x = q[(j - 12 & 0x3FF)];
+ ret = (p[x & 0xFF] + p[((x >> 8) & 0xFF) + 256]
+ + p[((x >> 16) & 0xFF) + 512] + p[((x >> 24) & 0xFF) + 768])
+ ^ q[j];
+ }
+ cnt = cnt + 1 & 0x7FF;
+ return ret;
+ }
+
+ private byte[] key, iv;
+ private boolean initialised;
+
+ private void init()
+ {
+ if (key.length != 32 && key.length != 16)
+ {
+ throw new IllegalArgumentException(
+ "The key must be 128/256 bits long");
+ }
+
+ if (iv.length < 16)
+ {
+ throw new IllegalArgumentException(
+ "The IV must be at least 128 bits long");
+ }
+
+ if (key.length != 32)
+ {
+ byte[] k = new byte[32];
+
+ System.arraycopy(key, 0, k, 0, key.length);
+ System.arraycopy(key, 0, k, 16, key.length);
+
+ key = k;
+ }
+
+ if (iv.length < 32)
+ {
+ byte[] newIV = new byte[32];
+
+ System.arraycopy(iv, 0, newIV, 0, iv.length);
+ System.arraycopy(iv, 0, newIV, iv.length, newIV.length - iv.length);
+
+ iv = newIV;
+ }
+
+ idx = 0;
+ cnt = 0;
+
+ int[] w = new int[2560];
+
+ for (int i = 0; i < 32; i++)
+ {
+ w[i >> 2] |= (key[i] & 0xff) << (8 * (i & 0x3));
+ }
+
+ for (int i = 0; i < 32; i++)
+ {
+ w[(i >> 2) + 8] |= (iv[i] & 0xff) << (8 * (i & 0x3));
+ }
+
+ for (int i = 16; i < 2560; i++)
+ {
+ int x = w[i - 2];
+ int y = w[i - 15];
+ w[i] = (rotateRight(x, 17) ^ rotateRight(x, 19) ^ (x >>> 10))
+ + w[i - 7]
+ + (rotateRight(y, 7) ^ rotateRight(y, 18) ^ (y >>> 3))
+ + w[i - 16] + i;
+ }
+
+ System.arraycopy(w, 512, p, 0, 1024);
+ System.arraycopy(w, 1536, q, 0, 1024);
+
+ for (int i = 0; i < 4096; i++)
+ {
+ step();
+ }
+
+ cnt = 0;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "HC-256";
+ }
+
+ /**
+ * Initialise a HC-256 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption. Irrelevant, as
+ * encryption and decryption are the same.
+ * @param params the parameters required to set up the cipher.
+ * @throws IllegalArgumentException if the params argument is
+ * inappropriate (ie. the key is not 256 bit long).
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ CipherParameters keyParam = params;
+
+ if (params instanceof ParametersWithIV)
+ {
+ iv = ((ParametersWithIV)params).getIV();
+ keyParam = ((ParametersWithIV)params).getParameters();
+ }
+ else
+ {
+ iv = new byte[0];
+ }
+
+ if (keyParam instanceof KeyParameter)
+ {
+ key = ((KeyParameter)keyParam).getKey();
+ init();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "Invalid parameter passed to HC256 init - "
+ + params.getClass().getName());
+ }
+
+ initialised = true;
+ }
+
+ private byte[] buf = new byte[4];
+ private int idx = 0;
+
+ private byte getByte()
+ {
+ if (idx == 0)
+ {
+ int step = step();
+ buf[0] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[1] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[2] = (byte)(step & 0xFF);
+ step >>= 8;
+ buf[3] = (byte)(step & 0xFF);
+ }
+ byte ret = buf[idx];
+ idx = idx + 1 & 0x3;
+ return ret;
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff) throws DataLengthException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()
+ + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[outOff + i] = (byte)(in[inOff + i] ^ getByte());
+ }
+
+ return len;
+ }
+
+ public void reset()
+ {
+ init();
+ }
+
+ public byte returnByte(byte in)
+ {
+ return (byte)(in ^ getByte());
+ }
+
+ private static int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/IDEAEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/IDEAEngine.java
new file mode 100644
index 00000000..68cef879
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/IDEAEngine.java
@@ -0,0 +1,357 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
+ * <p>
+ * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
+ * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (barring 1 typo at the
+ * end of the mulinv function!).
+ * <p>
+ * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
+ * <p>
+ * Note: This algorithm was patented in the USA, Japan and Europe. These patents expired in 2011/2012.
+ */
+public class IDEAEngine
+ implements BlockCipher
+{
+ protected static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey = null;
+
+ /**
+ * standard constructor.
+ */
+ public IDEAEngine()
+ {
+ }
+
+ /**
+ * initialise an IDEA cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ workingKey = generateWorkingKey(forEncryption,
+ ((KeyParameter)params).getKey());
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to IDEA init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "IDEA";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("IDEA engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ ideaFunc(workingKey, in, inOff, out, outOff);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private static final int MASK = 0xffff;
+ private static final int BASE = 0x10001;
+
+ private int bytesToWord(
+ byte[] in,
+ int inOff)
+ {
+ return ((in[inOff] << 8) & 0xff00) + (in[inOff + 1] & 0xff);
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)(word >>> 8);
+ out[outOff + 1] = (byte)word;
+ }
+
+ /**
+ * return x = x * y where the multiplication is done modulo
+ * 65537 (0x10001) (as defined in the IDEA specification) and
+ * a zero input is taken to be 65536 (0x10000).
+ *
+ * @param x the x value
+ * @param y the y value
+ * @return x = x * y
+ */
+ private int mul(
+ int x,
+ int y)
+ {
+ if (x == 0)
+ {
+ x = (BASE - y);
+ }
+ else if (y == 0)
+ {
+ x = (BASE - x);
+ }
+ else
+ {
+ int p = x * y;
+
+ y = p & MASK;
+ x = p >>> 16;
+ x = y - x + ((y < x) ? 1 : 0);
+ }
+
+ return x & MASK;
+ }
+
+ private void ideaFunc(
+ int[] workingKey,
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x0, x1, x2, x3, t0, t1;
+ int keyOff = 0;
+
+ x0 = bytesToWord(in, inOff);
+ x1 = bytesToWord(in, inOff + 2);
+ x2 = bytesToWord(in, inOff + 4);
+ x3 = bytesToWord(in, inOff + 6);
+
+ for (int round = 0; round < 8; round++)
+ {
+ x0 = mul(x0, workingKey[keyOff++]);
+ x1 += workingKey[keyOff++];
+ x1 &= MASK;
+ x2 += workingKey[keyOff++];
+ x2 &= MASK;
+ x3 = mul(x3, workingKey[keyOff++]);
+
+ t0 = x1;
+ t1 = x2;
+ x2 ^= x0;
+ x1 ^= x3;
+
+ x2 = mul(x2, workingKey[keyOff++]);
+ x1 += x2;
+ x1 &= MASK;
+
+ x1 = mul(x1, workingKey[keyOff++]);
+ x2 += x1;
+ x2 &= MASK;
+
+ x0 ^= x1;
+ x3 ^= x2;
+ x1 ^= t1;
+ x2 ^= t0;
+ }
+
+ wordToBytes(mul(x0, workingKey[keyOff++]), out, outOff);
+ wordToBytes(x2 + workingKey[keyOff++], out, outOff + 2); /* NB: Order */
+ wordToBytes(x1 + workingKey[keyOff++], out, outOff + 4);
+ wordToBytes(mul(x3, workingKey[keyOff]), out, outOff + 6);
+ }
+
+ /**
+ * The following function is used to expand the user key to the encryption
+ * subkey. The first 16 bytes are the user key, and the rest of the subkey
+ * is calculated by rotating the previous 16 bytes by 25 bits to the left,
+ * and so on until the subkey is completed.
+ */
+ private int[] expandKey(
+ byte[] uKey)
+ {
+ int[] key = new int[52];
+
+ if (uKey.length < 16)
+ {
+ byte[] tmp = new byte[16];
+
+ System.arraycopy(uKey, 0, tmp, tmp.length - uKey.length, uKey.length);
+
+ uKey = tmp;
+ }
+
+ for (int i = 0; i < 8; i++)
+ {
+ key[i] = bytesToWord(uKey, i * 2);
+ }
+
+ for (int i = 8; i < 52; i++)
+ {
+ if ((i & 7) < 6)
+ {
+ key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK;
+ }
+ else if ((i & 7) == 6)
+ {
+ key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK;
+ }
+ else
+ {
+ key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK;
+ }
+ }
+
+ return key;
+ }
+
+ /**
+ * This function computes multiplicative inverse using Euclid's Greatest
+ * Common Divisor algorithm. Zero and one are self inverse.
+ * <p>
+ * i.e. x * mulInv(x) == 1 (modulo BASE)
+ */
+ private int mulInv(
+ int x)
+ {
+ int t0, t1, q, y;
+
+ if (x < 2)
+ {
+ return x;
+ }
+
+ t0 = 1;
+ t1 = BASE / x;
+ y = BASE % x;
+
+ while (y != 1)
+ {
+ q = x / y;
+ x = x % y;
+ t0 = (t0 + (t1 * q)) & MASK;
+ if (x == 1)
+ {
+ return t0;
+ }
+ q = y / x;
+ y = y % x;
+ t1 = (t1 + (t0 * q)) & MASK;
+ }
+
+ return (1 - t1) & MASK;
+ }
+
+ /**
+ * Return the additive inverse of x.
+ * <p>
+ * i.e. x + addInv(x) == 0
+ */
+ int addInv(
+ int x)
+ {
+ return (0 - x) & MASK;
+ }
+
+ /**
+ * The function to invert the encryption subkey to the decryption subkey.
+ * It also involves the multiplicative inverse and the additive inverse functions.
+ */
+ private int[] invertKey(
+ int[] inKey)
+ {
+ int t1, t2, t3, t4;
+ int p = 52; /* We work backwards */
+ int[] key = new int[52];
+ int inOff = 0;
+
+ t1 = mulInv(inKey[inOff++]);
+ t2 = addInv(inKey[inOff++]);
+ t3 = addInv(inKey[inOff++]);
+ t4 = mulInv(inKey[inOff++]);
+ key[--p] = t4;
+ key[--p] = t3;
+ key[--p] = t2;
+ key[--p] = t1;
+
+ for (int round = 1; round < 8; round++)
+ {
+ t1 = inKey[inOff++];
+ t2 = inKey[inOff++];
+ key[--p] = t2;
+ key[--p] = t1;
+
+ t1 = mulInv(inKey[inOff++]);
+ t2 = addInv(inKey[inOff++]);
+ t3 = addInv(inKey[inOff++]);
+ t4 = mulInv(inKey[inOff++]);
+ key[--p] = t4;
+ key[--p] = t2; /* NB: Order */
+ key[--p] = t3;
+ key[--p] = t1;
+ }
+
+ t1 = inKey[inOff++];
+ t2 = inKey[inOff++];
+ key[--p] = t2;
+ key[--p] = t1;
+
+ t1 = mulInv(inKey[inOff++]);
+ t2 = addInv(inKey[inOff++]);
+ t3 = addInv(inKey[inOff++]);
+ t4 = mulInv(inKey[inOff]);
+ key[--p] = t4;
+ key[--p] = t3;
+ key[--p] = t2;
+ key[--p] = t1;
+
+ return key;
+ }
+
+ private int[] generateWorkingKey(
+ boolean forEncryption,
+ byte[] userKey)
+ {
+ if (forEncryption)
+ {
+ return expandKey(userKey);
+ }
+ else
+ {
+ return invertKey(expandKey(userKey));
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/IESEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/IESEngine.java
new file mode 100755
index 00000000..1aebdf4d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/IESEngine.java
@@ -0,0 +1,438 @@
+package org.spongycastle.crypto.engines;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.BasicAgreement;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DerivationFunction;
+import org.spongycastle.crypto.EphemeralKeyPair;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.KeyParser;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.IESParameters;
+import org.spongycastle.crypto.params.IESWithCipherParameters;
+import org.spongycastle.crypto.params.KDFParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.BigIntegers;
+import org.spongycastle.util.Pack;
+
+/**
+ * Support class for constructing integrated encryption ciphers
+ * for doing basic message exchanges on top of key agreement ciphers.
+ * Follows the description given in IEEE Std 1363a.
+ */
+public class IESEngine
+{
+ BasicAgreement agree;
+ DerivationFunction kdf;
+ Mac mac;
+ BufferedBlockCipher cipher;
+ byte[] macBuf;
+
+ boolean forEncryption;
+ CipherParameters privParam, pubParam;
+ IESParameters param;
+
+ byte[] V;
+ private EphemeralKeyPairGenerator keyPairGenerator;
+ private KeyParser keyParser;
+ private byte[] IV;
+
+ /**
+ * set up for use with stream mode, where the key derivation function
+ * is used to provide a stream of bytes to xor with the message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ */
+ public IESEngine(
+ BasicAgreement agree,
+ DerivationFunction kdf,
+ Mac mac)
+ {
+ this.agree = agree;
+ this.kdf = kdf;
+ this.mac = mac;
+ this.macBuf = new byte[mac.getMacSize()];
+ this.cipher = null;
+ }
+
+
+ /**
+ * set up for use in conjunction with a block cipher to handle the
+ * message.
+ *
+ * @param agree the key agreement used as the basis for the encryption
+ * @param kdf the key derivation function used for byte generation
+ * @param mac the message authentication code generator for the message
+ * @param cipher the cipher to used for encrypting the message
+ */
+ public IESEngine(
+ BasicAgreement agree,
+ DerivationFunction kdf,
+ Mac mac,
+ BufferedBlockCipher cipher)
+ {
+ this.agree = agree;
+ this.kdf = kdf;
+ this.mac = mac;
+ this.macBuf = new byte[mac.getMacSize()];
+ this.cipher = cipher;
+ }
+
+ /**
+ * Initialise the encryptor.
+ *
+ * @param forEncryption whether or not this is encryption/decryption.
+ * @param privParam our private key parameters
+ * @param pubParam the recipient's/sender's public key parameters
+ * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters privParam,
+ CipherParameters pubParam,
+ CipherParameters params)
+ {
+ this.forEncryption = forEncryption;
+ this.privParam = privParam;
+ this.pubParam = pubParam;
+ this.V = new byte[0];
+
+ extractParams(params);
+ }
+
+
+ /**
+ * Initialise the encryptor.
+ *
+ * @param publicKey the recipient's/sender's public key parameters
+ * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher.
+ * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use.
+ */
+ public void init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator)
+ {
+ this.forEncryption = true;
+ this.pubParam = publicKey;
+ this.keyPairGenerator = ephemeralKeyPairGenerator;
+
+ extractParams(params);
+ }
+
+ /**
+ * Initialise the encryptor.
+ *
+ * @param privateKey the recipient's private key.
+ * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher.
+ * @param publicKeyParser the parser for reading the ephemeral public key.
+ */
+ public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser)
+ {
+ this.forEncryption = false;
+ this.privParam = privateKey;
+ this.keyParser = publicKeyParser;
+
+ extractParams(params);
+ }
+
+ private void extractParams(CipherParameters params)
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ this.IV = ((ParametersWithIV)params).getIV();
+ this.param = (IESParameters)((ParametersWithIV)params).getParameters();
+ }
+ else
+ {
+ this.IV = null;
+ this.param = (IESParameters)params;
+ }
+ }
+
+ public BufferedBlockCipher getCipher()
+ {
+ return cipher;
+ }
+
+ public Mac getMac()
+ {
+ return mac;
+ }
+
+ private byte[] encryptBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] C = null, K = null, K1 = null, K2 = null;
+ int len;
+
+ if (cipher == null)
+ {
+ // Streaming mode.
+ K1 = new byte[inLen];
+ K2 = new byte[param.getMacKeySize() / 8];
+ K = new byte[K1.length + K2.length];
+
+ kdf.generateBytes(K, 0, K.length);
+
+ if (V.length != 0)
+ {
+ System.arraycopy(K, 0, K2, 0, K2.length);
+ System.arraycopy(K, K2.length, K1, 0, K1.length);
+ }
+ else
+ {
+ System.arraycopy(K, 0, K1, 0, K1.length);
+ System.arraycopy(K, inLen, K2, 0, K2.length);
+ }
+
+ C = new byte[inLen];
+
+ for (int i = 0; i != inLen; i++)
+ {
+ C[i] = (byte)(in[inOff + i] ^ K1[i]);
+ }
+ len = inLen;
+ }
+ else
+ {
+ // Block cipher mode.
+ K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8];
+ K2 = new byte[param.getMacKeySize() / 8];
+ K = new byte[K1.length + K2.length];
+
+ kdf.generateBytes(K, 0, K.length);
+ System.arraycopy(K, 0, K1, 0, K1.length);
+ System.arraycopy(K, K1.length, K2, 0, K2.length);
+
+ // If iv provided use it to initialise the cipher
+ if (IV != null)
+ {
+ cipher.init(true, new ParametersWithIV(new KeyParameter(K1), IV));
+ }
+ else
+ {
+ cipher.init(true, new KeyParameter(K1));
+ }
+
+ C = new byte[cipher.getOutputSize(inLen)];
+ len = cipher.processBytes(in, inOff, inLen, C, 0);
+ len += cipher.doFinal(C, len);
+ }
+
+
+ // Convert the length of the encoding vector into a byte array.
+ byte[] P2 = param.getEncodingV();
+ byte[] L2 = new byte[4];
+ if (V.length != 0 && P2 != null)
+ {
+ Pack.intToBigEndian(P2.length * 8, L2, 0);
+ }
+
+
+ // Apply the MAC.
+ byte[] T = new byte[mac.getMacSize()];
+
+ mac.init(new KeyParameter(K2));
+ mac.update(C, 0, C.length);
+ if (P2 != null)
+ {
+ mac.update(P2, 0, P2.length);
+ }
+ if (V.length != 0)
+ {
+ mac.update(L2, 0, L2.length);
+ }
+ mac.doFinal(T, 0);
+
+
+ // Output the triple (V,C,T).
+ byte[] Output = new byte[V.length + len + T.length];
+ System.arraycopy(V, 0, Output, 0, V.length);
+ System.arraycopy(C, 0, Output, V.length, len);
+ System.arraycopy(T, 0, Output, V.length + len, T.length);
+ return Output;
+ }
+
+ private byte[] decryptBlock(
+ byte[] in_enc,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] M = null, K = null, K1 = null, K2 = null;
+ int len;
+
+ // Ensure that the length of the input is greater than the MAC in bytes
+ if (inLen <= (param.getMacKeySize() / 8))
+ {
+ throw new InvalidCipherTextException("Length of input must be greater than the MAC");
+ }
+
+ if (cipher == null)
+ {
+ // Streaming mode.
+ K1 = new byte[inLen - V.length - mac.getMacSize()];
+ K2 = new byte[param.getMacKeySize() / 8];
+ K = new byte[K1.length + K2.length];
+
+ kdf.generateBytes(K, 0, K.length);
+
+ if (V.length != 0)
+ {
+ System.arraycopy(K, 0, K2, 0, K2.length);
+ System.arraycopy(K, K2.length, K1, 0, K1.length);
+ }
+ else
+ {
+ System.arraycopy(K, 0, K1, 0, K1.length);
+ System.arraycopy(K, K1.length, K2, 0, K2.length);
+ }
+
+ M = new byte[K1.length];
+
+ for (int i = 0; i != K1.length; i++)
+ {
+ M[i] = (byte)(in_enc[inOff + V.length + i] ^ K1[i]);
+ }
+
+ len = K1.length;
+ }
+ else
+ {
+ // Block cipher mode.
+ K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8];
+ K2 = new byte[param.getMacKeySize() / 8];
+ K = new byte[K1.length + K2.length];
+
+ kdf.generateBytes(K, 0, K.length);
+ System.arraycopy(K, 0, K1, 0, K1.length);
+ System.arraycopy(K, K1.length, K2, 0, K2.length);
+
+ // If IV provide use it to initialize the cipher
+ if (IV != null)
+ {
+ cipher.init(false, new ParametersWithIV(new KeyParameter(K1), IV));
+ }
+ else
+ {
+ cipher.init(false, new KeyParameter(K1));
+ }
+
+ M = new byte[cipher.getOutputSize(inLen - V.length - mac.getMacSize())];
+ len = cipher.processBytes(in_enc, inOff + V.length, inLen - V.length - mac.getMacSize(), M, 0);
+ len += cipher.doFinal(M, len);
+ }
+
+
+ // Convert the length of the encoding vector into a byte array.
+ byte[] P2 = param.getEncodingV();
+ byte[] L2 = new byte[4];
+ if (V.length != 0 && P2 != null)
+ {
+ Pack.intToBigEndian(P2.length * 8, L2, 0);
+ }
+
+
+ // Verify the MAC.
+ int end = inOff + inLen;
+ byte[] T1 = Arrays.copyOfRange(in_enc, end - mac.getMacSize(), end);
+
+ byte[] T2 = new byte[T1.length];
+ mac.init(new KeyParameter(K2));
+ mac.update(in_enc, inOff + V.length, inLen - V.length - T2.length);
+
+ if (P2 != null)
+ {
+ mac.update(P2, 0, P2.length);
+ }
+ if (V.length != 0)
+ {
+ mac.update(L2, 0, L2.length);
+ }
+ mac.doFinal(T2, 0);
+
+ if (!Arrays.constantTimeAreEqual(T1, T2))
+ {
+ throw new InvalidCipherTextException("Invalid MAC.");
+ }
+
+
+ // Output the message.
+ return Arrays.copyOfRange(M, 0, len);
+ }
+
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ if (keyPairGenerator != null)
+ {
+ EphemeralKeyPair ephKeyPair = keyPairGenerator.generate();
+
+ this.privParam = ephKeyPair.getKeyPair().getPrivate();
+ this.V = ephKeyPair.getEncodedPublicKey();
+ }
+ }
+ else
+ {
+ if (keyParser != null)
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(in, inOff, inLen);
+
+ try
+ {
+ this.pubParam = keyParser.readKey(bIn);
+ }
+ catch (IOException e)
+ {
+ throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e);
+ }
+
+ int encLength = (inLen - bIn.available());
+ this.V = Arrays.copyOfRange(in, inOff, inOff + encLength);
+ }
+ }
+
+ // Compute the common value and convert to byte array.
+ agree.init(privParam);
+ BigInteger z = agree.calculateAgreement(pubParam);
+ byte[] Z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), z);
+
+ // Create input to KDF.
+ byte[] VZ;
+ if (V.length != 0)
+ {
+ VZ = new byte[V.length + Z.length];
+ System.arraycopy(V, 0, VZ, 0, V.length);
+ System.arraycopy(Z, 0, VZ, V.length, Z.length);
+ }
+ else
+ {
+ VZ = Z;
+ }
+
+ // Initialise the KDF.
+ KDFParameters kdfParam = new KDFParameters(VZ, param.getDerivationV());
+ kdf.init(kdfParam);
+
+ return forEncryption
+ ? encryptBlock(in, inOff, inLen)
+ : decryptBlock(in, inOff, inLen);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/ISAACEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/ISAACEngine.java
new file mode 100644
index 00000000..36e762da
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/ISAACEngine.java
@@ -0,0 +1,221 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Pack;
+
+/**
+ * Implementation of Bob Jenkin's ISAAC (Indirection Shift Accumulate Add and Count).
+ * see: http://www.burtleburtle.net/bob/rand/isaacafa.html
+*/
+public class ISAACEngine
+ implements StreamCipher
+{
+ // Constants
+ private final int sizeL = 8,
+ stateArraySize = sizeL<<5; // 256
+
+ // Cipher's internal state
+ private int[] engineState = null, // mm
+ results = null; // randrsl
+ private int a = 0, b = 0, c = 0;
+
+ // Engine state
+ private int index = 0;
+ private byte[] keyStream = new byte[stateArraySize<<2], // results expanded into bytes
+ workingKey = null;
+ private boolean initialised = false;
+
+ /**
+ * initialise an ISAAC cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to ISAAC init - " + params.getClass().getName());
+ }
+ /*
+ * ISAAC encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant.
+ */
+ KeyParameter p = (KeyParameter)params;
+ setKey(p.getKey());
+
+ return;
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (index == 0)
+ {
+ isaac();
+ keyStream = Pack.intToBigEndian(results);
+ }
+ byte out = (byte)(keyStream[index]^in);
+ index = (index + 1) & 1023;
+
+ return out;
+ }
+
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ if (index == 0)
+ {
+ isaac();
+ keyStream = Pack.intToBigEndian(results);
+ }
+ out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
+ index = (index + 1) & 1023;
+ }
+
+ return len;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "ISAAC";
+ }
+
+ public void reset()
+ {
+ setKey(workingKey);
+ }
+
+ // Private implementation
+ private void setKey(byte[] keyBytes)
+ {
+ workingKey = keyBytes;
+
+ if (engineState == null)
+ {
+ engineState = new int[stateArraySize];
+ }
+
+ if (results == null)
+ {
+ results = new int[stateArraySize];
+ }
+
+ int i, j, k;
+
+ // Reset state
+ for (i = 0; i < stateArraySize; i++)
+ {
+ engineState[i] = results[i] = 0;
+ }
+ a = b = c = 0;
+
+ // Reset index counter for output
+ index = 0;
+
+ // Convert the key bytes to ints and put them into results[] for initialization
+ byte[] t = new byte[keyBytes.length + (keyBytes.length & 3)];
+ System.arraycopy(keyBytes, 0, t, 0, keyBytes.length);
+ for (i = 0; i < t.length; i+=4)
+ {
+ results[i >>> 2] = Pack.littleEndianToInt(t, i);
+ }
+
+ // It has begun?
+ int[] abcdefgh = new int[sizeL];
+
+ for (i = 0; i < sizeL; i++)
+ {
+ abcdefgh[i] = 0x9e3779b9; // Phi (golden ratio)
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ mix(abcdefgh);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < stateArraySize; j+=sizeL)
+ {
+ for (k = 0; k < sizeL; k++)
+ {
+ abcdefgh[k] += (i<1) ? results[j+k] : engineState[j+k];
+ }
+
+ mix(abcdefgh);
+
+ for (k = 0; k < sizeL; k++)
+ {
+ engineState[j+k] = abcdefgh[k];
+ }
+ }
+ }
+
+ isaac();
+
+ initialised = true;
+ }
+
+ private void isaac()
+ {
+ int i, x, y;
+
+ b += ++c;
+ for (i = 0; i < stateArraySize; i++)
+ {
+ x = engineState[i];
+ switch (i & 3)
+ {
+ case 0: a ^= (a << 13); break;
+ case 1: a ^= (a >>> 6); break;
+ case 2: a ^= (a << 2); break;
+ case 3: a ^= (a >>> 16); break;
+ }
+ a += engineState[(i+128) & 0xFF];
+ engineState[i] = y = engineState[(x >>> 2) & 0xFF] + a + b;
+ results[i] = b = engineState[(y >>> 10) & 0xFF] + x;
+ }
+ }
+
+ private void mix(int[] x)
+ {
+ x[0]^=x[1]<< 11; x[3]+=x[0]; x[1]+=x[2];
+ x[1]^=x[2]>>> 2; x[4]+=x[1]; x[2]+=x[3];
+ x[2]^=x[3]<< 8; x[5]+=x[2]; x[3]+=x[4];
+ x[3]^=x[4]>>>16; x[6]+=x[3]; x[4]+=x[5];
+ x[4]^=x[5]<< 10; x[7]+=x[4]; x[5]+=x[6];
+ x[5]^=x[6]>>> 4; x[0]+=x[5]; x[6]+=x[7];
+ x[6]^=x[7]<< 8; x[1]+=x[6]; x[7]+=x[0];
+ x[7]^=x[0]>>> 9; x[2]+=x[7]; x[0]+=x[1];
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/NaccacheSternEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/NaccacheSternEngine.java
new file mode 100644
index 00000000..a25b12f1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/NaccacheSternEngine.java
@@ -0,0 +1,437 @@
+package org.spongycastle.crypto.engines;
+
+import java.math.BigInteger;
+import java.util.Vector;
+import org.spongycastle.util.Arrays;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.NaccacheSternKeyParameters;
+import org.spongycastle.crypto.params.NaccacheSternPrivateKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * NaccacheStern Engine. For details on this cipher, please see
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ */
+public class NaccacheSternEngine
+ implements AsymmetricBlockCipher
+{
+ private boolean forEncryption;
+
+ private NaccacheSternKeyParameters key;
+
+ private Vector[] lookup = null;
+
+ private boolean debug = false;
+
+ private static BigInteger ZERO = BigInteger.valueOf(0);
+ private static BigInteger ONE = BigInteger.valueOf(1);
+
+ /**
+ * Initializes this algorithm. Must be called before all other Functions.
+ *
+ * @see org.spongycastle.crypto.AsymmetricBlockCipher#init(boolean,
+ * org.spongycastle.crypto.CipherParameters)
+ */
+ public void init(boolean forEncryption, CipherParameters param)
+ {
+ this.forEncryption = forEncryption;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ param = ((ParametersWithRandom) param).getParameters();
+ }
+
+ key = (NaccacheSternKeyParameters)param;
+
+ // construct lookup table for faster decryption if necessary
+ if (!this.forEncryption)
+ {
+ if (debug)
+ {
+ System.out.println("Constructing lookup Array");
+ }
+ NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key;
+ Vector primes = priv.getSmallPrimes();
+ lookup = new Vector[primes.size()];
+ for (int i = 0; i < primes.size(); i++)
+ {
+ BigInteger actualPrime = (BigInteger)primes.elementAt(i);
+ int actualPrimeValue = actualPrime.intValue();
+
+ lookup[i] = new Vector();
+ lookup[i].addElement(ONE);
+
+ if (debug)
+ {
+ System.out.println("Constructing lookup ArrayList for " + actualPrimeValue);
+ }
+
+ BigInteger accJ = ZERO;
+
+ for (int j = 1; j < actualPrimeValue; j++)
+ {
+ accJ = accJ.add(priv.getPhi_n());
+ BigInteger comp = accJ.divide(actualPrime);
+ lookup[i].addElement(priv.getG().modPow(comp, priv.getModulus()));
+ }
+ }
+ }
+ }
+
+ public void setDebug(boolean debug)
+ {
+ this.debug = debug;
+ }
+
+ /**
+ * Returns the input block size of this algorithm.
+ *
+ * @see org.spongycastle.crypto.AsymmetricBlockCipher#getInputBlockSize()
+ */
+ public int getInputBlockSize()
+ {
+ if (forEncryption)
+ {
+ // We can only encrypt values up to lowerSigmaBound
+ return (key.getLowerSigmaBound() + 7) / 8 - 1;
+ }
+ else
+ {
+ // We pad to modulus-size bytes for easier decryption.
+ return key.getModulus().toByteArray().length;
+ }
+ }
+
+ /**
+ * Returns the output block size of this algorithm.
+ *
+ * @see org.spongycastle.crypto.AsymmetricBlockCipher#getOutputBlockSize()
+ */
+ public int getOutputBlockSize()
+ {
+ if (forEncryption)
+ {
+ // encrypted Data is always padded up to modulus size
+ return key.getModulus().toByteArray().length;
+ }
+ else
+ {
+ // decrypted Data has upper limit lowerSigmaBound
+ return (key.getLowerSigmaBound() + 7) / 8 - 1;
+ }
+ }
+
+ /**
+ * Process a single Block using the Naccache-Stern algorithm.
+ *
+ * @see org.spongycastle.crypto.AsymmetricBlockCipher#processBlock(byte[],
+ * int, int)
+ */
+ public byte[] processBlock(byte[] in, int inOff, int len) throws InvalidCipherTextException
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("NaccacheStern engine not initialised");
+ }
+ if (len > (getInputBlockSize() + 1))
+ {
+ throw new DataLengthException("input too large for Naccache-Stern cipher.\n");
+ }
+
+ if (!forEncryption)
+ {
+ // At decryption make sure that we receive padded data blocks
+ if (len < getInputBlockSize())
+ {
+ throw new InvalidCipherTextException("BlockLength does not match modulus for Naccache-Stern cipher.\n");
+ }
+ }
+
+ byte[] block;
+
+ if (inOff != 0 || len != in.length)
+ {
+ block = new byte[len];
+ System.arraycopy(in, inOff, block, 0, len);
+ }
+ else
+ {
+ block = in;
+ }
+
+ // transform input into BigInteger
+ BigInteger input = new BigInteger(1, block);
+ if (debug)
+ {
+ System.out.println("input as BigInteger: " + input);
+ }
+ byte[] output;
+ if (forEncryption)
+ {
+ output = encrypt(input);
+ }
+ else
+ {
+ Vector plain = new Vector();
+ NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key;
+ Vector primes = priv.getSmallPrimes();
+ // Get Chinese Remainders of CipherText
+ for (int i = 0; i < primes.size(); i++)
+ {
+ BigInteger exp = input.modPow(priv.getPhi_n().divide((BigInteger)primes.elementAt(i)), priv.getModulus());
+ Vector al = lookup[i];
+ if (lookup[i].size() != ((BigInteger)primes.elementAt(i)).intValue())
+ {
+ if (debug)
+ {
+ System.out.println("Prime is " + primes.elementAt(i) + ", lookup table has size " + al.size());
+ }
+ throw new InvalidCipherTextException("Error in lookup Array for "
+ + ((BigInteger)primes.elementAt(i)).intValue()
+ + ": Size mismatch. Expected ArrayList with length "
+ + ((BigInteger)primes.elementAt(i)).intValue() + " but found ArrayList of length "
+ + lookup[i].size());
+ }
+ int lookedup = al.indexOf(exp);
+
+ if (lookedup == -1)
+ {
+ if (debug)
+ {
+ System.out.println("Actual prime is " + primes.elementAt(i));
+ System.out.println("Decrypted value is " + exp);
+
+ System.out.println("LookupList for " + primes.elementAt(i) + " with size " + lookup[i].size()
+ + " is: ");
+ for (int j = 0; j < lookup[i].size(); j++)
+ {
+ System.out.println(lookup[i].elementAt(j));
+ }
+ }
+ throw new InvalidCipherTextException("Lookup failed");
+ }
+ plain.addElement(BigInteger.valueOf(lookedup));
+ }
+ BigInteger test = chineseRemainder(plain, primes);
+
+ // Should not be used as an oracle, so reencrypt output to see
+ // if it corresponds to input
+
+ // this breaks probabilisic encryption, so disable it. Anyway, we do
+ // use the first n primes for key generation, so it is pretty easy
+ // to guess them. But as stated in the paper, this is not a security
+ // breach. So we can just work with the correct sigma.
+
+ // if (debug) {
+ // System.out.println("Decryption is " + test);
+ // }
+ // if ((key.getG().modPow(test, key.getModulus())).equals(input)) {
+ // output = test.toByteArray();
+ // } else {
+ // if(debug){
+ // System.out.println("Engine seems to be used as an oracle,
+ // returning null");
+ // }
+ // output = null;
+ // }
+
+ output = test.toByteArray();
+
+ }
+
+ return output;
+ }
+
+ /**
+ * Encrypts a BigInteger aka Plaintext with the public key.
+ *
+ * @param plain
+ * The BigInteger to encrypt
+ * @return The byte[] representation of the encrypted BigInteger (i.e.
+ * crypted.toByteArray())
+ */
+ public byte[] encrypt(BigInteger plain)
+ {
+ // Always return modulus size values 0-padded at the beginning
+ // 0-padding at the beginning is correctly parsed by BigInteger :)
+ byte[] output = key.getModulus().toByteArray();
+ Arrays.fill(output, (byte)0);
+ byte[] tmp = key.getG().modPow(plain, key.getModulus()).toByteArray();
+ System
+ .arraycopy(tmp, 0, output, output.length - tmp.length,
+ tmp.length);
+ if (debug)
+ {
+ System.out
+ .println("Encrypted value is: " + new BigInteger(output));
+ }
+ return output;
+ }
+
+ /**
+ * Adds the contents of two encrypted blocks mod sigma
+ *
+ * @param block1
+ * the first encrypted block
+ * @param block2
+ * the second encrypted block
+ * @return encrypt((block1 + block2) mod sigma)
+ * @throws InvalidCipherTextException
+ */
+ public byte[] addCryptedBlocks(byte[] block1, byte[] block2)
+ throws InvalidCipherTextException
+ {
+ // check for correct blocksize
+ if (forEncryption)
+ {
+ if ((block1.length > getOutputBlockSize())
+ || (block2.length > getOutputBlockSize()))
+ {
+ throw new InvalidCipherTextException(
+ "BlockLength too large for simple addition.\n");
+ }
+ }
+ else
+ {
+ if ((block1.length > getInputBlockSize())
+ || (block2.length > getInputBlockSize()))
+ {
+ throw new InvalidCipherTextException(
+ "BlockLength too large for simple addition.\n");
+ }
+ }
+
+ // calculate resulting block
+ BigInteger m1Crypt = new BigInteger(1, block1);
+ BigInteger m2Crypt = new BigInteger(1, block2);
+ BigInteger m1m2Crypt = m1Crypt.multiply(m2Crypt);
+ m1m2Crypt = m1m2Crypt.mod(key.getModulus());
+ if (debug)
+ {
+ System.out.println("c(m1) as BigInteger:....... " + m1Crypt);
+ System.out.println("c(m2) as BigInteger:....... " + m2Crypt);
+ System.out.println("c(m1)*c(m2)%n = c(m1+m2)%n: " + m1m2Crypt);
+ }
+
+ byte[] output = key.getModulus().toByteArray();
+ Arrays.fill(output, (byte)0);
+ System.arraycopy(m1m2Crypt.toByteArray(), 0, output, output.length
+ - m1m2Crypt.toByteArray().length,
+ m1m2Crypt.toByteArray().length);
+
+ return output;
+ }
+
+ /**
+ * Convenience Method for data exchange with the cipher.
+ *
+ * Determines blocksize and splits data to blocksize.
+ *
+ * @param data the data to be processed
+ * @return the data after it went through the NaccacheSternEngine.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] processData(byte[] data) throws InvalidCipherTextException
+ {
+ if (debug)
+ {
+ System.out.println();
+ }
+ if (data.length > getInputBlockSize())
+ {
+ int inBlocksize = getInputBlockSize();
+ int outBlocksize = getOutputBlockSize();
+ if (debug)
+ {
+ System.out.println("Input blocksize is: " + inBlocksize + " bytes");
+ System.out.println("Output blocksize is: " + outBlocksize + " bytes");
+ System.out.println("Data has length:.... " + data.length + " bytes");
+ }
+ int datapos = 0;
+ int retpos = 0;
+ byte[] retval = new byte[(data.length / inBlocksize + 1) * outBlocksize];
+ while (datapos < data.length)
+ {
+ byte[] tmp;
+ if (datapos + inBlocksize < data.length)
+ {
+ tmp = processBlock(data, datapos, inBlocksize);
+ datapos += inBlocksize;
+ }
+ else
+ {
+ tmp = processBlock(data, datapos, data.length - datapos);
+ datapos += data.length - datapos;
+ }
+ if (debug)
+ {
+ System.out.println("new datapos is " + datapos);
+ }
+ if (tmp != null)
+ {
+ System.arraycopy(tmp, 0, retval, retpos, tmp.length);
+
+ retpos += tmp.length;
+ }
+ else
+ {
+ if (debug)
+ {
+ System.out.println("cipher returned null");
+ }
+ throw new InvalidCipherTextException("cipher returned null");
+ }
+ }
+ byte[] ret = new byte[retpos];
+ System.arraycopy(retval, 0, ret, 0, retpos);
+ if (debug)
+ {
+ System.out.println("returning " + ret.length + " bytes");
+ }
+ return ret;
+ }
+ else
+ {
+ if (debug)
+ {
+ System.out.println("data size is less then input block size, processing directly");
+ }
+ return processBlock(data, 0, data.length);
+ }
+ }
+
+ /**
+ * Computes the integer x that is expressed through the given primes and the
+ * congruences with the chinese remainder theorem (CRT).
+ *
+ * @param congruences
+ * the congruences c_i
+ * @param primes
+ * the primes p_i
+ * @return an integer x for that x % p_i == c_i
+ */
+ private static BigInteger chineseRemainder(Vector congruences, Vector primes)
+ {
+ BigInteger retval = ZERO;
+ BigInteger all = ONE;
+ for (int i = 0; i < primes.size(); i++)
+ {
+ all = all.multiply((BigInteger)primes.elementAt(i));
+ }
+ for (int i = 0; i < primes.size(); i++)
+ {
+ BigInteger a = (BigInteger)primes.elementAt(i);
+ BigInteger b = all.divide(a);
+ BigInteger b_ = b.modInverse(a);
+ BigInteger tmp = b.multiply(b_);
+ tmp = tmp.multiply((BigInteger)congruences.elementAt(i));
+ retval = retval.add(tmp);
+ }
+
+ return retval.mod(all);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/NoekeonEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/NoekeonEngine.java
new file mode 100644
index 00000000..3abaeede
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/NoekeonEngine.java
@@ -0,0 +1,263 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * A Noekeon engine, using direct-key mode.
+ */
+
+public class NoekeonEngine
+ implements BlockCipher
+{
+ private static final int genericSize = 16; // Block and key size, as well as the amount of rounds.
+
+ private static final int[] nullVector =
+ {
+ 0x00, 0x00, 0x00, 0x00 // Used in decryption
+ },
+
+ roundConstants =
+ {
+ 0x80, 0x1b, 0x36, 0x6c,
+ 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63,
+ 0xc6, 0x97, 0x35, 0x6a,
+ 0xd4
+ };
+
+ private int[] state = new int[4], // a
+ subKeys = new int[4], // k
+ decryptKeys = new int[4];
+
+ private boolean _initialised,
+ _forEncryption;
+
+ /**
+ * Create an instance of the Noekeon encryption algorithm
+ * and set some defaults
+ */
+ public NoekeonEngine()
+ {
+ _initialised = false;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Noekeon";
+ }
+
+ public int getBlockSize()
+ {
+ return genericSize;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to Noekeon init - " + params.getClass().getName());
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (!_initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + genericSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + genericSize) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (_forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ subKeys[0] = bytesToIntBig(key, 0);
+ subKeys[1] = bytesToIntBig(key, 4);
+ subKeys[2] = bytesToIntBig(key, 8);
+ subKeys[3] = bytesToIntBig(key, 12);
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ state[0] = bytesToIntBig(in, inOff);
+ state[1] = bytesToIntBig(in, inOff+4);
+ state[2] = bytesToIntBig(in, inOff+8);
+ state[3] = bytesToIntBig(in, inOff+12);
+
+ int i;
+ for (i = 0; i < genericSize; i++)
+ {
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ state[0] ^= roundConstants[i];
+ theta(state, subKeys);
+
+ intToBytesBig(state[0], out, outOff);
+ intToBytesBig(state[1], out, outOff+4);
+ intToBytesBig(state[2], out, outOff+8);
+ intToBytesBig(state[3], out, outOff+12);
+
+ return genericSize;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ state[0] = bytesToIntBig(in, inOff);
+ state[1] = bytesToIntBig(in, inOff+4);
+ state[2] = bytesToIntBig(in, inOff+8);
+ state[3] = bytesToIntBig(in, inOff+12);
+
+ System.arraycopy(subKeys, 0, decryptKeys, 0, subKeys.length);
+ theta(decryptKeys, nullVector);
+
+ int i;
+ for (i = genericSize; i > 0; i--)
+ {
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+ pi1(state);
+ gamma(state);
+ pi2(state);
+ }
+
+ theta(state, decryptKeys);
+ state[0] ^= roundConstants[i];
+
+ intToBytesBig(state[0], out, outOff);
+ intToBytesBig(state[1], out, outOff+4);
+ intToBytesBig(state[2], out, outOff+8);
+ intToBytesBig(state[3], out, outOff+12);
+
+ return genericSize;
+ }
+
+ private void gamma(int[] a)
+ {
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+
+ int tmp = a[3];
+ a[3] = a[0];
+ a[0] = tmp;
+ a[2] ^= a[0]^a[1]^a[3];
+
+ a[1] ^= ~a[3] & ~a[2];
+ a[0] ^= a[2] & a[1];
+ }
+
+ private void theta(int[] a, int[] k)
+ {
+ int tmp;
+
+ tmp = a[0]^a[2];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[1] ^= tmp;
+ a[3] ^= tmp;
+
+ for (int i = 0; i < 4; i++)
+ {
+ a[i] ^= k[i];
+ }
+
+ tmp = a[1]^a[3];
+ tmp ^= rotl(tmp,8)^rotl(tmp,24);
+ a[0] ^= tmp;
+ a[2] ^= tmp;
+ }
+
+ private void pi1(int[] a)
+ {
+ a[1] = rotl(a[1], 1);
+ a[2] = rotl(a[2], 5);
+ a[3] = rotl(a[3], 2);
+ }
+
+ private void pi2(int[] a)
+ {
+ a[1] = rotl(a[1], 31);
+ a[2] = rotl(a[2], 27);
+ a[3] = rotl(a[3], 30);
+ }
+
+ // Helpers
+
+ private int bytesToIntBig(byte[] in, int off)
+ {
+ return ((in[off++]) << 24) |
+ ((in[off++] & 0xff) << 16) |
+ ((in[off++] & 0xff) << 8) |
+ (in[off ] & 0xff);
+ }
+
+ private void intToBytesBig(int x, byte[] out, int off)
+ {
+ out[off++] = (byte)(x >>> 24);
+ out[off++] = (byte)(x >>> 16);
+ out[off++] = (byte)(x >>> 8);
+ out[off ] = (byte)x;
+ }
+
+ private int rotl(int x, int y)
+ {
+ return (x << y) | (x >>> (32-y));
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/NullEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/NullEngine.java
new file mode 100644
index 00000000..f8d28413
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/NullEngine.java
@@ -0,0 +1,96 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+
+/**
+ * The no-op engine that just copies bytes through, irrespective of whether encrypting and decrypting.
+ * Provided for the sake of completeness.
+ */
+public class NullEngine implements BlockCipher
+{
+ private boolean initialised;
+ protected static final int DEFAULT_BLOCK_SIZE = 1;
+ private final int blockSize;
+
+ /**
+ * Constructs a null engine with a block size of 1 byte.
+ */
+ public NullEngine()
+ {
+ this(DEFAULT_BLOCK_SIZE);
+ }
+
+ /**
+ * Constructs a null engine with a specific block size.
+ *
+ * @param blockSize the block size in bytes.
+ */
+ public NullEngine(int blockSize)
+ {
+ this.blockSize = blockSize;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#init(boolean, org.spongycastle.crypto.CipherParameters)
+ */
+ public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
+ {
+ // we don't mind any parameters that may come in
+ this.initialised = true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#getAlgorithmName()
+ */
+ public String getAlgorithmName()
+ {
+ return "Null";
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#getBlockSize()
+ */
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#processBlock(byte[], int, byte[], int)
+ */
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException("Null engine not initialised");
+ }
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < blockSize; ++i)
+ {
+ out[outOff + i] = in[inOff + i];
+ }
+
+ return blockSize;
+ }
+
+ /* (non-Javadoc)
+ * @see org.spongycastle.crypto.BlockCipher#reset()
+ */
+ public void reset()
+ {
+ // nothing needs to be done
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RC2Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/RC2Engine.java
new file mode 100644
index 00000000..24bdab6a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RC2Engine.java
@@ -0,0 +1,317 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.RC2Parameters;
+
+/**
+ * an implementation of RC2 as described in RFC 2268
+ * "A Description of the RC2(r) Encryption Algorithm" R. Rivest.
+ */
+public class RC2Engine
+ implements BlockCipher
+{
+ //
+ // the values we use for key expansion (based on the digits of PI)
+ //
+ private static byte[] piTable =
+ {
+ (byte)0xd9, (byte)0x78, (byte)0xf9, (byte)0xc4, (byte)0x19, (byte)0xdd, (byte)0xb5, (byte)0xed,
+ (byte)0x28, (byte)0xe9, (byte)0xfd, (byte)0x79, (byte)0x4a, (byte)0xa0, (byte)0xd8, (byte)0x9d,
+ (byte)0xc6, (byte)0x7e, (byte)0x37, (byte)0x83, (byte)0x2b, (byte)0x76, (byte)0x53, (byte)0x8e,
+ (byte)0x62, (byte)0x4c, (byte)0x64, (byte)0x88, (byte)0x44, (byte)0x8b, (byte)0xfb, (byte)0xa2,
+ (byte)0x17, (byte)0x9a, (byte)0x59, (byte)0xf5, (byte)0x87, (byte)0xb3, (byte)0x4f, (byte)0x13,
+ (byte)0x61, (byte)0x45, (byte)0x6d, (byte)0x8d, (byte)0x9, (byte)0x81, (byte)0x7d, (byte)0x32,
+ (byte)0xbd, (byte)0x8f, (byte)0x40, (byte)0xeb, (byte)0x86, (byte)0xb7, (byte)0x7b, (byte)0xb,
+ (byte)0xf0, (byte)0x95, (byte)0x21, (byte)0x22, (byte)0x5c, (byte)0x6b, (byte)0x4e, (byte)0x82,
+ (byte)0x54, (byte)0xd6, (byte)0x65, (byte)0x93, (byte)0xce, (byte)0x60, (byte)0xb2, (byte)0x1c,
+ (byte)0x73, (byte)0x56, (byte)0xc0, (byte)0x14, (byte)0xa7, (byte)0x8c, (byte)0xf1, (byte)0xdc,
+ (byte)0x12, (byte)0x75, (byte)0xca, (byte)0x1f, (byte)0x3b, (byte)0xbe, (byte)0xe4, (byte)0xd1,
+ (byte)0x42, (byte)0x3d, (byte)0xd4, (byte)0x30, (byte)0xa3, (byte)0x3c, (byte)0xb6, (byte)0x26,
+ (byte)0x6f, (byte)0xbf, (byte)0xe, (byte)0xda, (byte)0x46, (byte)0x69, (byte)0x7, (byte)0x57,
+ (byte)0x27, (byte)0xf2, (byte)0x1d, (byte)0x9b, (byte)0xbc, (byte)0x94, (byte)0x43, (byte)0x3,
+ (byte)0xf8, (byte)0x11, (byte)0xc7, (byte)0xf6, (byte)0x90, (byte)0xef, (byte)0x3e, (byte)0xe7,
+ (byte)0x6, (byte)0xc3, (byte)0xd5, (byte)0x2f, (byte)0xc8, (byte)0x66, (byte)0x1e, (byte)0xd7,
+ (byte)0x8, (byte)0xe8, (byte)0xea, (byte)0xde, (byte)0x80, (byte)0x52, (byte)0xee, (byte)0xf7,
+ (byte)0x84, (byte)0xaa, (byte)0x72, (byte)0xac, (byte)0x35, (byte)0x4d, (byte)0x6a, (byte)0x2a,
+ (byte)0x96, (byte)0x1a, (byte)0xd2, (byte)0x71, (byte)0x5a, (byte)0x15, (byte)0x49, (byte)0x74,
+ (byte)0x4b, (byte)0x9f, (byte)0xd0, (byte)0x5e, (byte)0x4, (byte)0x18, (byte)0xa4, (byte)0xec,
+ (byte)0xc2, (byte)0xe0, (byte)0x41, (byte)0x6e, (byte)0xf, (byte)0x51, (byte)0xcb, (byte)0xcc,
+ (byte)0x24, (byte)0x91, (byte)0xaf, (byte)0x50, (byte)0xa1, (byte)0xf4, (byte)0x70, (byte)0x39,
+ (byte)0x99, (byte)0x7c, (byte)0x3a, (byte)0x85, (byte)0x23, (byte)0xb8, (byte)0xb4, (byte)0x7a,
+ (byte)0xfc, (byte)0x2, (byte)0x36, (byte)0x5b, (byte)0x25, (byte)0x55, (byte)0x97, (byte)0x31,
+ (byte)0x2d, (byte)0x5d, (byte)0xfa, (byte)0x98, (byte)0xe3, (byte)0x8a, (byte)0x92, (byte)0xae,
+ (byte)0x5, (byte)0xdf, (byte)0x29, (byte)0x10, (byte)0x67, (byte)0x6c, (byte)0xba, (byte)0xc9,
+ (byte)0xd3, (byte)0x0, (byte)0xe6, (byte)0xcf, (byte)0xe1, (byte)0x9e, (byte)0xa8, (byte)0x2c,
+ (byte)0x63, (byte)0x16, (byte)0x1, (byte)0x3f, (byte)0x58, (byte)0xe2, (byte)0x89, (byte)0xa9,
+ (byte)0xd, (byte)0x38, (byte)0x34, (byte)0x1b, (byte)0xab, (byte)0x33, (byte)0xff, (byte)0xb0,
+ (byte)0xbb, (byte)0x48, (byte)0xc, (byte)0x5f, (byte)0xb9, (byte)0xb1, (byte)0xcd, (byte)0x2e,
+ (byte)0xc5, (byte)0xf3, (byte)0xdb, (byte)0x47, (byte)0xe5, (byte)0xa5, (byte)0x9c, (byte)0x77,
+ (byte)0xa, (byte)0xa6, (byte)0x20, (byte)0x68, (byte)0xfe, (byte)0x7f, (byte)0xc1, (byte)0xad
+ };
+
+ private static final int BLOCK_SIZE = 8;
+
+ private int[] workingKey;
+ private boolean encrypting;
+
+ private int[] generateWorkingKey(
+ byte[] key,
+ int bits)
+ {
+ int x;
+ int[] xKey = new int[128];
+
+ for (int i = 0; i != key.length; i++)
+ {
+ xKey[i] = key[i] & 0xff;
+ }
+
+ // Phase 1: Expand input key to 128 bytes
+ int len = key.length;
+
+ if (len < 128)
+ {
+ int index = 0;
+
+ x = xKey[len - 1];
+
+ do
+ {
+ x = piTable[(x + xKey[index++]) & 255] & 0xff;
+ xKey[len++] = x;
+ }
+ while (len < 128);
+ }
+
+ // Phase 2 - reduce effective key size to "bits"
+ len = (bits + 7) >> 3;
+ x = piTable[xKey[128 - len] & (255 >> (7 & -bits))] & 0xff;
+ xKey[128 - len] = x;
+
+ for (int i = 128 - len - 1; i >= 0; i--)
+ {
+ x = piTable[x ^ xKey[i + len]] & 0xff;
+ xKey[i] = x;
+ }
+
+ // Phase 3 - copy to newKey in little-endian order
+ int[] newKey = new int[64];
+
+ for (int i = 0; i != newKey.length; i++)
+ {
+ newKey[i] = (xKey[2 * i] + (xKey[2 * i + 1] << 8));
+ }
+
+ return newKey;
+ }
+
+ /**
+ * initialise a RC2 cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ this.encrypting = encrypting;
+
+ if (params instanceof RC2Parameters)
+ {
+ RC2Parameters param = (RC2Parameters)params;
+
+ workingKey = generateWorkingKey(param.getKey(),
+ param.getEffectiveKeyBits());
+ }
+ else if (params instanceof KeyParameter)
+ {
+ byte[] key = ((KeyParameter)params).getKey();
+
+ workingKey = generateWorkingKey(key, key.length * 8);
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC2 init - " + params.getClass().getName());
+ }
+
+ }
+
+ public void reset()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC2";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("RC2 engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * return the result rotating the 16 bit number in x left by y
+ */
+ private int rotateWordLeft(
+ int x,
+ int y)
+ {
+ x &= 0xffff;
+ return (x << y) | (x >> (16 - y));
+ }
+
+ private void encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((in[inOff + 7] & 0xff) << 8) + (in[inOff + 6] & 0xff);
+ x54 = ((in[inOff + 5] & 0xff) << 8) + (in[inOff + 4] & 0xff);
+ x32 = ((in[inOff + 3] & 0xff) << 8) + (in[inOff + 2] & 0xff);
+ x10 = ((in[inOff + 1] & 0xff) << 8) + (in[inOff + 0] & 0xff);
+
+ for (int i = 0; i <= 16; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 20; i <= 40; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ x10 += workingKey[x76 & 63];
+ x32 += workingKey[x10 & 63];
+ x54 += workingKey[x32 & 63];
+ x76 += workingKey[x54 & 63];
+
+ for (int i = 44; i < 64; i += 4)
+ {
+ x10 = rotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1);
+ x32 = rotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+ x54 = rotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+ x76 = rotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+ }
+
+ out[outOff + 0] = (byte)x10;
+ out[outOff + 1] = (byte)(x10 >> 8);
+ out[outOff + 2] = (byte)x32;
+ out[outOff + 3] = (byte)(x32 >> 8);
+ out[outOff + 4] = (byte)x54;
+ out[outOff + 5] = (byte)(x54 >> 8);
+ out[outOff + 6] = (byte)x76;
+ out[outOff + 7] = (byte)(x76 >> 8);
+ }
+
+ private void decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int x76, x54, x32, x10;
+
+ x76 = ((in[inOff + 7] & 0xff) << 8) + (in[inOff + 6] & 0xff);
+ x54 = ((in[inOff + 5] & 0xff) << 8) + (in[inOff + 4] & 0xff);
+ x32 = ((in[inOff + 3] & 0xff) << 8) + (in[inOff + 2] & 0xff);
+ x10 = ((in[inOff + 1] & 0xff) << 8) + (in[inOff + 0] & 0xff);
+
+ for (int i = 60; i >= 44; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 40; i >= 20; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ x76 -= workingKey[x54 & 63];
+ x54 -= workingKey[x32 & 63];
+ x32 -= workingKey[x10 & 63];
+ x10 -= workingKey[x76 & 63];
+
+ for (int i = 16; i >= 0; i -= 4)
+ {
+ x76 = rotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]);
+ x54 = rotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]);
+ x32 = rotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]);
+ x10 = rotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]);
+ }
+
+ out[outOff + 0] = (byte)x10;
+ out[outOff + 1] = (byte)(x10 >> 8);
+ out[outOff + 2] = (byte)x32;
+ out[outOff + 3] = (byte)(x32 >> 8);
+ out[outOff + 4] = (byte)x54;
+ out[outOff + 5] = (byte)(x54 >> 8);
+ out[outOff + 6] = (byte)x76;
+ out[outOff + 7] = (byte)(x76 >> 8);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RC2WrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RC2WrapEngine.java
new file mode 100644
index 00000000..4203954e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RC2WrapEngine.java
@@ -0,0 +1,385 @@
+package org.spongycastle.crypto.engines;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Wrap keys according to RFC 3217 - RC2 mechanism
+ */
+public class RC2WrapEngine
+ implements Wrapper
+{
+ /** Field engine */
+ private CBCBlockCipher engine;
+
+ /** Field param */
+ private CipherParameters param;
+
+ /** Field paramPlusIV */
+ private ParametersWithIV paramPlusIV;
+
+ /** Field iv */
+ private byte[] iv;
+
+ /** Field forWrapping */
+ private boolean forWrapping;
+
+ private SecureRandom sr;
+
+ /** Field IV2 */
+ private static final byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
+ (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
+ (byte) 0x21, (byte) 0x05 };
+
+ //
+ // checksum digest
+ //
+ Digest sha1 = new SHA1Digest();
+ byte[] digest = new byte[20];
+
+ /**
+ * Method init
+ *
+ * @param forWrapping
+ * @param param
+ */
+ public void init(boolean forWrapping, CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+ this.engine = new CBCBlockCipher(new RC2Engine());
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom pWithR = (ParametersWithRandom)param;
+ sr = pWithR.getRandom();
+ param = pWithR.getParameters();
+ }
+ else
+ {
+ sr = new SecureRandom();
+ }
+
+ if (param instanceof ParametersWithIV)
+ {
+ this.paramPlusIV = (ParametersWithIV)param;
+ this.iv = this.paramPlusIV.getIV();
+ this.param = this.paramPlusIV.getParameters();
+
+ if (this.forWrapping)
+ {
+ if ((this.iv == null) || (this.iv.length != 8))
+ {
+ throw new IllegalArgumentException("IV is not 8 octets");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "You should not supply an IV for unwrapping");
+ }
+ }
+ else
+ {
+ this.param = param;
+
+ if (this.forWrapping)
+ {
+
+ // Hm, we have no IV but we want to wrap ?!?
+ // well, then we have to create our own IV.
+ this.iv = new byte[8];
+
+ sr.nextBytes(iv);
+
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+ }
+ }
+
+ }
+
+ /**
+ * Method getAlgorithmName
+ *
+ * @return the algorithm name "RC2".
+ */
+ public String getAlgorithmName()
+ {
+ return "RC2";
+ }
+
+ /**
+ * Method wrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the wrapped bytes.
+ */
+ public byte[] wrap(byte[] in, int inOff, int inLen)
+ {
+
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("Not initialized for wrapping");
+ }
+
+ int length = inLen + 1;
+ if ((length % 8) != 0)
+ {
+ length += 8 - (length % 8);
+ }
+
+ byte keyToBeWrapped[] = new byte[length];
+
+ keyToBeWrapped[0] = (byte)inLen;
+ System.arraycopy(in, inOff, keyToBeWrapped, 1, inLen);
+
+ byte[] pad = new byte[keyToBeWrapped.length - inLen - 1];
+
+ if (pad.length > 0)
+ {
+ sr.nextBytes(pad);
+ System.arraycopy(pad, 0, keyToBeWrapped, inLen + 1, pad.length);
+ }
+
+ // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
+ byte[] CKS = calculateCMSKeyChecksum(keyToBeWrapped);
+
+ // Let WKCKS = WK || CKS where || is concatenation.
+ byte[] WKCKS = new byte[keyToBeWrapped.length + CKS.length];
+
+ System.arraycopy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.length);
+ System.arraycopy(CKS, 0, WKCKS, keyToBeWrapped.length, CKS.length);
+
+ // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ // initialization vector. Call the results TEMP1.
+ byte TEMP1[] = new byte[WKCKS.length];
+
+ System.arraycopy(WKCKS, 0, TEMP1, 0, WKCKS.length);
+
+ int noOfBlocks = WKCKS.length / engine.getBlockSize();
+ int extraBytes = WKCKS.length % engine.getBlockSize();
+
+ if (extraBytes != 0)
+ {
+ throw new IllegalStateException("Not multiple of block length");
+ }
+
+ engine.init(true, paramPlusIV);
+
+ for (int i = 0; i < noOfBlocks; i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP1, currentBytePos, TEMP1, currentBytePos);
+ }
+
+ // Left TEMP2 = IV || TEMP1.
+ byte[] TEMP2 = new byte[this.iv.length + TEMP1.length];
+
+ System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
+ System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
+
+ // Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ byte[] TEMP3 = new byte[TEMP2.length];
+
+ for (int i = 0; i < TEMP2.length; i++)
+ {
+ TEMP3[i] = TEMP2[TEMP2.length - (i + 1)];
+ }
+
+ // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the
+ // desired
+ // result. It is 40 octets long if a 168 bit key is being wrapped.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(true, param2);
+
+ for (int i = 0; i < noOfBlocks + 1; i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ return TEMP3;
+ }
+
+ /**
+ * Method unwrap
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return the unwrapped bytes.
+ * @throws InvalidCipherTextException
+ */
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+
+ if (forWrapping)
+ {
+ throw new IllegalStateException("Not set for unwrapping");
+ }
+
+ if (in == null)
+ {
+ throw new InvalidCipherTextException("Null pointer as ciphertext");
+ }
+
+ if (inLen % engine.getBlockSize() != 0)
+ {
+ throw new InvalidCipherTextException("Ciphertext not multiple of "
+ + engine.getBlockSize());
+ }
+
+ /*
+ * // Check if the length of the cipher text is reasonable given the key //
+ * type. It must be 40 bytes for a 168 bit key and either 32, 40, or //
+ * 48 bytes for a 128, 192, or 256 bit key. If the length is not
+ * supported // or inconsistent with the algorithm for which the key is
+ * intended, // return error. // // we do not accept 168 bit keys. it
+ * has to be 192 bit. int lengthA = (estimatedKeyLengthInBit / 8) + 16;
+ * int lengthB = estimatedKeyLengthInBit % 8;
+ *
+ * if ((lengthA != keyToBeUnwrapped.length) || (lengthB != 0)) { throw
+ * new XMLSecurityException("empty"); }
+ */
+
+ // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
+ // and an initialization vector (IV) of 0x4adda22c79e82105. Call the
+ // output TEMP3.
+ ParametersWithIV param2 = new ParametersWithIV(this.param, IV2);
+
+ this.engine.init(false, param2);
+
+ byte TEMP3[] = new byte[inLen];
+
+ System.arraycopy(in, inOff, TEMP3, 0, inLen);
+
+ for (int i = 0; i < (TEMP3.length / engine.getBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+ }
+
+ // Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ byte[] TEMP2 = new byte[TEMP3.length];
+
+ for (int i = 0; i < TEMP3.length; i++)
+ {
+ TEMP2[i] = TEMP3[TEMP3.length - (i + 1)];
+ }
+
+ // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining
+ // octets.
+ this.iv = new byte[8];
+
+ byte[] TEMP1 = new byte[TEMP2.length - 8];
+
+ System.arraycopy(TEMP2, 0, this.iv, 0, 8);
+ System.arraycopy(TEMP2, 8, TEMP1, 0, TEMP2.length - 8);
+
+ // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
+ // found in the previous step. Call the result WKCKS.
+ this.paramPlusIV = new ParametersWithIV(this.param, this.iv);
+
+ this.engine.init(false, this.paramPlusIV);
+
+ byte[] LCEKPADICV = new byte[TEMP1.length];
+
+ System.arraycopy(TEMP1, 0, LCEKPADICV, 0, TEMP1.length);
+
+ for (int i = 0; i < (LCEKPADICV.length / engine.getBlockSize()); i++)
+ {
+ int currentBytePos = i * engine.getBlockSize();
+
+ engine.processBlock(LCEKPADICV, currentBytePos, LCEKPADICV,
+ currentBytePos);
+ }
+
+ // Decompose LCEKPADICV. CKS is the last 8 octets and WK, the wrapped
+ // key, are
+ // those octets before the CKS.
+ byte[] result = new byte[LCEKPADICV.length - 8];
+ byte[] CKStoBeVerified = new byte[8];
+
+ System.arraycopy(LCEKPADICV, 0, result, 0, LCEKPADICV.length - 8);
+ System.arraycopy(LCEKPADICV, LCEKPADICV.length - 8, CKStoBeVerified, 0,
+ 8);
+
+ // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and
+ // compare
+ // with the CKS extracted in the above step. If they are not equal,
+ // return error.
+ if (!checkCMSKeyChecksum(result, CKStoBeVerified))
+ {
+ throw new InvalidCipherTextException(
+ "Checksum inside ciphertext is corrupted");
+ }
+
+ if ((result.length - ((result[0] & 0xff) + 1)) > 7)
+ {
+ throw new InvalidCipherTextException("too many pad bytes ("
+ + (result.length - ((result[0] & 0xff) + 1)) + ")");
+ }
+
+ // CEK is the wrapped key, now extracted for use in data decryption.
+ byte[] CEK = new byte[result[0]];
+ System.arraycopy(result, 1, CEK, 0, CEK.length);
+ return CEK;
+ }
+
+ /**
+ * Some key wrap algorithms make use of the Key Checksum defined
+ * in CMS [CMS-Algorithms]. This is used to provide an integrity
+ * check value for the key being wrapped. The algorithm is
+ *
+ * - Compute the 20 octet SHA-1 hash on the key being wrapped.
+ * - Use the first 8 octets of this hash as the checksum value.
+ *
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ * @param key
+ * @return
+ * @throws RuntimeException
+ *
+ */
+ private byte[] calculateCMSKeyChecksum(
+ byte[] key)
+ {
+ byte[] result = new byte[8];
+
+ sha1.update(key, 0, key.length);
+ sha1.doFinal(digest, 0);
+
+ System.arraycopy(digest, 0, result, 0, 8);
+
+ return result;
+ }
+
+ /**
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ *
+ * @param key
+ * @param checksum
+ * @return
+ */
+ private boolean checkCMSKeyChecksum(
+ byte[] key,
+ byte[] checksum)
+ {
+ return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RC4Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/RC4Engine.java
new file mode 100644
index 00000000..e7ff73a9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RC4Engine.java
@@ -0,0 +1,146 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+
+public class RC4Engine implements StreamCipher
+{
+ private final static int STATE_LENGTH = 256;
+
+ /*
+ * variables to hold the state of the RC4 engine
+ * during encryption and decryption
+ */
+
+ private byte[] engineState = null;
+ private int x = 0;
+ private int y = 0;
+ private byte[] workingKey = null;
+
+ /**
+ * initialise a RC4 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params
+ )
+ {
+ if (params instanceof KeyParameter)
+ {
+ /*
+ * RC4 encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant.
+ */
+ workingKey = ((KeyParameter)params).getKey();
+ setKey(workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC4";
+ }
+
+ public byte returnByte(byte in)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len ; i++)
+ {
+ x = (x + 1) & 0xff;
+ y = (engineState[x] + y) & 0xff;
+
+ // swap
+ byte tmp = engineState[x];
+ engineState[x] = engineState[y];
+ engineState[y] = tmp;
+
+ // xor
+ out[i+outOff] = (byte)(in[i + inOff]
+ ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
+ }
+
+ return len;
+ }
+
+ public void reset()
+ {
+ setKey(workingKey);
+ }
+
+ // Private implementation
+
+ private void setKey(byte[] keyBytes)
+ {
+ workingKey = keyBytes;
+
+ // System.out.println("the key length is ; "+ workingKey.length);
+
+ x = 0;
+ y = 0;
+
+ if (engineState == null)
+ {
+ engineState = new byte[STATE_LENGTH];
+ }
+
+ // reset the state of the engine
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ engineState[i] = (byte)i;
+ }
+
+ int i1 = 0;
+ int i2 = 0;
+
+ for (int i=0; i < STATE_LENGTH; i++)
+ {
+ i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;
+ // do the byte-swap inline
+ byte tmp = engineState[i];
+ engineState[i] = engineState[i2];
+ engineState[i2] = tmp;
+ i1 = (i1+1) % keyBytes.length;
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RC532Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/RC532Engine.java
new file mode 100644
index 00000000..24619716
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RC532Engine.java
@@ -0,0 +1,287 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation has a word size of 32 bits.
+ * <p>
+ * Implementation courtesy of Tito Pena.
+ */
+public class RC532Engine
+ implements BlockCipher
+{
+ /*
+ * the number of rounds to perform
+ */
+ private int _noRounds;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private int _S[];
+
+ /*
+ * our "magic constants" for 32 32
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static final int P32 = 0xb7e15163;
+ private static final int Q32 = 0x9e3779b9;
+
+ private boolean forEncryption;
+
+ /**
+ * Create an instance of the RC5 encryption algorithm
+ * and set some defaults
+ */
+ public RC532Engine()
+ {
+ _noRounds = 12; // the default
+ _S = null;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC5-32";
+ }
+
+ public int getBlockSize()
+ {
+ return 2 * 4;
+ }
+
+ /**
+ * initialise a RC5-32 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof RC5Parameters)
+ {
+ RC5Parameters p = (RC5Parameters)params;
+
+ _noRounds = p.getRounds();
+
+ setKey(p.getKey());
+ }
+ else if (params instanceof KeyParameter)
+ {
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC532 init - " + params.getClass().getName());
+ }
+
+ this.forEncryption = forEncryption;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = 32/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ int[] L = new int[(key.length + (4 - 1)) / 4];
+
+ for (int i = 0; i != key.length; i++)
+ {
+ L[i / 4] += (key[i] & 0xff) << (8 * (i % 4));
+ }
+
+ //
+ // Phase 2:
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new int[2*(_noRounds + 1)];
+
+ _S[0] = P32;
+ for (int i=1; i < _S.length; i++)
+ {
+ _S[i] = (_S[i-1] + Q32);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.length > _S.length)
+ {
+ iter = 3 * L.length;
+ }
+ else
+ {
+ iter = 3 * _S.length;
+ }
+
+ int A = 0, B = 0;
+ int i = 0, j = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+ B = L[j] = rotateLeft(L[j] + A + B, A+B);
+ i = (i+1) % _S.length;
+ j = (j+1) % L.length;
+ }
+ }
+
+ /**
+ * Encrypt the given block starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * <p>
+ * @param in in byte buffer containing data to encrypt
+ * @param inOff offset into src buffer
+ * @param out out buffer where encrypted data is written
+ * @param outOff offset into out buffer
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int A = bytesToWord(in, inOff) + _S[0];
+ int B = bytesToWord(in, inOff + 4) + _S[1];
+
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ A = rotateLeft(A ^ B, B) + _S[2*i];
+ B = rotateLeft(B ^ A, A) + _S[2*i+1];
+ }
+
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + 4);
+
+ return 2 * 4;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int A = bytesToWord(in, inOff);
+ int B = bytesToWord(in, inOff + 4);
+
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ B = rotateRight(B - _S[2*i+1], A) ^ A;
+ A = rotateRight(A - _S[2*i], B) ^ B;
+ }
+
+ wordToBytes(A - _S[0], out, outOff);
+ wordToBytes(B - _S[1], out, outOff + 4);
+
+ return 2 * 4;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % 32
+ */
+ private int rotateLeft(int x, int y)
+ {
+ return ((x << (y & (32-1))) | (x >>> (32 - (y & (32-1)))));
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(32)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % 32
+ */
+ private int rotateRight(int x, int y)
+ {
+ return ((x >>> (y & (32-1))) | (x << (32 - (y & (32-1)))));
+ }
+
+ private int bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8)
+ | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24);
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff] = (byte)word;
+ dst[dstOff + 1] = (byte)(word >> 8);
+ dst[dstOff + 2] = (byte)(word >> 16);
+ dst[dstOff + 3] = (byte)(word >> 24);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RC564Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/RC564Engine.java
new file mode 100644
index 00000000..06fcbf7c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RC564Engine.java
@@ -0,0 +1,288 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.params.RC5Parameters;
+
+/**
+ * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
+ * publication in RSA CryptoBytes, Spring of 1995.
+ * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
+ * <p>
+ * This implementation is set to work with a 64 bit word size.
+ * <p>
+ * Implementation courtesy of Tito Pena.
+ */
+public class RC564Engine
+ implements BlockCipher
+{
+ private static final int wordSize = 64;
+ private static final int bytesPerWord = wordSize / 8;
+
+ /*
+ * the number of rounds to perform
+ */
+ private int _noRounds;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private long _S[];
+
+ /*
+ * our "magic constants" for wordSize 62
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static final long P64 = 0xb7e151628aed2a6bL;
+ private static final long Q64 = 0x9e3779b97f4a7c15L;
+
+ private boolean forEncryption;
+
+ /**
+ * Create an instance of the RC5 encryption algorithm
+ * and set some defaults
+ */
+ public RC564Engine()
+ {
+ _noRounds = 12;
+ _S = null;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC5-64";
+ }
+
+ public int getBlockSize()
+ {
+ return 2 * bytesPerWord;
+ }
+
+ /**
+ * initialise a RC5-64 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof RC5Parameters))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC564 init - " + params.getClass().getName());
+ }
+
+ RC5Parameters p = (RC5Parameters)params;
+
+ this.forEncryption = forEncryption;
+
+ _noRounds = p.getRounds();
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ return (forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = wordSize/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ long[] L = new long[(key.length + (bytesPerWord - 1)) / bytesPerWord];
+
+ for (int i = 0; i != key.length; i++)
+ {
+ L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord));
+ }
+
+ //
+ // Phase 2:
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new long[2*(_noRounds + 1)];
+
+ _S[0] = P64;
+ for (int i=1; i < _S.length; i++)
+ {
+ _S[i] = (_S[i-1] + Q64);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.length > _S.length)
+ {
+ iter = 3 * L.length;
+ }
+ else
+ {
+ iter = 3 * _S.length;
+ }
+
+ long A = 0, B = 0;
+ int i = 0, j = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+ B = L[j] = rotateLeft(L[j] + A + B, A+B);
+ i = (i+1) % _S.length;
+ j = (j+1) % L.length;
+ }
+ }
+
+ /**
+ * Encrypt the given block starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * <p>
+ * @param in in byte buffer containing data to encrypt
+ * @param inOff offset into src buffer
+ * @param out out buffer where encrypted data is written
+ * @param outOff offset into out buffer
+ */
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ long A = bytesToWord(in, inOff) + _S[0];
+ long B = bytesToWord(in, inOff + bytesPerWord) + _S[1];
+
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ A = rotateLeft(A ^ B, B) + _S[2*i];
+ B = rotateLeft(B ^ A, A) + _S[2*i+1];
+ }
+
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + bytesPerWord);
+
+ return 2 * bytesPerWord;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ long A = bytesToWord(in, inOff);
+ long B = bytesToWord(in, inOff + bytesPerWord);
+
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ B = rotateRight(B - _S[2*i+1], A) ^ A;
+ A = rotateRight(A - _S[2*i], B) ^ B;
+ }
+
+ wordToBytes(A - _S[0], out, outOff);
+ wordToBytes(B - _S[1], out, outOff + bytesPerWord);
+
+ return 2 * bytesPerWord;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private long rotateLeft(long x, long y)
+ {
+ return ((x << (y & (wordSize-1))) | (x >>> (wordSize - (y & (wordSize-1)))));
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private long rotateRight(long x, long y)
+ {
+ return ((x >>> (y & (wordSize-1))) | (x << (wordSize - (y & (wordSize-1)))));
+ }
+
+ private long bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ long word = 0;
+
+ for (int i = bytesPerWord - 1; i >= 0; i--)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void wordToBytes(
+ long word,
+ byte[] dst,
+ int dstOff)
+ {
+ for (int i = 0; i < bytesPerWord; i++)
+ {
+ dst[i + dstOff] = (byte)word;
+ word >>>= 8;
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RC6Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/RC6Engine.java
new file mode 100644
index 00000000..e91e654e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RC6Engine.java
@@ -0,0 +1,363 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * An RC6 engine.
+ */
+public class RC6Engine
+ implements BlockCipher
+{
+ private static final int wordSize = 32;
+ private static final int bytesPerWord = wordSize / 8;
+
+ /*
+ * the number of rounds to perform
+ */
+ private static final int _noRounds = 20;
+
+ /*
+ * the expanded key array of size 2*(rounds + 1)
+ */
+ private int _S[];
+
+ /*
+ * our "magic constants" for wordSize 32
+ *
+ * Pw = Odd((e-2) * 2^wordsize)
+ * Qw = Odd((o-2) * 2^wordsize)
+ *
+ * where e is the base of natural logarithms (2.718281828...)
+ * and o is the golden ratio (1.61803398...)
+ */
+ private static final int P32 = 0xb7e15163;
+ private static final int Q32 = 0x9e3779b9;
+
+ private static final int LGW = 5; // log2(32)
+
+ private boolean forEncryption;
+
+ /**
+ * Create an instance of the RC6 encryption algorithm
+ * and set some defaults
+ */
+ public RC6Engine()
+ {
+ _S = null;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "RC6";
+ }
+
+ public int getBlockSize()
+ {
+ return 4 * bytesPerWord;
+ }
+
+ /**
+ * initialise a RC5-32 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to RC6 init - " + params.getClass().getName());
+ }
+
+ KeyParameter p = (KeyParameter)params;
+ this.forEncryption = forEncryption;
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int blockSize = getBlockSize();
+ if (_S == null)
+ {
+ throw new IllegalStateException("RC6 engine not initialised");
+ }
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (forEncryption)
+ ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+
+ //
+ // KEY EXPANSION:
+ //
+ // There are 3 phases to the key expansion.
+ //
+ // Phase 1:
+ // Copy the secret key K[0...b-1] into an array L[0..c-1] of
+ // c = ceil(b/u), where u = wordSize/8 in little-endian order.
+ // In other words, we fill up L using u consecutive key bytes
+ // of K. Any unfilled byte positions in L are zeroed. In the
+ // case that b = c = 0, set c = 1 and L[0] = 0.
+ //
+ // compute number of dwords
+ int c = (key.length + (bytesPerWord - 1)) / bytesPerWord;
+ if (c == 0)
+ {
+ c = 1;
+ }
+ int[] L = new int[(key.length + bytesPerWord - 1) / bytesPerWord];
+
+ // load all key bytes into array of key dwords
+ for (int i = key.length - 1; i >= 0; i--)
+ {
+ L[i / bytesPerWord] = (L[i / bytesPerWord] << 8) + (key[i] & 0xff);
+ }
+
+ //
+ // Phase 2:
+ // Key schedule is placed in a array of 2+2*ROUNDS+2 = 44 dwords.
+ // Initialize S to a particular fixed pseudo-random bit pattern
+ // using an arithmetic progression modulo 2^wordsize determined
+ // by the magic numbers, Pw & Qw.
+ //
+ _S = new int[2+2*_noRounds+2];
+
+ _S[0] = P32;
+ for (int i=1; i < _S.length; i++)
+ {
+ _S[i] = (_S[i-1] + Q32);
+ }
+
+ //
+ // Phase 3:
+ // Mix in the user's secret key in 3 passes over the arrays S & L.
+ // The max of the arrays sizes is used as the loop control
+ //
+ int iter;
+
+ if (L.length > _S.length)
+ {
+ iter = 3 * L.length;
+ }
+ else
+ {
+ iter = 3 * _S.length;
+ }
+
+ int A = 0;
+ int B = 0;
+ int i = 0, j = 0;
+
+ for (int k = 0; k < iter; k++)
+ {
+ A = _S[i] = rotateLeft(_S[i] + A + B, 3);
+ B = L[j] = rotateLeft(L[j] + A + B, A+B);
+ i = (i+1) % _S.length;
+ j = (j+1) % L.length;
+ }
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // load A,B,C and D registers from in.
+ int A = bytesToWord(in, inOff);
+ int B = bytesToWord(in, inOff + bytesPerWord);
+ int C = bytesToWord(in, inOff + bytesPerWord*2);
+ int D = bytesToWord(in, inOff + bytesPerWord*3);
+
+ // Do pseudo-round #0: pre-whitening of B and D
+ B += _S[0];
+ D += _S[1];
+
+ // perform round #1,#2 ... #ROUNDS of encryption
+ for (int i = 1; i <= _noRounds; i++)
+ {
+ int t = 0,u = 0;
+
+ t = B*(2*B+1);
+ t = rotateLeft(t,5);
+
+ u = D*(2*D+1);
+ u = rotateLeft(u,5);
+
+ A ^= t;
+ A = rotateLeft(A,u);
+ A += _S[2*i];
+
+ C ^= u;
+ C = rotateLeft(C,t);
+ C += _S[2*i+1];
+
+ int temp = A;
+ A = B;
+ B = C;
+ C = D;
+ D = temp;
+ }
+ // do pseudo-round #(ROUNDS+1) : post-whitening of A and C
+ A += _S[2*_noRounds+2];
+ C += _S[2*_noRounds+3];
+
+ // store A, B, C and D registers to out
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + bytesPerWord);
+ wordToBytes(C, out, outOff + bytesPerWord*2);
+ wordToBytes(D, out, outOff + bytesPerWord*3);
+
+ return 4 * bytesPerWord;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // load A,B,C and D registers from out.
+ int A = bytesToWord(in, inOff);
+ int B = bytesToWord(in, inOff + bytesPerWord);
+ int C = bytesToWord(in, inOff + bytesPerWord*2);
+ int D = bytesToWord(in, inOff + bytesPerWord*3);
+
+ // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C
+ C -= _S[2*_noRounds+3];
+ A -= _S[2*_noRounds+2];
+
+ // Undo round #ROUNDS, .., #2,#1 of encryption
+ for (int i = _noRounds; i >= 1; i--)
+ {
+ int t=0,u = 0;
+
+ int temp = D;
+ D = C;
+ C = B;
+ B = A;
+ A = temp;
+
+ t = B*(2*B+1);
+ t = rotateLeft(t, LGW);
+
+ u = D*(2*D+1);
+ u = rotateLeft(u, LGW);
+
+ C -= _S[2*i+1];
+ C = rotateRight(C,t);
+ C ^= u;
+
+ A -= _S[2*i];
+ A = rotateRight(A,u);
+ A ^= t;
+
+ }
+ // Undo pseudo-round #0: pre-whitening of B and D
+ D -= _S[1];
+ B -= _S[0];
+
+ wordToBytes(A, out, outOff);
+ wordToBytes(B, out, outOff + bytesPerWord);
+ wordToBytes(C, out, outOff + bytesPerWord*2);
+ wordToBytes(D, out, outOff + bytesPerWord*3);
+
+ return 4 * bytesPerWord;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ //
+ // PRIVATE Helper Methods
+ //
+ //////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a left "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is 32.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private int rotateLeft(int x, int y)
+ {
+ return (x << y) | (x >>> -y);
+ }
+
+ /**
+ * Perform a right "spin" of the word. The rotation of the given
+ * word <em>x</em> is rotated left by <em>y</em> bits.
+ * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
+ * are used to determine the rotation amount. Here it is
+ * assumed that the wordsize used is a power of 2.
+ * <p>
+ * @param x word to rotate
+ * @param y number of bits to rotate % wordSize
+ */
+ private int rotateRight(int x, int y)
+ {
+ return (x >>> y) | (x << -y);
+ }
+
+ private int bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ int word = 0;
+
+ for (int i = bytesPerWord - 1; i >= 0; i--)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ for (int i = 0; i < bytesPerWord; i++)
+ {
+ dst[i + dstOff] = (byte)word;
+ word >>>= 8;
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RFC3211WrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RFC3211WrapEngine.java
new file mode 100644
index 00000000..f55a717a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RFC3211WrapEngine.java
@@ -0,0 +1,175 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+
+import java.security.SecureRandom;
+
+/**
+ * an implementation of the RFC 3211 Key Wrap
+ * Specification.
+ */
+public class RFC3211WrapEngine
+ implements Wrapper
+{
+ private CBCBlockCipher engine;
+ private ParametersWithIV param;
+ private boolean forWrapping;
+ private SecureRandom rand;
+
+ public RFC3211WrapEngine(BlockCipher engine)
+ {
+ this.engine = new CBCBlockCipher(engine);
+ }
+
+ public void init(
+ boolean forWrapping,
+ CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom p = (ParametersWithRandom)param;
+
+ rand = p.getRandom();
+ this.param = (ParametersWithIV)p.getParameters();
+ }
+ else
+ {
+ if (forWrapping)
+ {
+ rand = new SecureRandom();
+ }
+
+ this.param = (ParametersWithIV)param;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return engine.getUnderlyingCipher().getAlgorithmName() + "/RFC3211Wrap";
+ }
+
+ public byte[] wrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("not set for wrapping");
+ }
+
+ engine.init(true, param);
+
+ int blockSize = engine.getBlockSize();
+ byte[] cekBlock;
+
+ if (inLen + 4 < blockSize * 2)
+ {
+ cekBlock = new byte[blockSize * 2];
+ }
+ else
+ {
+ cekBlock = new byte[(inLen + 4) % blockSize == 0 ? inLen + 4 : ((inLen + 4) / blockSize + 1) * blockSize];
+ }
+
+ cekBlock[0] = (byte)inLen;
+ cekBlock[1] = (byte)~in[inOff];
+ cekBlock[2] = (byte)~in[inOff + 1];
+ cekBlock[3] = (byte)~in[inOff + 2];
+
+ System.arraycopy(in, inOff, cekBlock, 4, inLen);
+
+ for (int i = inLen + 4; i < cekBlock.length; i++)
+ {
+ cekBlock[i] = (byte)rand.nextInt();
+ }
+
+ for (int i = 0; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ for (int i = 0; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ return cekBlock;
+ }
+
+ public byte[] unwrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("not set for unwrapping");
+ }
+
+ int blockSize = engine.getBlockSize();
+
+ if (inLen < 2 * blockSize)
+ {
+ throw new InvalidCipherTextException("input too short");
+ }
+
+ byte[] cekBlock = new byte[inLen];
+ byte[] iv = new byte[blockSize];
+
+ System.arraycopy(in, inOff, cekBlock, 0, inLen);
+ System.arraycopy(in, inOff, iv, 0, iv.length);
+
+ engine.init(false, new ParametersWithIV(param.getParameters(), iv));
+
+ for (int i = blockSize; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ System.arraycopy(cekBlock, cekBlock.length - iv.length, iv, 0, iv.length);
+
+ engine.init(false, new ParametersWithIV(param.getParameters(), iv));
+
+ engine.processBlock(cekBlock, 0, cekBlock, 0);
+
+ engine.init(false, param);
+
+ for (int i = 0; i < cekBlock.length; i += blockSize)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ if ((cekBlock[0] & 0xff) > cekBlock.length - 4)
+ {
+ throw new InvalidCipherTextException("wrapped key corrupted");
+ }
+
+ byte[] key = new byte[cekBlock[0] & 0xff];
+
+ System.arraycopy(cekBlock, 4, key, 0, cekBlock[0]);
+
+ // Note: Using constant time comparison
+ int nonEqual = 0;
+ for (int i = 0; i != 3; i++)
+ {
+ byte check = (byte)~cekBlock[1 + i];
+ nonEqual |= (check ^ key[i]);
+ }
+ if (nonEqual != 0)
+ {
+ throw new InvalidCipherTextException("wrapped key fails checksum");
+ }
+
+ return key;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RFC3394WrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RFC3394WrapEngine.java
new file mode 100644
index 00000000..67ee43a7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RFC3394WrapEngine.java
@@ -0,0 +1,177 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.Arrays;
+
+/**
+ * an implementation of the AES Key Wrapper from the NIST Key Wrap
+ * Specification as described in RFC 3394.
+ * <p>
+ * For further details see: <a href="http://www.ietf.org/rfc/rfc3394.txt">http://www.ietf.org/rfc/rfc3394.txt</a>
+ * and <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ */
+public class RFC3394WrapEngine
+ implements Wrapper
+{
+ private BlockCipher engine;
+ private KeyParameter param;
+ private boolean forWrapping;
+
+ private byte[] iv = {
+ (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
+ (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
+
+ public RFC3394WrapEngine(BlockCipher engine)
+ {
+ this.engine = engine;
+ }
+
+ public void init(
+ boolean forWrapping,
+ CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ param = ((ParametersWithRandom) param).getParameters();
+ }
+
+ if (param instanceof KeyParameter)
+ {
+ this.param = (KeyParameter)param;
+ }
+ else if (param instanceof ParametersWithIV)
+ {
+ this.iv = ((ParametersWithIV)param).getIV();
+ this.param = (KeyParameter)((ParametersWithIV) param).getParameters();
+ if (this.iv.length != 8)
+ {
+ throw new IllegalArgumentException("IV not equal to 8");
+ }
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return engine.getAlgorithmName();
+ }
+
+ public byte[] wrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("not set for wrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new DataLengthException("wrap data must be a multiple of 8 bytes");
+ }
+
+ byte[] block = new byte[inLen + iv.length];
+ byte[] buf = new byte[8 + iv.length];
+
+ System.arraycopy(iv, 0, block, 0, iv.length);
+ System.arraycopy(in, inOff, block, iv.length, inLen);
+
+ engine.init(true, param);
+
+ for (int j = 0; j != 6; j++)
+ {
+ for (int i = 1; i <= n; i++)
+ {
+ System.arraycopy(block, 0, buf, 0, iv.length);
+ System.arraycopy(block, 8 * i, buf, iv.length, 8);
+ engine.processBlock(buf, 0, buf, 0);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.length - k] ^= v;
+
+ t >>>= 8;
+ }
+
+ System.arraycopy(buf, 0, block, 0, 8);
+ System.arraycopy(buf, 8, block, 8 * i, 8);
+ }
+ }
+
+ return block;
+ }
+
+ public byte[] unwrap(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("not set for unwrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
+ }
+
+ byte[] block = new byte[inLen - iv.length];
+ byte[] a = new byte[iv.length];
+ byte[] buf = new byte[8 + iv.length];
+
+ System.arraycopy(in, inOff, a, 0, iv.length);
+ System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+ engine.init(false, param);
+
+ n = n - 1;
+
+ for (int j = 5; j >= 0; j--)
+ {
+ for (int i = n; i >= 1; i--)
+ {
+ System.arraycopy(a, 0, buf, 0, iv.length);
+ System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.length - k] ^= v;
+
+ t >>>= 8;
+ }
+
+ engine.processBlock(buf, 0, buf, 0);
+ System.arraycopy(buf, 0, a, 0, 8);
+ System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
+ }
+ }
+
+ if (!Arrays.constantTimeAreEqual(a, iv))
+ {
+ throw new InvalidCipherTextException("checksum failed");
+ }
+
+ return block;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RFC5649WrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RFC5649WrapEngine.java
new file mode 100644
index 00000000..59cd7732
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RFC5649WrapEngine.java
@@ -0,0 +1,294 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Pack;
+
+/**
+ * An implementation of the AES Key Wrap with Padding specification
+ * as described in RFC 5649.
+ * <p>
+ * For details on the specification see:
+ * <a href="https://tools.ietf.org/html/rfc5649">https://tools.ietf.org/html/rfc5649</a>
+ * </p>
+ */
+public class RFC5649WrapEngine
+ implements Wrapper
+{
+ private BlockCipher engine;
+ private KeyParameter param;
+ private boolean forWrapping;
+
+ // The AIV as defined in the RFC
+ private byte[] highOrderIV = {(byte)0xa6, (byte)0x59, (byte)0x59, (byte)0xa6};
+ private byte[] preIV = highOrderIV;
+
+ private byte[] extractedAIV = null;
+
+ public RFC5649WrapEngine(BlockCipher engine)
+ {
+ this.engine = engine;
+ }
+
+ public void init(boolean forWrapping, CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ param = ((ParametersWithRandom)param).getParameters();
+ }
+
+ if (param instanceof KeyParameter)
+ {
+ this.param = (KeyParameter)param;
+ }
+ else if (param instanceof ParametersWithIV)
+ {
+ this.preIV = ((ParametersWithIV)param).getIV();
+ this.param = (KeyParameter)((ParametersWithIV)param).getParameters();
+ if (this.preIV.length != 4)
+ {
+ throw new IllegalArgumentException("IV length not equal to 4");
+ }
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return engine.getAlgorithmName();
+ }
+
+ /**
+ * Pads the plaintext (i.e., the key to be wrapped)
+ * as per section 4.1 of RFC 5649.
+ *
+ * @param plaintext The key being wrapped.
+ * @return The padded key.
+ */
+ private byte[] padPlaintext(byte[] plaintext)
+ {
+ int plaintextLength = plaintext.length;
+ int numOfZerosToAppend = (8 - (plaintextLength % 8)) % 8;
+ byte[] paddedPlaintext = new byte[plaintextLength + numOfZerosToAppend];
+ System.arraycopy(plaintext, 0, paddedPlaintext, 0, plaintextLength);
+ if (numOfZerosToAppend != 0)
+ {
+ // plaintext (i.e., key to be wrapped) does not have
+ // a multiple of 8 octet blocks so it must be padded
+ byte[] zeros = new byte[numOfZerosToAppend];
+ System.arraycopy(zeros, 0, paddedPlaintext, plaintextLength, numOfZerosToAppend);
+ }
+ return paddedPlaintext;
+ }
+
+ public byte[] wrap(byte[] in, int inOff, int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("not set for wrapping");
+ }
+ byte[] iv = new byte[8];
+
+ // MLI = size of key to be wrapped
+ byte[] mli = Pack.intToBigEndian(inLen);
+ // copy in the fixed portion of the AIV
+ System.arraycopy(preIV, 0, iv, 0, preIV.length);
+ // copy in the MLI after the AIV
+ System.arraycopy(mli, 0, iv, preIV.length, mli.length);
+
+ // get the relevant plaintext to be wrapped
+ byte[] relevantPlaintext = new byte[inLen];
+ System.arraycopy(in, inOff, relevantPlaintext, 0, inLen);
+ byte[] paddedPlaintext = padPlaintext(relevantPlaintext);
+
+ if (paddedPlaintext.length == 8)
+ {
+ // if the padded plaintext contains exactly 8 octets,
+ // then prepend iv and encrypt using AES in ECB mode.
+
+ // prepend the IV to the plaintext
+ byte[] paddedPlainTextWithIV = new byte[paddedPlaintext.length + iv.length];
+ System.arraycopy(iv, 0, paddedPlainTextWithIV, 0, iv.length);
+ System.arraycopy(paddedPlaintext, 0, paddedPlainTextWithIV, iv.length, paddedPlaintext.length);
+
+ engine.init(true, param);
+ for (int i = 0; i < paddedPlainTextWithIV.length; i += engine.getBlockSize())
+ {
+ engine.processBlock(paddedPlainTextWithIV, i, paddedPlainTextWithIV, i);
+ }
+
+ return paddedPlainTextWithIV;
+ }
+ else
+ {
+ // otherwise, apply the RFC 3394 wrap to
+ // the padded plaintext with the new IV
+ Wrapper wrapper = new RFC3394WrapEngine(engine);
+ ParametersWithIV paramsWithIV = new ParametersWithIV(param, iv);
+ wrapper.init(true, paramsWithIV);
+ return wrapper.wrap(paddedPlaintext, inOff, paddedPlaintext.length);
+ }
+
+ }
+
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("not set for unwrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
+ }
+
+ if (n == 1)
+ {
+ throw new InvalidCipherTextException("unwrap data must be at least 16 bytes");
+ }
+
+ byte[] relevantCiphertext = new byte[inLen];
+ System.arraycopy(in, inOff, relevantCiphertext, 0, inLen);
+ byte[] decrypted = new byte[inLen];
+ byte[] paddedPlaintext;
+
+ if (n == 2)
+ {
+ // When there are exactly two 64-bit blocks of ciphertext,
+ // they are decrypted as a single block using AES in ECB.
+ engine.init(false, param);
+ for (int i = 0; i < relevantCiphertext.length; i += engine.getBlockSize())
+ {
+ engine.processBlock(relevantCiphertext, i, decrypted, i);
+ }
+
+ // extract the AIV
+ extractedAIV = new byte[8];
+ System.arraycopy(decrypted, 0, extractedAIV, 0, extractedAIV.length);
+ paddedPlaintext = new byte[decrypted.length - extractedAIV.length];
+ System.arraycopy(decrypted, extractedAIV.length, paddedPlaintext, 0, paddedPlaintext.length);
+ }
+ else
+ {
+ // Otherwise, unwrap as per RFC 3394 but don't check IV the same way
+ decrypted = rfc3394UnwrapNoIvCheck(in, inOff, inLen);
+ paddedPlaintext = decrypted;
+ }
+
+ // Decompose the extracted AIV to the fixed portion and the MLI
+ byte[] extractedHighOrderAIV = new byte[4];
+ byte[] mliBytes = new byte[4];
+ System.arraycopy(extractedAIV, 0, extractedHighOrderAIV, 0, extractedHighOrderAIV.length);
+ System.arraycopy(extractedAIV, extractedHighOrderAIV.length, mliBytes, 0, mliBytes.length);
+ int mli = Pack.bigEndianToInt(mliBytes, 0);
+
+ // Even if a check fails we still continue and check everything
+ // else in order to avoid certain timing based side-channel attacks.
+ boolean isValid = true;
+
+ // Check the fixed portion of the AIV
+ if (!Arrays.constantTimeAreEqual(extractedHighOrderAIV, preIV))
+ {
+ isValid = false;
+ }
+
+ // Check the MLI against the actual length
+ int upperBound = paddedPlaintext.length;
+ int lowerBound = upperBound - 8;
+ if (mli <= lowerBound)
+ {
+ isValid = false;
+ }
+ if (mli > upperBound)
+ {
+ isValid = false;
+ }
+
+ // Check the number of padded zeros
+ int expectedZeros = upperBound - mli;
+ byte[] zeros = new byte[expectedZeros];
+ byte[] pad = new byte[expectedZeros];
+ System.arraycopy(paddedPlaintext, paddedPlaintext.length - expectedZeros, pad, 0, expectedZeros);
+ if (!Arrays.constantTimeAreEqual(pad, zeros))
+ {
+ isValid = false;
+ }
+
+ // Extract the plaintext from the padded plaintext
+ byte[] plaintext = new byte[mli];
+ System.arraycopy(paddedPlaintext, 0, plaintext, 0, plaintext.length);
+
+ if (!isValid)
+ {
+ throw new InvalidCipherTextException("checksum failed");
+ }
+
+ return plaintext;
+ }
+
+ /**
+ * Performs steps 1 and 2 of the unwrap process defined in RFC 3394.
+ * This code is duplicated from RFC3394WrapEngine because that class
+ * will throw an error during unwrap because the IV won't match up.
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return Unwrapped data.
+ */
+ private byte[] rfc3394UnwrapNoIvCheck(byte[] in, int inOff, int inLen)
+ {
+ byte[] iv = new byte[8];
+ byte[] block = new byte[inLen - iv.length];
+ byte[] a = new byte[iv.length];
+ byte[] buf = new byte[8 + iv.length];
+
+ System.arraycopy(in, inOff, a, 0, iv.length);
+ System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+ engine.init(false, param);
+
+ int n = inLen / 8;
+ n = n - 1;
+
+ for (int j = 5; j >= 0; j--)
+ {
+ for (int i = n; i >= 1; i--)
+ {
+ System.arraycopy(a, 0, buf, 0, iv.length);
+ System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.length - k] ^= v;
+
+ t >>>= 8;
+ }
+
+ engine.processBlock(buf, 0, buf, 0);
+ System.arraycopy(buf, 0, a, 0, 8);
+ System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
+ }
+ }
+
+ // set the extracted AIV
+ extractedAIV = a;
+
+ return block;
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RSABlindedEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RSABlindedEngine.java
new file mode 100644
index 00000000..d254f1c6
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RSABlindedEngine.java
@@ -0,0 +1,126 @@
+package org.spongycastle.crypto.engines;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.spongycastle.util.BigIntegers;
+
+/**
+ * this does your basic RSA algorithm with blinding
+ */
+public class RSABlindedEngine
+ implements AsymmetricBlockCipher
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private RSACoreEngine core = new RSACoreEngine();
+ private RSAKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ core.init(forEncryption, param);
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RSAKeyParameters)rParam.getParameters();
+ random = rParam.getRandom();
+ }
+ else
+ {
+ key = (RSAKeyParameters)param;
+ random = new SecureRandom();
+ }
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ return core.getInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ return core.getOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the basic RSA algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("RSA engine not initialised");
+ }
+
+ BigInteger input = core.convertInput(in, inOff, inLen);
+
+ BigInteger result;
+ if (key instanceof RSAPrivateCrtKeyParameters)
+ {
+ RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key;
+
+ BigInteger e = k.getPublicExponent();
+ if (e != null) // can't do blinding without a public exponent
+ {
+ BigInteger m = k.getModulus();
+ BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random);
+
+ BigInteger blindedInput = r.modPow(e, m).multiply(input).mod(m);
+ BigInteger blindedResult = core.processBlock(blindedInput);
+
+ BigInteger rInv = r.modInverse(m);
+ result = blindedResult.multiply(rInv).mod(m);
+ }
+ else
+ {
+ result = core.processBlock(input);
+ }
+ }
+ else
+ {
+ result = core.processBlock(input);
+ }
+
+ return core.convertOutput(result);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RSABlindingEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RSABlindingEngine.java
new file mode 100644
index 00000000..1888916e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RSABlindingEngine.java
@@ -0,0 +1,137 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSABlindingParameters;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * This does your basic RSA Chaum's blinding and unblinding as outlined in
+ * "Handbook of Applied Cryptography", page 475. You need to use this if you are
+ * trying to get another party to generate signatures without them being aware
+ * of the message they are signing.
+ */
+public class RSABlindingEngine
+ implements AsymmetricBlockCipher
+{
+ private RSACoreEngine core = new RSACoreEngine();
+
+ private RSAKeyParameters key;
+ private BigInteger blindingFactor;
+
+ private boolean forEncryption;
+
+ /**
+ * Initialise the blinding engine.
+ *
+ * @param forEncryption true if we are encrypting (blinding), false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ RSABlindingParameters p;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ p = (RSABlindingParameters)rParam.getParameters();
+ }
+ else
+ {
+ p = (RSABlindingParameters)param;
+ }
+
+ core.init(forEncryption, p.getPublicKey());
+
+ this.forEncryption = forEncryption;
+ this.key = p.getPublicKey();
+ this.blindingFactor = p.getBlindingFactor();
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ return core.getInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ return core.getOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the RSA blinding algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @throws DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ BigInteger msg = core.convertInput(in, inOff, inLen);
+
+ if (forEncryption)
+ {
+ msg = blindMessage(msg);
+ }
+ else
+ {
+ msg = unblindMessage(msg);
+ }
+
+ return core.convertOutput(msg);
+ }
+
+ /*
+ * Blind message with the blind factor.
+ */
+ private BigInteger blindMessage(
+ BigInteger msg)
+ {
+ BigInteger blindMsg = blindingFactor;
+ blindMsg = msg.multiply(blindMsg.modPow(key.getExponent(), key.getModulus()));
+ blindMsg = blindMsg.mod(key.getModulus());
+
+ return blindMsg;
+ }
+
+ /*
+ * Unblind the message blinded with the blind factor.
+ */
+ private BigInteger unblindMessage(
+ BigInteger blindedMsg)
+ {
+ BigInteger m = key.getModulus();
+ BigInteger msg = blindedMsg;
+ BigInteger blindFactorInverse = blindingFactor.modInverse(m);
+ msg = msg.multiply(blindFactorInverse);
+ msg = msg.mod(m);
+
+ return msg;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RSACoreEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RSACoreEngine.java
new file mode 100644
index 00000000..4a98bbda
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RSACoreEngine.java
@@ -0,0 +1,203 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.RSAKeyParameters;
+import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+import java.math.BigInteger;
+
+/**
+ * this does your basic RSA algorithm.
+ */
+class RSACoreEngine
+{
+ private RSAKeyParameters key;
+ private boolean forEncryption;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (RSAKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ key = (RSAKeyParameters)param;
+ }
+
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ int bitSize = key.getModulus().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ else
+ {
+ return (bitSize + 7) / 8;
+ }
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ int bitSize = key.getModulus().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8;
+ }
+ else
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ }
+
+ public BigInteger convertInput(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (inLen > (getInputBlockSize() + 1))
+ {
+ throw new DataLengthException("input too large for RSA cipher.");
+ }
+ else if (inLen == (getInputBlockSize() + 1) && !forEncryption)
+ {
+ throw new DataLengthException("input too large for RSA cipher.");
+ }
+
+ byte[] block;
+
+ if (inOff != 0 || inLen != in.length)
+ {
+ block = new byte[inLen];
+
+ System.arraycopy(in, inOff, block, 0, inLen);
+ }
+ else
+ {
+ block = in;
+ }
+
+ BigInteger res = new BigInteger(1, block);
+ if (res.compareTo(key.getModulus()) >= 0)
+ {
+ throw new DataLengthException("input too large for RSA cipher.");
+ }
+
+ return res;
+ }
+
+ public byte[] convertOutput(
+ BigInteger result)
+ {
+ byte[] output = result.toByteArray();
+
+ if (forEncryption)
+ {
+ if (output[0] == 0 && output.length > getOutputBlockSize()) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ if (output.length < getOutputBlockSize()) // have ended up with less bytes than normal, lengthen
+ {
+ byte[] tmp = new byte[getOutputBlockSize()];
+
+ System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
+
+ return tmp;
+ }
+ }
+ else
+ {
+ if (output[0] == 0) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+ }
+
+ return output;
+ }
+
+ public BigInteger processBlock(BigInteger input)
+ {
+ if (key instanceof RSAPrivateCrtKeyParameters)
+ {
+ //
+ // we have the extra factors, use the Chinese Remainder Theorem - the author
+ // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for
+ // advice regarding the expression of this.
+ //
+ RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
+
+ BigInteger p = crtKey.getP();
+ BigInteger q = crtKey.getQ();
+ BigInteger dP = crtKey.getDP();
+ BigInteger dQ = crtKey.getDQ();
+ BigInteger qInv = crtKey.getQInv();
+
+ BigInteger mP, mQ, h, m;
+
+ // mP = ((input mod p) ^ dP)) mod p
+ mP = (input.remainder(p)).modPow(dP, p);
+
+ // mQ = ((input mod q) ^ dQ)) mod q
+ mQ = (input.remainder(q)).modPow(dQ, q);
+
+ // h = qInv * (mP - mQ) mod p
+ h = mP.subtract(mQ);
+ h = h.multiply(qInv);
+ h = h.mod(p); // mod (in Java) returns the positive residual
+
+ // m = h * q + mQ
+ m = h.multiply(q);
+ m = m.add(mQ);
+
+ return m;
+ }
+ else
+ {
+ return input.modPow(
+ key.getExponent(), key.getModulus());
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RSAEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RSAEngine.java
new file mode 100644
index 00000000..5ffe2ace
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RSAEngine.java
@@ -0,0 +1,78 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+
+/**
+ * this does your basic RSA algorithm.
+ */
+public class RSAEngine
+ implements AsymmetricBlockCipher
+{
+ private RSACoreEngine core;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (core == null)
+ {
+ core = new RSACoreEngine();
+ }
+
+ core.init(forEncryption, param);
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ return core.getInputBlockSize();
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ return core.getOutputBlockSize();
+ }
+
+ /**
+ * Process a single block using the basic RSA algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (core == null)
+ {
+ throw new IllegalStateException("RSA engine not initialised");
+ }
+
+ return core.convertOutput(core.processBlock(core.convertInput(in, inOff, inLen)));
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/RijndaelEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/RijndaelEngine.java
new file mode 100644
index 00000000..b38c7114
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/RijndaelEngine.java
@@ -0,0 +1,725 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * an implementation of Rijndael, based on the documentation and reference implementation
+ * by Paulo Barreto, Vincent Rijmen, for v2.0 August '99.
+ * <p>
+ * Note: this implementation is based on information prior to final NIST publication.
+ */
+public class RijndaelEngine
+ implements BlockCipher
+{
+ private static final int MAXROUNDS = 14;
+
+ private static final int MAXKC = (256/4);
+
+ private static final byte[] logtable = {
+ (byte)0, (byte)0, (byte)25, (byte)1, (byte)50, (byte)2, (byte)26, (byte)198,
+ (byte)75, (byte)199, (byte)27, (byte)104, (byte)51, (byte)238, (byte)223, (byte)3,
+ (byte)100, (byte)4, (byte)224, (byte)14, (byte)52, (byte)141, (byte)129, (byte)239,
+ (byte)76, (byte)113, (byte)8, (byte)200, (byte)248, (byte)105, (byte)28, (byte)193,
+ (byte)125, (byte)194, (byte)29, (byte)181, (byte)249, (byte)185, (byte)39, (byte)106,
+ (byte)77, (byte)228, (byte)166, (byte)114, (byte)154, (byte)201, (byte)9, (byte)120,
+ (byte)101, (byte)47, (byte)138, (byte)5, (byte)33, (byte)15, (byte)225, (byte)36,
+ (byte)18, (byte)240, (byte)130, (byte)69, (byte)53, (byte)147, (byte)218, (byte)142,
+ (byte)150, (byte)143, (byte)219, (byte)189, (byte)54, (byte)208, (byte)206, (byte)148,
+ (byte)19, (byte)92, (byte)210, (byte)241, (byte)64, (byte)70, (byte)131, (byte)56,
+ (byte)102, (byte)221, (byte)253, (byte)48, (byte)191, (byte)6, (byte)139, (byte)98,
+ (byte)179, (byte)37, (byte)226, (byte)152, (byte)34, (byte)136, (byte)145, (byte)16,
+ (byte)126, (byte)110, (byte)72, (byte)195, (byte)163, (byte)182, (byte)30, (byte)66,
+ (byte)58, (byte)107, (byte)40, (byte)84, (byte)250, (byte)133, (byte)61, (byte)186,
+ (byte)43, (byte)121, (byte)10, (byte)21, (byte)155, (byte)159, (byte)94, (byte)202,
+ (byte)78, (byte)212, (byte)172, (byte)229, (byte)243, (byte)115, (byte)167, (byte)87,
+ (byte)175, (byte)88, (byte)168, (byte)80, (byte)244, (byte)234, (byte)214, (byte)116,
+ (byte)79, (byte)174, (byte)233, (byte)213, (byte)231, (byte)230, (byte)173, (byte)232,
+ (byte)44, (byte)215, (byte)117, (byte)122, (byte)235, (byte)22, (byte)11, (byte)245,
+ (byte)89, (byte)203, (byte)95, (byte)176, (byte)156, (byte)169, (byte)81, (byte)160,
+ (byte)127, (byte)12, (byte)246, (byte)111, (byte)23, (byte)196, (byte)73, (byte)236,
+ (byte)216, (byte)67, (byte)31, (byte)45, (byte)164, (byte)118, (byte)123, (byte)183,
+ (byte)204, (byte)187, (byte)62, (byte)90, (byte)251, (byte)96, (byte)177, (byte)134,
+ (byte)59, (byte)82, (byte)161, (byte)108, (byte)170, (byte)85, (byte)41, (byte)157,
+ (byte)151, (byte)178, (byte)135, (byte)144, (byte)97, (byte)190, (byte)220, (byte)252,
+ (byte)188, (byte)149, (byte)207, (byte)205, (byte)55, (byte)63, (byte)91, (byte)209,
+ (byte)83, (byte)57, (byte)132, (byte)60, (byte)65, (byte)162, (byte)109, (byte)71,
+ (byte)20, (byte)42, (byte)158, (byte)93, (byte)86, (byte)242, (byte)211, (byte)171,
+ (byte)68, (byte)17, (byte)146, (byte)217, (byte)35, (byte)32, (byte)46, (byte)137,
+ (byte)180, (byte)124, (byte)184, (byte)38, (byte)119, (byte)153, (byte)227, (byte)165,
+ (byte)103, (byte)74, (byte)237, (byte)222, (byte)197, (byte)49, (byte)254, (byte)24,
+ (byte)13, (byte)99, (byte)140, (byte)128, (byte)192, (byte)247, (byte)112, (byte)7
+ };
+
+ private static final byte[] aLogtable = {
+ (byte)0, (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53,
+ (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170,
+ (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49,
+ (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205,
+ (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136,
+ (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154,
+ (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163,
+ (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160,
+ (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65,
+ (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117,
+ (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128,
+ (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84,
+ (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202,
+ (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14,
+ (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23,
+ (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1,
+ (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53,
+ (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170,
+ (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49,
+ (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205,
+ (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136,
+ (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154,
+ (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163,
+ (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160,
+ (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65,
+ (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117,
+ (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128,
+ (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84,
+ (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202,
+ (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14,
+ (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23,
+ (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1,
+ };
+
+ private static final byte[] S = {
+ (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
+ (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
+ (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
+ (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
+ (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
+ (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
+ (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
+ (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
+ (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
+ (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
+ (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
+ (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
+ (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
+ (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
+ (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
+ (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
+ };
+
+ private static final byte[] Si = {
+ (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251,
+ (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203,
+ (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78,
+ (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37,
+ (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146,
+ (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132,
+ (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6,
+ (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107,
+ (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115,
+ (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110,
+ (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27,
+ (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244,
+ (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95,
+ (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239,
+ (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97,
+ (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125,
+ };
+
+ private static final int[] rcon = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ static byte[][] shifts0 =
+ {
+ { 0, 8, 16, 24 },
+ { 0, 8, 16, 24 },
+ { 0, 8, 16, 24 },
+ { 0, 8, 16, 32 },
+ { 0, 8, 24, 32 }
+ };
+
+ static byte[][] shifts1 =
+ {
+ { 0, 24, 16, 8 },
+ { 0, 32, 24, 16 },
+ { 0, 40, 32, 24 },
+ { 0, 48, 40, 24 },
+ { 0, 56, 40, 32 }
+ };
+
+ /**
+ * multiply two elements of GF(2^m)
+ * needed for MixColumn and InvMixColumn
+ */
+ private byte mul0x2(
+ int b)
+ {
+ if (b != 0)
+ {
+ return aLogtable[25 + (logtable[b] & 0xff)];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0x3(
+ int b)
+ {
+ if (b != 0)
+ {
+ return aLogtable[1 + (logtable[b] & 0xff)];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0x9(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[199 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0xb(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[104 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0xd(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[238 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ private byte mul0xe(
+ int b)
+ {
+ if (b >= 0)
+ {
+ return aLogtable[223 + b];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ /**
+ * xor corresponding text input and round key input bytes
+ */
+ private void KeyAddition(
+ long[] rk)
+ {
+ A0 ^= rk[0];
+ A1 ^= rk[1];
+ A2 ^= rk[2];
+ A3 ^= rk[3];
+ }
+
+ private long shift(
+ long r,
+ int shift)
+ {
+ return (((r >>> shift) | (r << (BC - shift)))) & BC_MASK;
+ }
+
+ /**
+ * Row 0 remains unchanged
+ * The other three rows are shifted a variable amount
+ */
+ private void ShiftRow(
+ byte[] shiftsSC)
+ {
+ A1 = shift(A1, shiftsSC[1]);
+ A2 = shift(A2, shiftsSC[2]);
+ A3 = shift(A3, shiftsSC[3]);
+ }
+
+ private long applyS(
+ long r,
+ byte[] box)
+ {
+ long res = 0;
+
+ for (int j = 0; j < BC; j += 8)
+ {
+ res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j;
+ }
+
+ return res;
+ }
+
+ /**
+ * Replace every byte of the input by the byte at that place
+ * in the nonlinear S-box
+ */
+ private void Substitution(
+ byte[] box)
+ {
+ A0 = applyS(A0, box);
+ A1 = applyS(A1, box);
+ A2 = applyS(A2, box);
+ A3 = applyS(A3, box);
+ }
+
+ /**
+ * Mix the bytes of every column in a linear way
+ */
+ private void MixColumn()
+ {
+ long r0, r1, r2, r3;
+
+ r0 = r1 = r2 = r3 = 0;
+
+ for (int j = 0; j < BC; j += 8)
+ {
+ int a0 = (int)((A0 >> j) & 0xff);
+ int a1 = (int)((A1 >> j) & 0xff);
+ int a2 = (int)((A2 >> j) & 0xff);
+ int a3 = (int)((A3 >> j) & 0xff);
+
+ r0 |= (long)((mul0x2(a0) ^ mul0x3(a1) ^ a2 ^ a3) & 0xff) << j;
+
+ r1 |= (long)((mul0x2(a1) ^ mul0x3(a2) ^ a3 ^ a0) & 0xff) << j;
+
+ r2 |= (long)((mul0x2(a2) ^ mul0x3(a3) ^ a0 ^ a1) & 0xff) << j;
+
+ r3 |= (long)((mul0x2(a3) ^ mul0x3(a0) ^ a1 ^ a2) & 0xff) << j;
+ }
+
+ A0 = r0;
+ A1 = r1;
+ A2 = r2;
+ A3 = r3;
+ }
+
+ /**
+ * Mix the bytes of every column in a linear way
+ * This is the opposite operation of Mixcolumn
+ */
+ private void InvMixColumn()
+ {
+ long r0, r1, r2, r3;
+
+ r0 = r1 = r2 = r3 = 0;
+ for (int j = 0; j < BC; j += 8)
+ {
+ int a0 = (int)((A0 >> j) & 0xff);
+ int a1 = (int)((A1 >> j) & 0xff);
+ int a2 = (int)((A2 >> j) & 0xff);
+ int a3 = (int)((A3 >> j) & 0xff);
+
+ //
+ // pre-lookup the log table
+ //
+ a0 = (a0 != 0) ? (logtable[a0 & 0xff] & 0xff) : -1;
+ a1 = (a1 != 0) ? (logtable[a1 & 0xff] & 0xff) : -1;
+ a2 = (a2 != 0) ? (logtable[a2 & 0xff] & 0xff) : -1;
+ a3 = (a3 != 0) ? (logtable[a3 & 0xff] & 0xff) : -1;
+
+ r0 |= (long)((mul0xe(a0) ^ mul0xb(a1) ^ mul0xd(a2) ^ mul0x9(a3)) & 0xff) << j;
+
+ r1 |= (long)((mul0xe(a1) ^ mul0xb(a2) ^ mul0xd(a3) ^ mul0x9(a0)) & 0xff) << j;
+
+ r2 |= (long)((mul0xe(a2) ^ mul0xb(a3) ^ mul0xd(a0) ^ mul0x9(a1)) & 0xff) << j;
+
+ r3 |= (long)((mul0xe(a3) ^ mul0xb(a0) ^ mul0xd(a1) ^ mul0x9(a2)) & 0xff) << j;
+ }
+
+ A0 = r0;
+ A1 = r1;
+ A2 = r2;
+ A3 = r3;
+ }
+
+ /**
+ * Calculate the necessary round keys
+ * The number of calculations depends on keyBits and blockBits
+ */
+ private long[][] generateWorkingKey(
+ byte[] key)
+ {
+ int KC;
+ int t, rconpointer = 0;
+ int keyBits = key.length * 8;
+ byte[][] tk = new byte[4][MAXKC];
+ long[][] W = new long[MAXROUNDS+1][4];
+
+ switch (keyBits)
+ {
+ case 128:
+ KC = 4;
+ break;
+ case 160:
+ KC = 5;
+ break;
+ case 192:
+ KC = 6;
+ break;
+ case 224:
+ KC = 7;
+ break;
+ case 256:
+ KC = 8;
+ break;
+ default :
+ throw new IllegalArgumentException("Key length not 128/160/192/224/256 bits.");
+ }
+
+ if (keyBits >= blockBits)
+ {
+ ROUNDS = KC + 6;
+ }
+ else
+ {
+ ROUNDS = (BC / 8) + 6;
+ }
+
+ //
+ // copy the key into the processing area
+ //
+ int index = 0;
+
+ for (int i = 0; i < key.length; i++)
+ {
+ tk[i % 4][i / 4] = key[index++];
+ }
+
+ t = 0;
+
+ //
+ // copy values into round key array
+ //
+ for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC / 8)); j++, t++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[t / (BC / 8)][i] |= (long)(tk[i][j] & 0xff) << ((t * 8) % BC);
+ }
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ while (t < (ROUNDS+1)*(BC/8))
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][0] ^= S[tk[(i+1)%4][KC-1] & 0xff];
+ }
+ tk[0][0] ^= rcon[rconpointer++];
+
+ if (KC <= 6)
+ {
+ for (int j = 1; j < KC; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][j] ^= tk[i][j-1];
+ }
+ }
+ }
+ else
+ {
+ for (int j = 1; j < 4; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][j] ^= tk[i][j-1];
+ }
+ }
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][4] ^= S[tk[i][3] & 0xff];
+ }
+ for (int j = 5; j < KC; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ tk[i][j] ^= tk[i][j-1];
+ }
+ }
+ }
+
+ //
+ // copy values into round key array
+ //
+ for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC/8)); j++, t++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[t / (BC/8)][i] |= (long)(tk[i][j] & 0xff) << ((t * 8) % (BC));
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int BC;
+ private long BC_MASK;
+ private int ROUNDS;
+ private int blockBits;
+ private long[][] workingKey;
+ private long A0, A1, A2, A3;
+ private boolean forEncryption;
+ private byte[] shifts0SC;
+ private byte[] shifts1SC;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public RijndaelEngine()
+ {
+ this(128);
+ }
+
+ /**
+ * basic constructor - set the cipher up for a given blocksize
+ *
+ * @param blockBits the blocksize in bits, must be 128, 192, or 256.
+ */
+ public RijndaelEngine(
+ int blockBits)
+ {
+ switch (blockBits)
+ {
+ case 128:
+ BC = 32;
+ BC_MASK = 0xffffffffL;
+ shifts0SC = shifts0[0];
+ shifts1SC = shifts1[0];
+ break;
+ case 160:
+ BC = 40;
+ BC_MASK = 0xffffffffffL;
+ shifts0SC = shifts0[1];
+ shifts1SC = shifts1[1];
+ break;
+ case 192:
+ BC = 48;
+ BC_MASK = 0xffffffffffffL;
+ shifts0SC = shifts0[2];
+ shifts1SC = shifts1[2];
+ break;
+ case 224:
+ BC = 56;
+ BC_MASK = 0xffffffffffffffL;
+ shifts0SC = shifts0[3];
+ shifts1SC = shifts1[3];
+ break;
+ case 256:
+ BC = 64;
+ BC_MASK = 0xffffffffffffffffL;
+ shifts0SC = shifts0[4];
+ shifts1SC = shifts1[4];
+ break;
+ default:
+ throw new IllegalArgumentException("unknown blocksize to Rijndael");
+ }
+
+ this.blockBits = blockBits;
+ }
+
+ /**
+ * initialise a Rijndael cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ workingKey = generateWorkingKey(((KeyParameter)params).getKey());
+ this.forEncryption = forEncryption;
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Rijndael init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Rijndael";
+ }
+
+ public int getBlockSize()
+ {
+ return BC / 2;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Rijndael engine not initialised");
+ }
+
+ if ((inOff + (BC / 2)) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + (BC / 2)) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(workingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(workingKey);
+ packBlock(out, outOff);
+ }
+
+ return BC / 2;
+ }
+
+ public void reset()
+ {
+ }
+
+ private void unpackBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ A0 = (bytes[index++] & 0xff);
+ A1 = (bytes[index++] & 0xff);
+ A2 = (bytes[index++] & 0xff);
+ A3 = (bytes[index++] & 0xff);
+
+ for (int j = 8; j != BC; j += 8)
+ {
+ A0 |= (long)(bytes[index++] & 0xff) << j;
+ A1 |= (long)(bytes[index++] & 0xff) << j;
+ A2 |= (long)(bytes[index++] & 0xff) << j;
+ A3 |= (long)(bytes[index++] & 0xff) << j;
+ }
+ }
+
+ private void packBlock(
+ byte[] bytes,
+ int off)
+ {
+ int index = off;
+
+ for (int j = 0; j != BC; j += 8)
+ {
+ bytes[index++] = (byte)(A0 >> j);
+ bytes[index++] = (byte)(A1 >> j);
+ bytes[index++] = (byte)(A2 >> j);
+ bytes[index++] = (byte)(A3 >> j);
+ }
+ }
+
+ private void encryptBlock(
+ long[][] rk)
+ {
+ int r;
+
+ //
+ // begin with a key addition
+ //
+ KeyAddition(rk[0]);
+
+ //
+ // ROUNDS-1 ordinary rounds
+ //
+ for (r = 1; r < ROUNDS; r++)
+ {
+ Substitution(S);
+ ShiftRow(shifts0SC);
+ MixColumn();
+ KeyAddition(rk[r]);
+ }
+
+ //
+ // Last round is special: there is no MixColumn
+ //
+ Substitution(S);
+ ShiftRow(shifts0SC);
+ KeyAddition(rk[ROUNDS]);
+ }
+
+ private void decryptBlock(
+ long[][] rk)
+ {
+ int r;
+
+ // To decrypt: apply the inverse operations of the encrypt routine,
+ // in opposite order
+ //
+ // (KeyAddition is an involution: it 's equal to its inverse)
+ // (the inverse of Substitution with table S is Substitution with the inverse table of S)
+ // (the inverse of Shiftrow is Shiftrow over a suitable distance)
+ //
+
+ // First the special round:
+ // without InvMixColumn
+ // with extra KeyAddition
+ //
+ KeyAddition(rk[ROUNDS]);
+ Substitution(Si);
+ ShiftRow(shifts1SC);
+
+ //
+ // ROUNDS-1 ordinary rounds
+ //
+ for (r = ROUNDS-1; r > 0; r--)
+ {
+ KeyAddition(rk[r]);
+ InvMixColumn();
+ Substitution(Si);
+ ShiftRow(shifts1SC);
+ }
+
+ //
+ // End with the extra key addition
+ //
+ KeyAddition(rk[0]);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/SEEDEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/SEEDEngine.java
new file mode 100644
index 00000000..1a376c91
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/SEEDEngine.java
@@ -0,0 +1,346 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * Implementation of the SEED algorithm as described in RFC 4009
+ */
+public class SEEDEngine
+ implements BlockCipher
+{
+ private final int BLOCK_SIZE = 16;
+
+ private static final int[] SS0 =
+ {
+ 0x2989a1a8, 0x05858184, 0x16c6d2d4, 0x13c3d3d0, 0x14445054, 0x1d0d111c, 0x2c8ca0ac, 0x25052124,
+ 0x1d4d515c, 0x03434340, 0x18081018, 0x1e0e121c, 0x11415150, 0x3cccf0fc, 0x0acac2c8, 0x23436360,
+ 0x28082028, 0x04444044, 0x20002020, 0x1d8d919c, 0x20c0e0e0, 0x22c2e2e0, 0x08c8c0c8, 0x17071314,
+ 0x2585a1a4, 0x0f8f838c, 0x03030300, 0x3b4b7378, 0x3b8bb3b8, 0x13031310, 0x12c2d2d0, 0x2ecee2ec,
+ 0x30407070, 0x0c8c808c, 0x3f0f333c, 0x2888a0a8, 0x32023230, 0x1dcdd1dc, 0x36c6f2f4, 0x34447074,
+ 0x2ccce0ec, 0x15859194, 0x0b0b0308, 0x17475354, 0x1c4c505c, 0x1b4b5358, 0x3d8db1bc, 0x01010100,
+ 0x24042024, 0x1c0c101c, 0x33437370, 0x18889098, 0x10001010, 0x0cccc0cc, 0x32c2f2f0, 0x19c9d1d8,
+ 0x2c0c202c, 0x27c7e3e4, 0x32427270, 0x03838380, 0x1b8b9398, 0x11c1d1d0, 0x06868284, 0x09c9c1c8,
+ 0x20406060, 0x10405050, 0x2383a3a0, 0x2bcbe3e8, 0x0d0d010c, 0x3686b2b4, 0x1e8e929c, 0x0f4f434c,
+ 0x3787b3b4, 0x1a4a5258, 0x06c6c2c4, 0x38487078, 0x2686a2a4, 0x12021210, 0x2f8fa3ac, 0x15c5d1d4,
+ 0x21416160, 0x03c3c3c0, 0x3484b0b4, 0x01414140, 0x12425250, 0x3d4d717c, 0x0d8d818c, 0x08080008,
+ 0x1f0f131c, 0x19899198, 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37c7f3f4, 0x21c1e1e0,
+ 0x3dcdf1fc, 0x36467274, 0x2f0f232c, 0x27072324, 0x3080b0b0, 0x0b8b8388, 0x0e0e020c, 0x2b8ba3a8,
+ 0x2282a2a0, 0x2e4e626c, 0x13839390, 0x0d4d414c, 0x29496168, 0x3c4c707c, 0x09090108, 0x0a0a0208,
+ 0x3f8fb3bc, 0x2fcfe3ec, 0x33c3f3f0, 0x05c5c1c4, 0x07878384, 0x14041014, 0x3ecef2fc, 0x24446064,
+ 0x1eced2dc, 0x2e0e222c, 0x0b4b4348, 0x1a0a1218, 0x06060204, 0x21012120, 0x2b4b6368, 0x26466264,
+ 0x02020200, 0x35c5f1f4, 0x12829290, 0x0a8a8288, 0x0c0c000c, 0x3383b3b0, 0x3e4e727c, 0x10c0d0d0,
+ 0x3a4a7278, 0x07474344, 0x16869294, 0x25c5e1e4, 0x26062224, 0x00808080, 0x2d8da1ac, 0x1fcfd3dc,
+ 0x2181a1a0, 0x30003030, 0x37073334, 0x2e8ea2ac, 0x36063234, 0x15051114, 0x22022220, 0x38083038,
+ 0x34c4f0f4, 0x2787a3a4, 0x05454144, 0x0c4c404c, 0x01818180, 0x29c9e1e8, 0x04848084, 0x17879394,
+ 0x35053134, 0x0bcbc3c8, 0x0ecec2cc, 0x3c0c303c, 0x31417170, 0x11011110, 0x07c7c3c4, 0x09898188,
+ 0x35457174, 0x3bcbf3f8, 0x1acad2d8, 0x38c8f0f8, 0x14849094, 0x19495158, 0x02828280, 0x04c4c0c4,
+ 0x3fcff3fc, 0x09494148, 0x39093138, 0x27476364, 0x00c0c0c0, 0x0fcfc3cc, 0x17c7d3d4, 0x3888b0b8,
+ 0x0f0f030c, 0x0e8e828c, 0x02424240, 0x23032320, 0x11819190, 0x2c4c606c, 0x1bcbd3d8, 0x2484a0a4,
+ 0x34043034, 0x31c1f1f0, 0x08484048, 0x02c2c2c0, 0x2f4f636c, 0x3d0d313c, 0x2d0d212c, 0x00404040,
+ 0x3e8eb2bc, 0x3e0e323c, 0x3c8cb0bc, 0x01c1c1c0, 0x2a8aa2a8, 0x3a8ab2b8, 0x0e4e424c, 0x15455154,
+ 0x3b0b3338, 0x1cccd0dc, 0x28486068, 0x3f4f737c, 0x1c8c909c, 0x18c8d0d8, 0x0a4a4248, 0x16465254,
+ 0x37477374, 0x2080a0a0, 0x2dcde1ec, 0x06464244, 0x3585b1b4, 0x2b0b2328, 0x25456164, 0x3acaf2f8,
+ 0x23c3e3e0, 0x3989b1b8, 0x3181b1b0, 0x1f8f939c, 0x1e4e525c, 0x39c9f1f8, 0x26c6e2e4, 0x3282b2b0,
+ 0x31013130, 0x2acae2e8, 0x2d4d616c, 0x1f4f535c, 0x24c4e0e4, 0x30c0f0f0, 0x0dcdc1cc, 0x08888088,
+ 0x16061214, 0x3a0a3238, 0x18485058, 0x14c4d0d4, 0x22426260, 0x29092128, 0x07070304, 0x33033330,
+ 0x28c8e0e8, 0x1b0b1318, 0x05050104, 0x39497178, 0x10809090, 0x2a4a6268, 0x2a0a2228, 0x1a8a9298
+ };
+
+ private static final int[] SS1 =
+ {
+
+ 0x38380830, 0xe828c8e0, 0x2c2d0d21, 0xa42686a2, 0xcc0fcfc3, 0xdc1eced2, 0xb03383b3, 0xb83888b0,
+ 0xac2f8fa3, 0x60204060, 0x54154551, 0xc407c7c3, 0x44044440, 0x6c2f4f63, 0x682b4b63, 0x581b4b53,
+ 0xc003c3c3, 0x60224262, 0x30330333, 0xb43585b1, 0x28290921, 0xa02080a0, 0xe022c2e2, 0xa42787a3,
+ 0xd013c3d3, 0x90118191, 0x10110111, 0x04060602, 0x1c1c0c10, 0xbc3c8cb0, 0x34360632, 0x480b4b43,
+ 0xec2fcfe3, 0x88088880, 0x6c2c4c60, 0xa82888a0, 0x14170713, 0xc404c4c0, 0x14160612, 0xf434c4f0,
+ 0xc002c2c2, 0x44054541, 0xe021c1e1, 0xd416c6d2, 0x3c3f0f33, 0x3c3d0d31, 0x8c0e8e82, 0x98188890,
+ 0x28280820, 0x4c0e4e42, 0xf436c6f2, 0x3c3e0e32, 0xa42585a1, 0xf839c9f1, 0x0c0d0d01, 0xdc1fcfd3,
+ 0xd818c8d0, 0x282b0b23, 0x64264662, 0x783a4a72, 0x24270723, 0x2c2f0f23, 0xf031c1f1, 0x70324272,
+ 0x40024242, 0xd414c4d0, 0x40014141, 0xc000c0c0, 0x70334373, 0x64274763, 0xac2c8ca0, 0x880b8b83,
+ 0xf437c7f3, 0xac2d8da1, 0x80008080, 0x1c1f0f13, 0xc80acac2, 0x2c2c0c20, 0xa82a8aa2, 0x34340430,
+ 0xd012c2d2, 0x080b0b03, 0xec2ecee2, 0xe829c9e1, 0x5c1d4d51, 0x94148490, 0x18180810, 0xf838c8f0,
+ 0x54174753, 0xac2e8ea2, 0x08080800, 0xc405c5c1, 0x10130313, 0xcc0dcdc1, 0x84068682, 0xb83989b1,
+ 0xfc3fcff3, 0x7c3d4d71, 0xc001c1c1, 0x30310131, 0xf435c5f1, 0x880a8a82, 0x682a4a62, 0xb03181b1,
+ 0xd011c1d1, 0x20200020, 0xd417c7d3, 0x00020202, 0x20220222, 0x04040400, 0x68284860, 0x70314171,
+ 0x04070703, 0xd81bcbd3, 0x9c1d8d91, 0x98198991, 0x60214161, 0xbc3e8eb2, 0xe426c6e2, 0x58194951,
+ 0xdc1dcdd1, 0x50114151, 0x90108090, 0xdc1cccd0, 0x981a8a92, 0xa02383a3, 0xa82b8ba3, 0xd010c0d0,
+ 0x80018181, 0x0c0f0f03, 0x44074743, 0x181a0a12, 0xe023c3e3, 0xec2ccce0, 0x8c0d8d81, 0xbc3f8fb3,
+ 0x94168692, 0x783b4b73, 0x5c1c4c50, 0xa02282a2, 0xa02181a1, 0x60234363, 0x20230323, 0x4c0d4d41,
+ 0xc808c8c0, 0x9c1e8e92, 0x9c1c8c90, 0x383a0a32, 0x0c0c0c00, 0x2c2e0e22, 0xb83a8ab2, 0x6c2e4e62,
+ 0x9c1f8f93, 0x581a4a52, 0xf032c2f2, 0x90128292, 0xf033c3f3, 0x48094941, 0x78384870, 0xcc0cccc0,
+ 0x14150511, 0xf83bcbf3, 0x70304070, 0x74354571, 0x7c3f4f73, 0x34350531, 0x10100010, 0x00030303,
+ 0x64244460, 0x6c2d4d61, 0xc406c6c2, 0x74344470, 0xd415c5d1, 0xb43484b0, 0xe82acae2, 0x08090901,
+ 0x74364672, 0x18190911, 0xfc3ecef2, 0x40004040, 0x10120212, 0xe020c0e0, 0xbc3d8db1, 0x04050501,
+ 0xf83acaf2, 0x00010101, 0xf030c0f0, 0x282a0a22, 0x5c1e4e52, 0xa82989a1, 0x54164652, 0x40034343,
+ 0x84058581, 0x14140410, 0x88098981, 0x981b8b93, 0xb03080b0, 0xe425c5e1, 0x48084840, 0x78394971,
+ 0x94178793, 0xfc3cccf0, 0x1c1e0e12, 0x80028282, 0x20210121, 0x8c0c8c80, 0x181b0b13, 0x5c1f4f53,
+ 0x74374773, 0x54144450, 0xb03282b2, 0x1c1d0d11, 0x24250521, 0x4c0f4f43, 0x00000000, 0x44064642,
+ 0xec2dcde1, 0x58184850, 0x50124252, 0xe82bcbe3, 0x7c3e4e72, 0xd81acad2, 0xc809c9c1, 0xfc3dcdf1,
+ 0x30300030, 0x94158591, 0x64254561, 0x3c3c0c30, 0xb43686b2, 0xe424c4e0, 0xb83b8bb3, 0x7c3c4c70,
+ 0x0c0e0e02, 0x50104050, 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393,
+ 0x34370733, 0xe427c7e3, 0x24240420, 0xa42484a0, 0xc80bcbc3, 0x50134353, 0x080a0a02, 0x84078783,
+ 0xd819c9d1, 0x4c0c4c40, 0x80038383, 0x8c0f8f83, 0xcc0ecec2, 0x383b0b33, 0x480a4a42, 0xb43787b3
+ };
+
+ private static final int[] SS2 =
+ {
+
+ 0xa1a82989, 0x81840585, 0xd2d416c6, 0xd3d013c3, 0x50541444, 0x111c1d0d, 0xa0ac2c8c, 0x21242505,
+ 0x515c1d4d, 0x43400343, 0x10181808, 0x121c1e0e, 0x51501141, 0xf0fc3ccc, 0xc2c80aca, 0x63602343,
+ 0x20282808, 0x40440444, 0x20202000, 0x919c1d8d, 0xe0e020c0, 0xe2e022c2, 0xc0c808c8, 0x13141707,
+ 0xa1a42585, 0x838c0f8f, 0x03000303, 0x73783b4b, 0xb3b83b8b, 0x13101303, 0xd2d012c2, 0xe2ec2ece,
+ 0x70703040, 0x808c0c8c, 0x333c3f0f, 0xa0a82888, 0x32303202, 0xd1dc1dcd, 0xf2f436c6, 0x70743444,
+ 0xe0ec2ccc, 0x91941585, 0x03080b0b, 0x53541747, 0x505c1c4c, 0x53581b4b, 0xb1bc3d8d, 0x01000101,
+ 0x20242404, 0x101c1c0c, 0x73703343, 0x90981888, 0x10101000, 0xc0cc0ccc, 0xf2f032c2, 0xd1d819c9,
+ 0x202c2c0c, 0xe3e427c7, 0x72703242, 0x83800383, 0x93981b8b, 0xd1d011c1, 0x82840686, 0xc1c809c9,
+ 0x60602040, 0x50501040, 0xa3a02383, 0xe3e82bcb, 0x010c0d0d, 0xb2b43686, 0x929c1e8e, 0x434c0f4f,
+ 0xb3b43787, 0x52581a4a, 0xc2c406c6, 0x70783848, 0xa2a42686, 0x12101202, 0xa3ac2f8f, 0xd1d415c5,
+ 0x61602141, 0xc3c003c3, 0xb0b43484, 0x41400141, 0x52501242, 0x717c3d4d, 0x818c0d8d, 0x00080808,
+ 0x131c1f0f, 0x91981989, 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xf3f437c7, 0xe1e021c1,
+ 0xf1fc3dcd, 0x72743646, 0x232c2f0f, 0x23242707, 0xb0b03080, 0x83880b8b, 0x020c0e0e, 0xa3a82b8b,
+ 0xa2a02282, 0x626c2e4e, 0x93901383, 0x414c0d4d, 0x61682949, 0x707c3c4c, 0x01080909, 0x02080a0a,
+ 0xb3bc3f8f, 0xe3ec2fcf, 0xf3f033c3, 0xc1c405c5, 0x83840787, 0x10141404, 0xf2fc3ece, 0x60642444,
+ 0xd2dc1ece, 0x222c2e0e, 0x43480b4b, 0x12181a0a, 0x02040606, 0x21202101, 0x63682b4b, 0x62642646,
+ 0x02000202, 0xf1f435c5, 0x92901282, 0x82880a8a, 0x000c0c0c, 0xb3b03383, 0x727c3e4e, 0xd0d010c0,
+ 0x72783a4a, 0x43440747, 0x92941686, 0xe1e425c5, 0x22242606, 0x80800080, 0xa1ac2d8d, 0xd3dc1fcf,
+ 0xa1a02181, 0x30303000, 0x33343707, 0xa2ac2e8e, 0x32343606, 0x11141505, 0x22202202, 0x30383808,
+ 0xf0f434c4, 0xa3a42787, 0x41440545, 0x404c0c4c, 0x81800181, 0xe1e829c9, 0x80840484, 0x93941787,
+ 0x31343505, 0xc3c80bcb, 0xc2cc0ece, 0x303c3c0c, 0x71703141, 0x11101101, 0xc3c407c7, 0x81880989,
+ 0x71743545, 0xf3f83bcb, 0xd2d81aca, 0xf0f838c8, 0x90941484, 0x51581949, 0x82800282, 0xc0c404c4,
+ 0xf3fc3fcf, 0x41480949, 0x31383909, 0x63642747, 0xc0c000c0, 0xc3cc0fcf, 0xd3d417c7, 0xb0b83888,
+ 0x030c0f0f, 0x828c0e8e, 0x42400242, 0x23202303, 0x91901181, 0x606c2c4c, 0xd3d81bcb, 0xa0a42484,
+ 0x30343404, 0xf1f031c1, 0x40480848, 0xc2c002c2, 0x636c2f4f, 0x313c3d0d, 0x212c2d0d, 0x40400040,
+ 0xb2bc3e8e, 0x323c3e0e, 0xb0bc3c8c, 0xc1c001c1, 0xa2a82a8a, 0xb2b83a8a, 0x424c0e4e, 0x51541545,
+ 0x33383b0b, 0xd0dc1ccc, 0x60682848, 0x737c3f4f, 0x909c1c8c, 0xd0d818c8, 0x42480a4a, 0x52541646,
+ 0x73743747, 0xa0a02080, 0xe1ec2dcd, 0x42440646, 0xb1b43585, 0x23282b0b, 0x61642545, 0xf2f83aca,
+ 0xe3e023c3, 0xb1b83989, 0xb1b03181, 0x939c1f8f, 0x525c1e4e, 0xf1f839c9, 0xe2e426c6, 0xb2b03282,
+ 0x31303101, 0xe2e82aca, 0x616c2d4d, 0x535c1f4f, 0xe0e424c4, 0xf0f030c0, 0xc1cc0dcd, 0x80880888,
+ 0x12141606, 0x32383a0a, 0x50581848, 0xd0d414c4, 0x62602242, 0x21282909, 0x03040707, 0x33303303,
+ 0xe0e828c8, 0x13181b0b, 0x01040505, 0x71783949, 0x90901080, 0x62682a4a, 0x22282a0a, 0x92981a8a
+ };
+
+
+ private static final int[] SS3 =
+ {
+
+ 0x08303838, 0xc8e0e828, 0x0d212c2d, 0x86a2a426, 0xcfc3cc0f, 0xced2dc1e, 0x83b3b033, 0x88b0b838,
+ 0x8fa3ac2f, 0x40606020, 0x45515415, 0xc7c3c407, 0x44404404, 0x4f636c2f, 0x4b63682b, 0x4b53581b,
+ 0xc3c3c003, 0x42626022, 0x03333033, 0x85b1b435, 0x09212829, 0x80a0a020, 0xc2e2e022, 0x87a3a427,
+ 0xc3d3d013, 0x81919011, 0x01111011, 0x06020406, 0x0c101c1c, 0x8cb0bc3c, 0x06323436, 0x4b43480b,
+ 0xcfe3ec2f, 0x88808808, 0x4c606c2c, 0x88a0a828, 0x07131417, 0xc4c0c404, 0x06121416, 0xc4f0f434,
+ 0xc2c2c002, 0x45414405, 0xc1e1e021, 0xc6d2d416, 0x0f333c3f, 0x0d313c3d, 0x8e828c0e, 0x88909818,
+ 0x08202828, 0x4e424c0e, 0xc6f2f436, 0x0e323c3e, 0x85a1a425, 0xc9f1f839, 0x0d010c0d, 0xcfd3dc1f,
+ 0xc8d0d818, 0x0b23282b, 0x46626426, 0x4a72783a, 0x07232427, 0x0f232c2f, 0xc1f1f031, 0x42727032,
+ 0x42424002, 0xc4d0d414, 0x41414001, 0xc0c0c000, 0x43737033, 0x47636427, 0x8ca0ac2c, 0x8b83880b,
+ 0xc7f3f437, 0x8da1ac2d, 0x80808000, 0x0f131c1f, 0xcac2c80a, 0x0c202c2c, 0x8aa2a82a, 0x04303434,
+ 0xc2d2d012, 0x0b03080b, 0xcee2ec2e, 0xc9e1e829, 0x4d515c1d, 0x84909414, 0x08101818, 0xc8f0f838,
+ 0x47535417, 0x8ea2ac2e, 0x08000808, 0xc5c1c405, 0x03131013, 0xcdc1cc0d, 0x86828406, 0x89b1b839,
+ 0xcff3fc3f, 0x4d717c3d, 0xc1c1c001, 0x01313031, 0xc5f1f435, 0x8a82880a, 0x4a62682a, 0x81b1b031,
+ 0xc1d1d011, 0x00202020, 0xc7d3d417, 0x02020002, 0x02222022, 0x04000404, 0x48606828, 0x41717031,
+ 0x07030407, 0xcbd3d81b, 0x8d919c1d, 0x89919819, 0x41616021, 0x8eb2bc3e, 0xc6e2e426, 0x49515819,
+ 0xcdd1dc1d, 0x41515011, 0x80909010, 0xccd0dc1c, 0x8a92981a, 0x83a3a023, 0x8ba3a82b, 0xc0d0d010,
+ 0x81818001, 0x0f030c0f, 0x47434407, 0x0a12181a, 0xc3e3e023, 0xcce0ec2c, 0x8d818c0d, 0x8fb3bc3f,
+ 0x86929416, 0x4b73783b, 0x4c505c1c, 0x82a2a022, 0x81a1a021, 0x43636023, 0x03232023, 0x4d414c0d,
+ 0xc8c0c808, 0x8e929c1e, 0x8c909c1c, 0x0a32383a, 0x0c000c0c, 0x0e222c2e, 0x8ab2b83a, 0x4e626c2e,
+ 0x8f939c1f, 0x4a52581a, 0xc2f2f032, 0x82929012, 0xc3f3f033, 0x49414809, 0x48707838, 0xccc0cc0c,
+ 0x05111415, 0xcbf3f83b, 0x40707030, 0x45717435, 0x4f737c3f, 0x05313435, 0x00101010, 0x03030003,
+ 0x44606424, 0x4d616c2d, 0xc6c2c406, 0x44707434, 0xc5d1d415, 0x84b0b434, 0xcae2e82a, 0x09010809,
+ 0x46727436, 0x09111819, 0xcef2fc3e, 0x40404000, 0x02121012, 0xc0e0e020, 0x8db1bc3d, 0x05010405,
+ 0xcaf2f83a, 0x01010001, 0xc0f0f030, 0x0a22282a, 0x4e525c1e, 0x89a1a829, 0x46525416, 0x43434003,
+ 0x85818405, 0x04101414, 0x89818809, 0x8b93981b, 0x80b0b030, 0xc5e1e425, 0x48404808, 0x49717839,
+ 0x87939417, 0xccf0fc3c, 0x0e121c1e, 0x82828002, 0x01212021, 0x8c808c0c, 0x0b13181b, 0x4f535c1f,
+ 0x47737437, 0x44505414, 0x82b2b032, 0x0d111c1d, 0x05212425, 0x4f434c0f, 0x00000000, 0x46424406,
+ 0xcde1ec2d, 0x48505818, 0x42525012, 0xcbe3e82b, 0x4e727c3e, 0xcad2d81a, 0xc9c1c809, 0xcdf1fc3d,
+ 0x00303030, 0x85919415, 0x45616425, 0x0c303c3c, 0x86b2b436, 0xc4e0e424, 0x8bb3b83b, 0x4c707c3c,
+ 0x0e020c0e, 0x40505010, 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013,
+ 0x07333437, 0xc7e3e427, 0x04202424, 0x84a0a424, 0xcbc3c80b, 0x43535013, 0x0a02080a, 0x87838407,
+ 0xc9d1d819, 0x4c404c0c, 0x83838003, 0x8f838c0f, 0xcec2cc0e, 0x0b33383b, 0x4a42480a, 0x87b3b437
+ };
+
+
+ private static final int[] KC =
+ {
+ 0x9e3779b9, 0x3c6ef373, 0x78dde6e6, 0xf1bbcdcc,
+ 0xe3779b99, 0xc6ef3733, 0x8dde6e67, 0x1bbcdccf,
+ 0x3779b99e, 0x6ef3733c, 0xdde6e678, 0xbbcdccf1,
+ 0x779b99e3, 0xef3733c6, 0xde6e678d, 0xbcdccf1b
+ };
+
+ private int[] wKey;
+ private boolean forEncryption;
+
+ public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
+ {
+ this.forEncryption = forEncryption;
+ wKey = createWorkingKey(((KeyParameter)params).getKey());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SEED";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException
+ {
+ if (wKey == null)
+ {
+ throw new IllegalStateException("SEED engine not initialised");
+ }
+
+ if (inOff + BLOCK_SIZE > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if (outOff + BLOCK_SIZE > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ long l = bytesToLong(in, inOff + 0);
+ long r = bytesToLong(in, inOff + 8);
+
+ if (forEncryption)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ long nl = r;
+
+ r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+ l = nl;
+ }
+ }
+ else
+ {
+ for (int i = 15; i >= 0; i--)
+ {
+ long nl = r;
+
+ r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+ l = nl;
+ }
+ }
+
+ longToBytes(out, outOff + 0, r);
+ longToBytes(out, outOff + 8, l);
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ private int[] createWorkingKey(byte[] inKey)
+ {
+ int[] key = new int[32];
+ long lower = bytesToLong(inKey, 0);
+ long upper = bytesToLong(inKey, 8);
+
+ int key0 = extractW0(lower);
+ int key1 = extractW1(lower);
+ int key2 = extractW0(upper);
+ int key3 = extractW1(upper);
+
+ for (int i = 0; i < 16; i++)
+ {
+ key[2 * i] = G(key0 + key2 - KC[i]);
+ key[2 * i + 1] = G(key1 - key3 + KC[i]);
+
+ if (i % 2 == 0)
+ {
+ lower = rotateRight8(lower);
+ key0 = extractW0(lower);
+ key1 = extractW1(lower);
+ }
+ else
+ {
+ upper = rotateLeft8(upper);
+ key2 = extractW0(upper);
+ key3 = extractW1(upper);
+ }
+ }
+
+ return key;
+ }
+
+ private int extractW1(long lVal)
+ {
+ return (int)lVal;
+ }
+
+ private int extractW0(long lVal)
+ {
+ return (int)(lVal >> 32);
+ }
+
+ private long rotateLeft8(long x)
+ {
+ return (x << 8) | (x >>> 56);
+ }
+
+ private long rotateRight8(long x)
+ {
+ return (x >>> 8) | (x << 56);
+ }
+
+ private long bytesToLong(
+ byte[] src,
+ int srcOff)
+ {
+ long word = 0;
+
+ for (int i = 0; i <= 7; i++)
+ {
+ word = (word << 8) + (src[i + srcOff] & 0xff);
+ }
+
+ return word;
+ }
+
+ private void longToBytes(
+ byte[] dest,
+ int destOff,
+ long value)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ dest[i + destOff] = (byte)(value >> ((7 - i) * 8));
+ }
+ }
+
+ private int G(int x)
+ {
+ return SS0[x & 0xff] ^ SS1[(x >> 8) & 0xff] ^ SS2[(x >> 16) & 0xff] ^ SS3[(x >> 24) & 0xff];
+ }
+
+ private long F(int ki0, int ki1, long r)
+ {
+ int r0 = (int)(r >> 32);
+ int r1 = (int)r;
+ int rd1 = phaseCalc2(r0, ki0, r1, ki1);
+ int rd0 = rd1 + phaseCalc1(r0, ki0, r1, ki1);
+
+ return ((long)rd0 << 32) | (rd1 & 0xffffffffL);
+ }
+
+ private int phaseCalc1(int r0, int ki0, int r1, int ki1)
+ {
+ return G(G((r0 ^ ki0) ^ (r1 ^ ki1)) + (r0 ^ ki0));
+ }
+
+ private int phaseCalc2(int r0, int ki0, int r1, int ki1)
+ {
+ return G(phaseCalc1(r0, ki0, r1, ki1) + G((r0 ^ ki0) ^ (r1 ^ ki1)));
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/SEEDWrapEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/SEEDWrapEngine.java
new file mode 100644
index 00000000..2b5213c2
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/SEEDWrapEngine.java
@@ -0,0 +1,15 @@
+package org.spongycastle.crypto.engines;
+
+/**
+ * An implementation of the SEED key wrapper based on RFC 4010/RFC 3394.
+ * <p>
+ * For further details see: <a href="http://www.ietf.org/rfc/rfc4010.txt">http://www.ietf.org/rfc/rfc4010.txt</a>.
+ */
+public class SEEDWrapEngine
+ extends RFC3394WrapEngine
+{
+ public SEEDWrapEngine()
+ {
+ super(new SEEDEngine());
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/Salsa20Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/Salsa20Engine.java
new file mode 100644
index 00000000..ff9953aa
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/Salsa20Engine.java
@@ -0,0 +1,474 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.MaxBytesExceededException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.SkippingStreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.util.Pack;
+import org.spongycastle.util.Strings;
+
+/**
+ * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
+ */
+public class Salsa20Engine
+ implements SkippingStreamCipher
+{
+ public final static int DEFAULT_ROUNDS = 20;
+
+ /** Constants */
+ private final static int STATE_SIZE = 16; // 16, 32 bit ints = 64 bytes
+
+ protected final static byte[]
+ sigma = Strings.toByteArray("expand 32-byte k"),
+ tau = Strings.toByteArray("expand 16-byte k");
+
+ protected int rounds;
+
+ /*
+ * variables to hold the state of the engine
+ * during encryption and decryption
+ */
+ private int index = 0;
+ protected int[] engineState = new int[STATE_SIZE]; // state
+ protected int[] x = new int[STATE_SIZE] ; // internal buffer
+ private byte[] keyStream = new byte[STATE_SIZE * 4]; // expanded state, 64 bytes
+ private boolean initialised = false;
+
+ /*
+ * internal counter
+ */
+ private int cW0, cW1, cW2;
+
+ /**
+ * Creates a 20 round Salsa20 engine.
+ */
+ public Salsa20Engine()
+ {
+ this(DEFAULT_ROUNDS);
+ }
+
+ /**
+ * Creates a Salsa20 engine with a specific number of rounds.
+ * @param rounds the number of rounds (must be an even number).
+ */
+ public Salsa20Engine(int rounds)
+ {
+ if (rounds <= 0 || (rounds & 1) != 0)
+ {
+ throw new IllegalArgumentException("'rounds' must be a positive, even number");
+ }
+
+ this.rounds = rounds;
+ }
+
+ /**
+ * initialise a Salsa20 cipher.
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ /*
+ * Salsa20 encryption and decryption is completely
+ * symmetrical, so the 'forEncryption' is
+ * irrelevant. (Like 90% of stream ciphers)
+ */
+
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV) params;
+
+ byte[] iv = ivParams.getIV();
+ if (iv == null || iv.length != getNonceSize())
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires exactly " + getNonceSize()
+ + " bytes of IV");
+ }
+
+ CipherParameters keyParam = ivParams.getParameters();
+ if (keyParam == null)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName() + " KeyParameter can not be null for first initialisation");
+ }
+
+ setKey(null, iv);
+ }
+ else if (keyParam instanceof KeyParameter)
+ {
+ setKey(((KeyParameter)keyParam).getKey(), iv);
+ }
+ else
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must contain a KeyParameter (or null for re-init)");
+ }
+
+ reset();
+
+ initialised = true;
+ }
+
+ protected int getNonceSize()
+ {
+ return 8;
+ }
+
+ public String getAlgorithmName()
+ {
+ String name = "Salsa20";
+ if (rounds != DEFAULT_ROUNDS)
+ {
+ name += "/" + rounds;
+ }
+ return name;
+ }
+
+ public byte returnByte(byte in)
+ {
+ if (limitExceeded())
+ {
+ throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV");
+ }
+
+ byte out = (byte)(keyStream[index]^in);
+ index = (index + 1) & 63;
+
+ if (index == 0)
+ {
+ advanceCounter();
+ generateKeyStream(keyStream);
+ }
+
+ return out;
+ }
+
+ protected void advanceCounter()
+ {
+ if (++engineState[8] == 0)
+ {
+ ++engineState[9];
+ }
+ }
+
+ protected void retreatCounter()
+ {
+ if (engineState[8] == 0 && engineState[9] == 0)
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+
+ if (--engineState[8] == -1)
+ {
+ --engineState[9];
+ }
+ }
+
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ {
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName() + " not initialised");
+ }
+
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (limitExceeded(len))
+ {
+ throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ out[i + outOff] = (byte)(keyStream[index] ^ in[i + inOff]);
+ index = (index + 1) & 63;
+
+ if (index == 0)
+ {
+ advanceCounter();
+ generateKeyStream(keyStream);
+ }
+ }
+
+ return len;
+ }
+
+ public long skip(long numberOfBytes)
+ {
+ if (numberOfBytes >= 0)
+ {
+ for (long i = 0; i < numberOfBytes; i++)
+ {
+ index = (index + 1) & 63;
+
+ if (index == 0)
+ {
+ advanceCounter();
+ }
+ }
+ }
+ else
+ {
+ for (long i = 0; i > numberOfBytes; i--)
+ {
+ if (index == 0)
+ {
+ retreatCounter();
+ }
+
+ index = (index - 1) & 63;
+ }
+ }
+
+ generateKeyStream(keyStream);
+
+ return numberOfBytes;
+ }
+
+ public long seekTo(long position)
+ {
+ reset();
+
+ return skip(position);
+ }
+
+ public long getPosition()
+ {
+ return getCounter() * 64 + index;
+ }
+
+ public void reset()
+ {
+ index = 0;
+ resetLimitCounter();
+ resetCounter();
+
+ generateKeyStream(keyStream);
+ }
+
+ protected long getCounter()
+ {
+ return ((long)engineState[9] << 32) | (engineState[8] & 0xffffffffL);
+ }
+
+ protected void resetCounter()
+ {
+ engineState[8] = engineState[9] = 0;
+ }
+
+ protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ if (keyBytes != null)
+ {
+ if ((keyBytes.length != 16) && (keyBytes.length != 32))
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+ }
+
+ // Key
+ engineState[1] = Pack.littleEndianToInt(keyBytes, 0);
+ engineState[2] = Pack.littleEndianToInt(keyBytes, 4);
+ engineState[3] = Pack.littleEndianToInt(keyBytes, 8);
+ engineState[4] = Pack.littleEndianToInt(keyBytes, 12);
+
+ byte[] constants;
+ int offset;
+ if (keyBytes.length == 32)
+ {
+ constants = sigma;
+ offset = 16;
+ }
+ else
+ {
+ constants = tau;
+ offset = 0;
+ }
+
+ engineState[11] = Pack.littleEndianToInt(keyBytes, offset);
+ engineState[12] = Pack.littleEndianToInt(keyBytes, offset + 4);
+ engineState[13] = Pack.littleEndianToInt(keyBytes, offset + 8);
+ engineState[14] = Pack.littleEndianToInt(keyBytes, offset + 12);
+
+ engineState[0 ] = Pack.littleEndianToInt(constants, 0);
+ engineState[5 ] = Pack.littleEndianToInt(constants, 4);
+ engineState[10] = Pack.littleEndianToInt(constants, 8);
+ engineState[15] = Pack.littleEndianToInt(constants, 12);
+ }
+
+ // IV
+ engineState[6] = Pack.littleEndianToInt(ivBytes, 0);
+ engineState[7] = Pack.littleEndianToInt(ivBytes, 4);
+ }
+
+ protected void generateKeyStream(byte[] output)
+ {
+ salsaCore(rounds, engineState, x);
+ Pack.intToLittleEndian(x, output, 0);
+ }
+
+ /**
+ * Salsa20 function
+ *
+ * @param input input data
+ */
+ public static void salsaCore(int rounds, int[] input, int[] x)
+ {
+ if (input.length != 16)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (x.length != 16)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (rounds % 2 != 0)
+ {
+ throw new IllegalArgumentException("Number of rounds must be even");
+ }
+
+ int x00 = input[ 0];
+ int x01 = input[ 1];
+ int x02 = input[ 2];
+ int x03 = input[ 3];
+ int x04 = input[ 4];
+ int x05 = input[ 5];
+ int x06 = input[ 6];
+ int x07 = input[ 7];
+ int x08 = input[ 8];
+ int x09 = input[ 9];
+ int x10 = input[10];
+ int x11 = input[11];
+ int x12 = input[12];
+ int x13 = input[13];
+ int x14 = input[14];
+ int x15 = input[15];
+
+ for (int i = rounds; i > 0; i -= 2)
+ {
+ x04 ^= rotl(x00 + x12, 7);
+ x08 ^= rotl(x04 + x00, 9);
+ x12 ^= rotl(x08 + x04, 13);
+ x00 ^= rotl(x12 + x08, 18);
+ x09 ^= rotl(x05 + x01, 7);
+ x13 ^= rotl(x09 + x05, 9);
+ x01 ^= rotl(x13 + x09, 13);
+ x05 ^= rotl(x01 + x13, 18);
+ x14 ^= rotl(x10 + x06, 7);
+ x02 ^= rotl(x14 + x10, 9);
+ x06 ^= rotl(x02 + x14, 13);
+ x10 ^= rotl(x06 + x02, 18);
+ x03 ^= rotl(x15 + x11, 7);
+ x07 ^= rotl(x03 + x15, 9);
+ x11 ^= rotl(x07 + x03, 13);
+ x15 ^= rotl(x11 + x07, 18);
+
+ x01 ^= rotl(x00 + x03, 7);
+ x02 ^= rotl(x01 + x00, 9);
+ x03 ^= rotl(x02 + x01, 13);
+ x00 ^= rotl(x03 + x02, 18);
+ x06 ^= rotl(x05 + x04, 7);
+ x07 ^= rotl(x06 + x05, 9);
+ x04 ^= rotl(x07 + x06, 13);
+ x05 ^= rotl(x04 + x07, 18);
+ x11 ^= rotl(x10 + x09, 7);
+ x08 ^= rotl(x11 + x10, 9);
+ x09 ^= rotl(x08 + x11, 13);
+ x10 ^= rotl(x09 + x08, 18);
+ x12 ^= rotl(x15 + x14, 7);
+ x13 ^= rotl(x12 + x15, 9);
+ x14 ^= rotl(x13 + x12, 13);
+ x15 ^= rotl(x14 + x13, 18);
+ }
+
+ x[ 0] = x00 + input[ 0];
+ x[ 1] = x01 + input[ 1];
+ x[ 2] = x02 + input[ 2];
+ x[ 3] = x03 + input[ 3];
+ x[ 4] = x04 + input[ 4];
+ x[ 5] = x05 + input[ 5];
+ x[ 6] = x06 + input[ 6];
+ x[ 7] = x07 + input[ 7];
+ x[ 8] = x08 + input[ 8];
+ x[ 9] = x09 + input[ 9];
+ x[10] = x10 + input[10];
+ x[11] = x11 + input[11];
+ x[12] = x12 + input[12];
+ x[13] = x13 + input[13];
+ x[14] = x14 + input[14];
+ x[15] = x15 + input[15];
+ }
+
+ /**
+ * Rotate left
+ *
+ * @param x value to rotate
+ * @param y amount to rotate x
+ *
+ * @return rotated x
+ */
+ protected static int rotl(int x, int y)
+ {
+ return (x << y) | (x >>> -y);
+ }
+
+ private void resetLimitCounter()
+ {
+ cW0 = 0;
+ cW1 = 0;
+ cW2 = 0;
+ }
+
+ private boolean limitExceeded()
+ {
+ if (++cW0 == 0)
+ {
+ if (++cW1 == 0)
+ {
+ return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * this relies on the fact len will always be positive.
+ */
+ private boolean limitExceeded(int len)
+ {
+ cW0 += len;
+ if (cW0 < len && cW0 >= 0)
+ {
+ if (++cW1 == 0)
+ {
+ return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6)
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/SerpentEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/SerpentEngine.java
new file mode 100644
index 00000000..944f0ae5
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/SerpentEngine.java
@@ -0,0 +1,783 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * Serpent is a 128-bit 32-round block cipher with variable key lengths,
+ * including 128, 192 and 256 bit keys conjectured to be at least as
+ * secure as three-key triple-DES.
+ * <p>
+ * Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a
+ * candidate algorithm for the NIST AES Quest.
+ * <p>
+ * For full details see the <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a>
+ */
+public class SerpentEngine
+ implements BlockCipher
+{
+ private static final int BLOCK_SIZE = 16;
+
+ static final int ROUNDS = 32;
+ static final int PHI = 0x9E3779B9; // (sqrt(5) - 1) * 2**31
+
+ private boolean encrypting;
+ private int[] wKey;
+
+ private int X0, X1, X2, X3; // registers
+
+ /**
+ * initialise a Serpent cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.wKey = makeWorkingKey(((KeyParameter)params).getKey());
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Serpent init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Serpent";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public final int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (wKey == null)
+ {
+ throw new IllegalStateException("Serpent not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Expand a user-supplied key material into a session key.
+ *
+ * @param key The user-key bytes (multiples of 4) to use.
+ * @exception IllegalArgumentException
+ */
+ private int[] makeWorkingKey(
+ byte[] key)
+ throws IllegalArgumentException
+ {
+ //
+ // pad key to 256 bits
+ //
+ int[] kPad = new int[16];
+ int off = 0;
+ int length = 0;
+
+ for (off = key.length - 4; off > 0; off -= 4)
+ {
+ kPad[length++] = bytesToWord(key, off);
+ }
+
+ if (off == 0)
+ {
+ kPad[length++] = bytesToWord(key, 0);
+ if (length < 8)
+ {
+ kPad[length] = 1;
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("key must be a multiple of 4 bytes");
+ }
+
+ //
+ // expand the padded key up to 33 x 128 bits of key material
+ //
+ int amount = (ROUNDS + 1) * 4;
+ int[] w = new int[amount];
+
+ //
+ // compute w0 to w7 from w-8 to w-1
+ //
+ for (int i = 8; i < 16; i++)
+ {
+ kPad[i] = rotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11);
+ }
+
+ System.arraycopy(kPad, 8, w, 0, 8);
+
+ //
+ // compute w8 to w136
+ //
+ for (int i = 8; i < amount; i++)
+ {
+ w[i] = rotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11);
+ }
+
+ //
+ // create the working keys by processing w with the Sbox and IP
+ //
+ sb3(w[0], w[1], w[2], w[3]);
+ w[0] = X0; w[1] = X1; w[2] = X2; w[3] = X3;
+ sb2(w[4], w[5], w[6], w[7]);
+ w[4] = X0; w[5] = X1; w[6] = X2; w[7] = X3;
+ sb1(w[8], w[9], w[10], w[11]);
+ w[8] = X0; w[9] = X1; w[10] = X2; w[11] = X3;
+ sb0(w[12], w[13], w[14], w[15]);
+ w[12] = X0; w[13] = X1; w[14] = X2; w[15] = X3;
+ sb7(w[16], w[17], w[18], w[19]);
+ w[16] = X0; w[17] = X1; w[18] = X2; w[19] = X3;
+ sb6(w[20], w[21], w[22], w[23]);
+ w[20] = X0; w[21] = X1; w[22] = X2; w[23] = X3;
+ sb5(w[24], w[25], w[26], w[27]);
+ w[24] = X0; w[25] = X1; w[26] = X2; w[27] = X3;
+ sb4(w[28], w[29], w[30], w[31]);
+ w[28] = X0; w[29] = X1; w[30] = X2; w[31] = X3;
+ sb3(w[32], w[33], w[34], w[35]);
+ w[32] = X0; w[33] = X1; w[34] = X2; w[35] = X3;
+ sb2(w[36], w[37], w[38], w[39]);
+ w[36] = X0; w[37] = X1; w[38] = X2; w[39] = X3;
+ sb1(w[40], w[41], w[42], w[43]);
+ w[40] = X0; w[41] = X1; w[42] = X2; w[43] = X3;
+ sb0(w[44], w[45], w[46], w[47]);
+ w[44] = X0; w[45] = X1; w[46] = X2; w[47] = X3;
+ sb7(w[48], w[49], w[50], w[51]);
+ w[48] = X0; w[49] = X1; w[50] = X2; w[51] = X3;
+ sb6(w[52], w[53], w[54], w[55]);
+ w[52] = X0; w[53] = X1; w[54] = X2; w[55] = X3;
+ sb5(w[56], w[57], w[58], w[59]);
+ w[56] = X0; w[57] = X1; w[58] = X2; w[59] = X3;
+ sb4(w[60], w[61], w[62], w[63]);
+ w[60] = X0; w[61] = X1; w[62] = X2; w[63] = X3;
+ sb3(w[64], w[65], w[66], w[67]);
+ w[64] = X0; w[65] = X1; w[66] = X2; w[67] = X3;
+ sb2(w[68], w[69], w[70], w[71]);
+ w[68] = X0; w[69] = X1; w[70] = X2; w[71] = X3;
+ sb1(w[72], w[73], w[74], w[75]);
+ w[72] = X0; w[73] = X1; w[74] = X2; w[75] = X3;
+ sb0(w[76], w[77], w[78], w[79]);
+ w[76] = X0; w[77] = X1; w[78] = X2; w[79] = X3;
+ sb7(w[80], w[81], w[82], w[83]);
+ w[80] = X0; w[81] = X1; w[82] = X2; w[83] = X3;
+ sb6(w[84], w[85], w[86], w[87]);
+ w[84] = X0; w[85] = X1; w[86] = X2; w[87] = X3;
+ sb5(w[88], w[89], w[90], w[91]);
+ w[88] = X0; w[89] = X1; w[90] = X2; w[91] = X3;
+ sb4(w[92], w[93], w[94], w[95]);
+ w[92] = X0; w[93] = X1; w[94] = X2; w[95] = X3;
+ sb3(w[96], w[97], w[98], w[99]);
+ w[96] = X0; w[97] = X1; w[98] = X2; w[99] = X3;
+ sb2(w[100], w[101], w[102], w[103]);
+ w[100] = X0; w[101] = X1; w[102] = X2; w[103] = X3;
+ sb1(w[104], w[105], w[106], w[107]);
+ w[104] = X0; w[105] = X1; w[106] = X2; w[107] = X3;
+ sb0(w[108], w[109], w[110], w[111]);
+ w[108] = X0; w[109] = X1; w[110] = X2; w[111] = X3;
+ sb7(w[112], w[113], w[114], w[115]);
+ w[112] = X0; w[113] = X1; w[114] = X2; w[115] = X3;
+ sb6(w[116], w[117], w[118], w[119]);
+ w[116] = X0; w[117] = X1; w[118] = X2; w[119] = X3;
+ sb5(w[120], w[121], w[122], w[123]);
+ w[120] = X0; w[121] = X1; w[122] = X2; w[123] = X3;
+ sb4(w[124], w[125], w[126], w[127]);
+ w[124] = X0; w[125] = X1; w[126] = X2; w[127] = X3;
+ sb3(w[128], w[129], w[130], w[131]);
+ w[128] = X0; w[129] = X1; w[130] = X2; w[131] = X3;
+
+ return w;
+ }
+
+ private int rotateLeft(
+ int x,
+ int bits)
+ {
+ return (x << bits) | (x >>> -bits);
+ }
+
+ private int rotateRight(
+ int x,
+ int bits)
+ {
+ return (x >>> bits) | (x << -bits);
+ }
+
+ private int bytesToWord(
+ byte[] src,
+ int srcOff)
+ {
+ return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) |
+ ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff)));
+ }
+
+ private void wordToBytes(
+ int word,
+ byte[] dst,
+ int dstOff)
+ {
+ dst[dstOff + 3] = (byte)(word);
+ dst[dstOff + 2] = (byte)(word >>> 8);
+ dst[dstOff + 1] = (byte)(word >>> 16);
+ dst[dstOff] = (byte)(word >>> 24);
+ }
+
+ /**
+ * Encrypt one block of plaintext.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ private void encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ X3 = bytesToWord(in, inOff);
+ X2 = bytesToWord(in, inOff + 4);
+ X1 = bytesToWord(in, inOff + 8);
+ X0 = bytesToWord(in, inOff + 12);
+
+ sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT();
+ sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT();
+ sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT();
+ sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT();
+ sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT();
+ sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT();
+ sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT();
+ sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT();
+ sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT();
+ sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT();
+ sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT();
+ sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT();
+ sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT();
+ sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT();
+ sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT();
+ sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT();
+ sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT();
+ sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT();
+ sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT();
+ sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT();
+ sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT();
+ sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT();
+ sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT();
+ sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT();
+ sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT();
+ sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT();
+ sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT();
+ sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT();
+ sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT();
+ sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT();
+ sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT();
+ sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3);
+
+ wordToBytes(wKey[131] ^ X3, out, outOff);
+ wordToBytes(wKey[130] ^ X2, out, outOff + 4);
+ wordToBytes(wKey[129] ^ X1, out, outOff + 8);
+ wordToBytes(wKey[128] ^ X0, out, outOff + 12);
+ }
+
+ /**
+ * Decrypt one block of ciphertext.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ */
+ private void decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ X3 = wKey[131] ^ bytesToWord(in, inOff);
+ X2 = wKey[130] ^ bytesToWord(in, inOff + 4);
+ X1 = wKey[129] ^ bytesToWord(in, inOff + 8);
+ X0 = wKey[128] ^ bytesToWord(in, inOff + 12);
+
+ ib7(X0, X1, X2, X3);
+ X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39];
+ inverseLT(); ib0(X0, X1, X2, X3);
+ X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35];
+ inverseLT(); ib7(X0, X1, X2, X3);
+ X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31];
+ inverseLT(); ib6(X0, X1, X2, X3);
+ X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27];
+ inverseLT(); ib5(X0, X1, X2, X3);
+ X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23];
+ inverseLT(); ib4(X0, X1, X2, X3);
+ X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19];
+ inverseLT(); ib3(X0, X1, X2, X3);
+ X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15];
+ inverseLT(); ib2(X0, X1, X2, X3);
+ X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11];
+ inverseLT(); ib1(X0, X1, X2, X3);
+ X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7];
+ inverseLT(); ib0(X0, X1, X2, X3);
+
+ wordToBytes(X3 ^ wKey[3], out, outOff);
+ wordToBytes(X2 ^ wKey[2], out, outOff + 4);
+ wordToBytes(X1 ^ wKey[1], out, outOff + 8);
+ wordToBytes(X0 ^ wKey[0], out, outOff + 12);
+ }
+
+ /**
+ * The sboxes below are based on the work of Brian Gladman and
+ * Sam Simpson, whose original notice appears below.
+ * <p>
+ * For further details see:
+ * http://fp.gladman.plus.com/cryptography_technology/serpent/
+ */
+
+ /* Partially optimised Serpent S Box boolean functions derived */
+ /* using a recursive descent analyser but without a full search */
+ /* of all subtrees. This set of S boxes is the result of work */
+ /* by Sam Simpson and Brian Gladman using the spare time on a */
+ /* cluster of high capacity servers to search for S boxes with */
+ /* this customised search engine. There are now an average of */
+ /* 15.375 terms per S box. */
+ /* */
+ /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */
+ /* and Sam Simpson (s.simpson@mia.co.uk) */
+ /* 17th December 1998 */
+ /* */
+ /* We hereby give permission for information in this file to be */
+ /* used freely subject only to acknowledgement of its origin. */
+
+ /**
+ * S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms.
+ */
+ private void sb0(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t3 = c ^ t1;
+ int t4 = b ^ t3;
+ X3 = (a & d) ^ t4;
+ int t7 = a ^ (b & t1);
+ X2 = t4 ^ (c | t7);
+ int t12 = X3 & (t3 ^ t7);
+ X1 = (~t3) ^ t12;
+ X0 = t12 ^ (~t7);
+ }
+
+ /**
+ * InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms.
+ */
+ private void ib0(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t4 = d ^ (t1 | t2);
+ int t5 = c ^ t4;
+ X2 = t2 ^ t5;
+ int t8 = t1 ^ (d & t2);
+ X1 = t4 ^ (X2 & t8);
+ X3 = (a & t4) ^ (t5 | X1);
+ X0 = X3 ^ (t5 ^ t8);
+ }
+
+ /**
+ * S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms.
+ */
+ private void sb1(int a, int b, int c, int d)
+ {
+ int t2 = b ^ (~a);
+ int t5 = c ^ (a | t2);
+ X2 = d ^ t5;
+ int t7 = b ^ (d | t2);
+ int t8 = t2 ^ X2;
+ X3 = t8 ^ (t5 & t7);
+ int t11 = t5 ^ t7;
+ X1 = X3 ^ t11;
+ X0 = t5 ^ (t8 & t11);
+ }
+
+ /**
+ * InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps.
+ */
+ private void ib1(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t3 = a ^ (b & t1);
+ int t4 = t1 ^ t3;
+ X3 = c ^ t4;
+ int t7 = b ^ (t1 & t3);
+ int t8 = X3 | t7;
+ X1 = t3 ^ t8;
+ int t10 = ~X1;
+ int t11 = X3 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = t4 ^ (t10 | t11);
+ }
+
+ /**
+ * S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms.
+ */
+ private void sb2(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = b ^ d;
+ int t3 = c & t1;
+ X0 = t2 ^ t3;
+ int t5 = c ^ t1;
+ int t6 = c ^ X0;
+ int t7 = b & t6;
+ X3 = t5 ^ t7;
+ X2 = a ^ ((d | t7) & (X0 | t5));
+ X1 = (t2 ^ X3) ^ (X2 ^ (d | t1));
+ }
+
+ /**
+ * InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps.
+ */
+ private void ib2(int a, int b, int c, int d)
+ {
+ int t1 = b ^ d;
+ int t2 = ~t1;
+ int t3 = a ^ c;
+ int t4 = c ^ t1;
+ int t5 = b & t4;
+ X0 = t3 ^ t5;
+ int t7 = a | t2;
+ int t8 = d ^ t7;
+ int t9 = t3 | t8;
+ X3 = t1 ^ t9;
+ int t11 = ~t4;
+ int t12 = X0 | X3;
+ X1 = t11 ^ t12;
+ X2 = (d & t11) ^ (t3 ^ t12);
+ }
+
+ /**
+ * S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms.
+ */
+ private void sb3(int a, int b, int c, int d)
+ {
+ int t1 = a ^ b;
+ int t2 = a & c;
+ int t3 = a | d;
+ int t4 = c ^ d;
+ int t5 = t1 & t3;
+ int t6 = t2 | t5;
+ X2 = t4 ^ t6;
+ int t8 = b ^ t3;
+ int t9 = t6 ^ t8;
+ int t10 = t4 & t9;
+ X0 = t1 ^ t10;
+ int t12 = X2 & X0;
+ X1 = t9 ^ t12;
+ X3 = (b | d) ^ (t4 ^ t12);
+ }
+
+ /**
+ * InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms
+ */
+ private void ib3(int a, int b, int c, int d)
+ {
+ int t1 = a | b;
+ int t2 = b ^ c;
+ int t3 = b & t2;
+ int t4 = a ^ t3;
+ int t5 = c ^ t4;
+ int t6 = d | t4;
+ X0 = t2 ^ t6;
+ int t8 = t2 | t6;
+ int t9 = d ^ t8;
+ X2 = t5 ^ t9;
+ int t11 = t1 ^ t9;
+ int t12 = X0 & t11;
+ X3 = t4 ^ t12;
+ X1 = X3 ^ (X0 ^ t11);
+ }
+
+ /**
+ * S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms.
+ */
+ private void sb4(int a, int b, int c, int d)
+ {
+ int t1 = a ^ d;
+ int t2 = d & t1;
+ int t3 = c ^ t2;
+ int t4 = b | t3;
+ X3 = t1 ^ t4;
+ int t6 = ~b;
+ int t7 = t1 | t6;
+ X0 = t3 ^ t7;
+ int t9 = a & X0;
+ int t10 = t1 ^ t6;
+ int t11 = t4 & t10;
+ X2 = t9 ^ t11;
+ X1 = (a ^ t3) ^ (t10 & X2);
+ }
+
+ /**
+ * InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms.
+ */
+ private void ib4(int a, int b, int c, int d)
+ {
+ int t1 = c | d;
+ int t2 = a & t1;
+ int t3 = b ^ t2;
+ int t4 = a & t3;
+ int t5 = c ^ t4;
+ X1 = d ^ t5;
+ int t7 = ~a;
+ int t8 = t5 & X1;
+ X3 = t3 ^ t8;
+ int t10 = X1 | t7;
+ int t11 = d ^ t10;
+ X0 = X3 ^ t11;
+ X2 = (t3 & t11) ^ (X1 ^ t7);
+ }
+
+ /**
+ * S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms.
+ */
+ private void sb5(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = a ^ d;
+ int t4 = c ^ t1;
+ int t5 = t2 | t3;
+ X0 = t4 ^ t5;
+ int t7 = d & X0;
+ int t8 = t2 ^ X0;
+ X1 = t7 ^ t8;
+ int t10 = t1 | X0;
+ int t11 = t2 | t7;
+ int t12 = t3 ^ t10;
+ X2 = t11 ^ t12;
+ X3 = (b ^ t7) ^ (X1 & t12);
+ }
+
+ /**
+ * InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms.
+ */
+ private void ib5(int a, int b, int c, int d)
+ {
+ int t1 = ~c;
+ int t2 = b & t1;
+ int t3 = d ^ t2;
+ int t4 = a & t3;
+ int t5 = b ^ t1;
+ X3 = t4 ^ t5;
+ int t7 = b | X3;
+ int t8 = a & t7;
+ X1 = t3 ^ t8;
+ int t10 = a | d;
+ int t11 = t1 ^ t7;
+ X0 = t10 ^ t11;
+ X2 = (b & t10) ^ (t4 | (a ^ c));
+ }
+
+ /**
+ * S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms.
+ */
+ private void sb6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ d;
+ int t3 = b ^ t2;
+ int t4 = t1 | t2;
+ int t5 = c ^ t4;
+ X1 = b ^ t5;
+ int t7 = t2 | X1;
+ int t8 = d ^ t7;
+ int t9 = t5 & t8;
+ X2 = t3 ^ t9;
+ int t11 = t5 ^ t8;
+ X0 = X2 ^ t11;
+ X3 = (~t5) ^ (t3 & t11);
+ }
+
+ /**
+ * InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms.
+ */
+ private void ib6(int a, int b, int c, int d)
+ {
+ int t1 = ~a;
+ int t2 = a ^ b;
+ int t3 = c ^ t2;
+ int t4 = c | t1;
+ int t5 = d ^ t4;
+ X1 = t3 ^ t5;
+ int t7 = t3 & t5;
+ int t8 = t2 ^ t7;
+ int t9 = b | t8;
+ X3 = t5 ^ t9;
+ int t11 = b | X3;
+ X0 = t8 ^ t11;
+ X2 = (d & t1) ^ (t3 ^ t11);
+ }
+
+ /**
+ * S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms.
+ */
+ private void sb7(int a, int b, int c, int d)
+ {
+ int t1 = b ^ c;
+ int t2 = c & t1;
+ int t3 = d ^ t2;
+ int t4 = a ^ t3;
+ int t5 = d | t1;
+ int t6 = t4 & t5;
+ X1 = b ^ t6;
+ int t8 = t3 | X1;
+ int t9 = a & t4;
+ X3 = t1 ^ t9;
+ int t11 = t4 ^ t8;
+ int t12 = X3 & t11;
+ X2 = t3 ^ t12;
+ X0 = (~t11) ^ (X3 & X2);
+ }
+
+ /**
+ * InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms.
+ */
+ private void ib7(int a, int b, int c, int d)
+ {
+ int t3 = c | (a & b);
+ int t4 = d & (a | b);
+ X3 = t3 ^ t4;
+ int t6 = ~d;
+ int t7 = b ^ t4;
+ int t9 = t7 | (X3 ^ t6);
+ X1 = a ^ t9;
+ X0 = (c ^ t7) ^ (d | X1);
+ X2 = (t3 ^ X1) ^ (X0 ^ (a & X3));
+ }
+
+ /**
+ * Apply the linear transformation to the register set.
+ */
+ private void LT()
+ {
+ int x0 = rotateLeft(X0, 13);
+ int x2 = rotateLeft(X2, 3);
+ int x1 = X1 ^ x0 ^ x2 ;
+ int x3 = X3 ^ x2 ^ x0 << 3;
+
+ X1 = rotateLeft(x1, 1);
+ X3 = rotateLeft(x3, 7);
+ X0 = rotateLeft(x0 ^ X1 ^ X3, 5);
+ X2 = rotateLeft(x2 ^ X3 ^ (X1 << 7), 22);
+ }
+
+ /**
+ * Apply the inverse of the linear transformation to the register set.
+ */
+ private void inverseLT()
+ {
+ int x2 = rotateRight(X2, 22) ^ X3 ^ (X1 << 7);
+ int x0 = rotateRight(X0, 5) ^ X1 ^ X3;
+ int x3 = rotateRight(X3, 7);
+ int x1 = rotateRight(X1, 1);
+ X3 = x3 ^ x2 ^ x0 << 3;
+ X1 = x1 ^ x0 ^ x2;
+ X2 = rotateRight(x2, 3);
+ X0 = rotateRight(x0, 13);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/Shacal2Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/Shacal2Engine.java
new file mode 100644
index 00000000..8b461f86
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/Shacal2Engine.java
@@ -0,0 +1,201 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * Block cipher Shacal2, designed by Helena Handschuh and David Naccache,
+ * based on hash function SHA-256,
+ * using SHA-256-Initialization-Values as data and SHA-256-Data as key.
+ * <p>
+ * A description of Shacal can be found at:
+ * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.3.4066
+ * Best known cryptanalytic (Wikipedia 11.2013):
+ * Related-key rectangle attack on 44-rounds (Jiqiang Lu, Jongsung Kim).
+ * Comments are related to SHA-256-Naming as described in FIPS PUB 180-2
+ * </p>
+ */
+public class Shacal2Engine
+ implements BlockCipher
+{
+ private final static int[] K = { // SHA-256-Constants
+ 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
+ };
+
+ private static final int BLOCK_SIZE = 32;
+ private boolean forEncryption = false;
+ private static final int ROUNDS = 64;
+
+ private int[] workingKey = null; // expanded key: corresponds to the message block W in FIPS PUB 180-2
+
+ public Shacal2Engine()
+ {
+ }
+
+ public void reset()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Shacal2";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void init(boolean _forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("only simple KeyParameter expected.");
+ }
+ this.forEncryption = _forEncryption;
+ workingKey = new int[64];
+ setKey( ((KeyParameter)params).getKey() );
+ }
+
+ public void setKey(byte[] kb)
+ {
+ if (kb.length == 0 || kb.length > 64 || kb.length < 16 || kb.length % 8 != 0)
+ {
+ throw new IllegalArgumentException("Shacal2-key must be 16 - 64 bytes and multiple of 8");
+ }
+
+ bytes2ints(kb, workingKey, 0, 0);
+
+ for ( int i = 16; i < 64; i++)
+ { // Key-Expansion, implicitly Zero-Padding for 16 > i > kb.length/4
+ workingKey[i] =
+ ( (workingKey[i-2] >>> 17 | workingKey[i-2] << -17) // corresponds to ROTL n(x) of FIPS PUB 180-2
+ ^ (workingKey[i-2] >>> 19 | workingKey[i-2] << -19)
+ ^ (workingKey[i-2] >>> 10) ) // corresponds to sigma1(x)-Function of FIPS PUB 180-2
+ + workingKey[i-7]
+ + ( (workingKey[i-15] >>> 7 | workingKey[i-15] << -7)
+ ^ (workingKey[i-15] >>> 18 | workingKey[i-15] << -18)
+ ^ (workingKey[i-15] >>> 3) ) // corresponds to sigma0(x)-Function of FIPS PUB 180-2
+ + workingKey[i-16];
+ }
+ }
+
+ public void encryptBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ {
+ int[] block = new int[BLOCK_SIZE / 4];// corresponds to working variables a,b,c,d,e,f,g,h of FIPS PUB 180-2
+ bytes2ints(in, block, inOffset, 0);
+
+ for (int i = 0; i < ROUNDS; i++)
+ {
+ int tmp =
+ (((block[4] >>> 6) | (block[4] << -6))
+ ^ ((block[4] >>> 11) | (block[4] << -11))
+ ^ ((block[4] >>> 25) | (block[4] << -25)))
+ + ((block[4] & block[5]) ^ ((~block[4]) & block[6]))
+ + block[7] + K[i] + workingKey[i]; // corresponds to T1 of FIPS PUB 180-2
+ block[7] = block[6];
+ block[6] = block[5];
+ block[5] = block[4];
+ block[4] = block[3] + tmp;
+ block[3] = block[2];
+ block[2] = block[1];
+ block[1] = block[0];
+ block[0] = tmp
+ + (((block[0] >>> 2) | (block[0] << -2))
+ ^ ((block[0] >>> 13) | (block[0] << -13))
+ ^ ((block[0] >>> 22) | (block[0] << -22)))
+ + ((block[0] & block[2]) ^ (block[0] & block[3]) ^ (block[2] & block[3]));
+ //corresponds to T2 of FIPS PUB 180-2, block[1] and block[2] replaced
+ }
+ ints2bytes(block, out, outOffset);
+ }
+
+ public void decryptBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ {
+ int[] block = new int[BLOCK_SIZE / 4];
+ bytes2ints(in, block, inOffset, 0);
+ for (int i = ROUNDS - 1; i >-1; i--)
+ {
+ int tmp = block[0] - (((block[1] >>> 2) | (block[1] << -2))
+ ^ ((block[1] >>> 13) | (block[1] << -13))
+ ^ ((block[1] >>> 22) | (block[1] << -22)))
+ - ((block[1] & block[2]) ^ (block[1] & block[3]) ^ (block[2] & block[3])); // T2
+ block[0] = block[1];
+ block[1] = block[2];
+ block[2] = block[3];
+ block[3] = block[4] - tmp;
+ block[4] = block[5];
+ block[5] = block[6];
+ block[6] = block[7];
+ block[7] = tmp - K[i] - workingKey[i]
+ - (((block[4] >>> 6) | (block[4] << -6))
+ ^ ((block[4] >>> 11) | (block[4] << -11))
+ ^ ((block[4] >>> 25) | (block[4] << -25)))
+ - ((block[4] & block[5]) ^ ((~block[4]) & block[6])); // T1
+ }
+ ints2bytes(block, out, outOffset);
+ }
+
+ public int processBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ throws DataLengthException, IllegalStateException
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Shacal2 not initialised");
+ }
+
+ if ((inOffset + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOffset + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ encryptBlock(in, inOffset, out, outOffset);
+ }
+ else
+ {
+ decryptBlock(in, inOffset, out, outOffset);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ private void bytes2ints(byte[] bytes, int[] block, int bytesPos, int blockPos)
+ {
+ for (int i = blockPos; i < bytes.length / 4; i++)
+ {
+ block[i] = ((bytes[bytesPos++] & 0xFF) << 24)
+ | ((bytes[bytesPos++] & 0xFF) << 16)
+ | ((bytes[bytesPos++] & 0xFF) << 8)
+ | (bytes[bytesPos++] & 0xFF);
+ }
+ }
+
+ private void ints2bytes(int[] block, byte[] out, int pos)
+ {
+ for (int i = 0; i < block.length; i++)
+ {
+ out[pos++] = (byte)(block[i] >>> 24);
+ out[pos++] = (byte)(block[i] >>> 16);
+ out[pos++] = (byte)(block[i] >>> 8);
+ out[pos++] = (byte)block[i];
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/SkipjackEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/SkipjackEngine.java
new file mode 100644
index 00000000..4f7a5f6b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/SkipjackEngine.java
@@ -0,0 +1,260 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * a class that provides a basic SKIPJACK engine.
+ */
+public class SkipjackEngine
+ implements BlockCipher
+{
+ static final int BLOCK_SIZE = 8;
+
+ static short ftable[] =
+ {
+ 0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
+ 0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
+ 0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53,
+ 0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2,
+ 0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8,
+ 0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90,
+ 0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76,
+ 0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d,
+ 0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18,
+ 0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4,
+ 0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40,
+ 0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5,
+ 0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2,
+ 0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8,
+ 0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac,
+ 0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46
+ };
+
+ private int[] key0, key1, key2, key3;
+ private boolean encrypting;
+
+ /**
+ * initialise a SKIPJACK cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to SKIPJACK init - " + params.getClass().getName());
+ }
+
+ byte[] keyBytes = ((KeyParameter)params).getKey();
+
+ this.encrypting = encrypting;
+ this.key0 = new int[32];
+ this.key1 = new int[32];
+ this.key2 = new int[32];
+ this.key3 = new int[32];
+
+ //
+ // expand the key to 128 bytes in 4 parts (saving us a modulo, multiply
+ // and an addition).
+ //
+ for (int i = 0; i < 32; i ++)
+ {
+ key0[i] = keyBytes[(i * 4) % 10] & 0xff;
+ key1[i] = keyBytes[(i * 4 + 1) % 10] & 0xff;
+ key2[i] = keyBytes[(i * 4 + 2) % 10] & 0xff;
+ key3[i] = keyBytes[(i * 4 + 3) % 10] & 0xff;
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SKIPJACK";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (key1 == null)
+ {
+ throw new IllegalStateException("SKIPJACK engine not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * The G permutation
+ */
+ private int g(
+ int k,
+ int w)
+ {
+ int g1, g2, g3, g4, g5, g6;
+
+ g1 = (w >> 8) & 0xff;
+ g2 = w & 0xff;
+
+ g3 = ftable[g2 ^ key0[k]] ^ g1;
+ g4 = ftable[g3 ^ key1[k]] ^ g2;
+ g5 = ftable[g4 ^ key2[k]] ^ g3;
+ g6 = ftable[g5 ^ key3[k]] ^ g4;
+
+ return ((g5 << 8) + g6);
+ }
+
+ public int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int w1 = (in[inOff + 0] << 8) + (in[inOff + 1] & 0xff);
+ int w2 = (in[inOff + 2] << 8) + (in[inOff + 3] & 0xff);
+ int w3 = (in[inOff + 4] << 8) + (in[inOff + 5] & 0xff);
+ int w4 = (in[inOff + 6] << 8) + (in[inOff + 7] & 0xff);
+
+ int k = 0;
+
+ for (int t = 0; t < 2; t++)
+ {
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w2;
+ w2 = g(k, w1);
+ w1 = w2 ^ tmp ^ (k + 1);
+ k++;
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w1 ^ w2 ^ (k + 1);
+ w2 = g(k, w1);
+ w1 = tmp;
+ k++;
+ }
+ }
+
+ out[outOff + 0] = (byte)((w1 >> 8));
+ out[outOff + 1] = (byte)(w1);
+ out[outOff + 2] = (byte)((w2 >> 8));
+ out[outOff + 3] = (byte)(w2);
+ out[outOff + 4] = (byte)((w3 >> 8));
+ out[outOff + 5] = (byte)(w3);
+ out[outOff + 6] = (byte)((w4 >> 8));
+ out[outOff + 7] = (byte)(w4);
+
+ return BLOCK_SIZE;
+ }
+
+ /**
+ * the inverse of the G permutation.
+ */
+ private int h(
+ int k,
+ int w)
+ {
+ int h1, h2, h3, h4, h5, h6;
+
+ h1 = w & 0xff;
+ h2 = (w >> 8) & 0xff;
+
+ h3 = ftable[h2 ^ key3[k]] ^ h1;
+ h4 = ftable[h3 ^ key2[k]] ^ h2;
+ h5 = ftable[h4 ^ key1[k]] ^ h3;
+ h6 = ftable[h5 ^ key0[k]] ^ h4;
+
+ return ((h6 << 8) + h5);
+ }
+
+ public int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ int w2 = (in[inOff + 0] << 8) + (in[inOff + 1] & 0xff);
+ int w1 = (in[inOff + 2] << 8) + (in[inOff + 3] & 0xff);
+ int w4 = (in[inOff + 4] << 8) + (in[inOff + 5] & 0xff);
+ int w3 = (in[inOff + 6] << 8) + (in[inOff + 7] & 0xff);
+
+ int k = 31;
+
+ for (int t = 0; t < 2; t++)
+ {
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w2;
+ w2 = h(k, w1);
+ w1 = w2 ^ tmp ^ (k + 1);
+ k--;
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ int tmp = w4;
+ w4 = w3;
+ w3 = w1 ^ w2 ^ (k + 1);
+ w2 = h(k, w1);
+ w1 = tmp;
+ k--;
+ }
+ }
+
+ out[outOff + 0] = (byte)((w2 >> 8));
+ out[outOff + 1] = (byte)(w2);
+ out[outOff + 2] = (byte)((w1 >> 8));
+ out[outOff + 3] = (byte)(w1);
+ out[outOff + 4] = (byte)((w4 >> 8));
+ out[outOff + 5] = (byte)(w4);
+ out[outOff + 6] = (byte)((w3 >> 8));
+ out[outOff + 7] = (byte)(w3);
+
+ return BLOCK_SIZE;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/TEAEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/TEAEngine.java
new file mode 100644
index 00000000..a027edc9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/TEAEngine.java
@@ -0,0 +1,184 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * An TEA engine.
+ */
+public class TEAEngine
+ implements BlockCipher
+{
+ private static final int rounds = 32,
+ block_size = 8,
+// key_size = 16,
+ delta = 0x9E3779B9,
+ d_sum = 0xC6EF3720; // sum on decrypt
+ /*
+ * the expanded key array of 4 subkeys
+ */
+ private int _a, _b, _c, _d;
+ private boolean _initialised;
+ private boolean _forEncryption;
+
+ /**
+ * Create an instance of the TEA encryption algorithm
+ * and set some defaults
+ */
+ public TEAEngine()
+ {
+ _initialised = false;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "TEA";
+ }
+
+ public int getBlockSize()
+ {
+ return block_size;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to TEA init - " + params.getClass().getName());
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (!_initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + block_size) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + block_size) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (_forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("Key size must be 128 bits.");
+ }
+
+ _a = bytesToInt(key, 0);
+ _b = bytesToInt(key, 4);
+ _c = bytesToInt(key, 8);
+ _d = bytesToInt(key, 12);
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ int sum = 0;
+
+ for (int i = 0; i != rounds; i++)
+ {
+ sum += delta;
+ v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b);
+ v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d);
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ int sum = d_sum;
+
+ for (int i = 0; i != rounds; i++)
+ {
+ v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d);
+ v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b);
+ sum -= delta;
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int bytesToInt(byte[] in, int inOff)
+ {
+ return ((in[inOff++]) << 24) |
+ ((in[inOff++] & 255) << 16) |
+ ((in[inOff++] & 255) << 8) |
+ ((in[inOff] & 255));
+ }
+
+ private void unpackInt(int v, byte[] out, int outOff)
+ {
+ out[outOff++] = (byte)(v >>> 24);
+ out[outOff++] = (byte)(v >>> 16);
+ out[outOff++] = (byte)(v >>> 8);
+ out[outOff ] = (byte)v;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/ThreefishEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/ThreefishEngine.java
new file mode 100644
index 00000000..40e08b9b
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/ThreefishEngine.java
@@ -0,0 +1,1494 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.TweakableBlockCipherParameters;
+
+/**
+ * Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block
+ * sizes.
+ * <p>
+ * This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST
+ * SHA-3 competition in October 2010.
+ * <p>
+ * Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p>
+ * This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables
+ * to speed up key schedule injection. <br>
+ * 2 x block size state is retained by each cipher instance.
+ */
+public class ThreefishEngine
+ implements BlockCipher
+{
+ /**
+ * 256 bit block size - Threefish-256
+ */
+ public static final int BLOCKSIZE_256 = 256;
+ /**
+ * 512 bit block size - Threefish-512
+ */
+ public static final int BLOCKSIZE_512 = 512;
+ /**
+ * 1024 bit block size - Threefish-1024
+ */
+ public static final int BLOCKSIZE_1024 = 1024;
+
+ /**
+ * Size of the tweak in bytes (always 128 bit/16 bytes)
+ */
+ private static final int TWEAK_SIZE_BYTES = 16;
+ private static final int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8;
+
+ /**
+ * Rounds in Threefish-256
+ */
+ private static final int ROUNDS_256 = 72;
+ /**
+ * Rounds in Threefish-512
+ */
+ private static final int ROUNDS_512 = 72;
+ /**
+ * Rounds in Threefish-1024
+ */
+ private static final int ROUNDS_1024 = 80;
+
+ /**
+ * Max rounds of any of the variants
+ */
+ private static final int MAX_ROUNDS = ROUNDS_1024;
+
+ /**
+ * Key schedule parity constant
+ */
+ private static final long C_240 = 0x1BD11BDAA9FC1A22L;
+
+ /* Pre-calculated modulo arithmetic tables for key schedule lookups */
+ private static int[] MOD9 = new int[MAX_ROUNDS];
+ private static int[] MOD17 = new int[MOD9.length];
+ private static int[] MOD5 = new int[MOD9.length];
+ private static int[] MOD3 = new int[MOD9.length];
+
+ static
+ {
+ for (int i = 0; i < MOD9.length; i++)
+ {
+ MOD17[i] = i % 17;
+ MOD9[i] = i % 9;
+ MOD5[i] = i % 5;
+ MOD3[i] = i % 3;
+ }
+ }
+
+ /**
+ * Block size in bytes
+ */
+ private int blocksizeBytes;
+
+ /**
+ * Block size in 64 bit words
+ */
+ private int blocksizeWords;
+
+ /**
+ * Buffer for byte oriented processBytes to call internal word API
+ */
+ private long[] currentBlock;
+
+ /**
+ * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup
+ */
+ private long[] t = new long[5];
+
+ /**
+ * Key schedule words
+ */
+ private long[] kw;
+
+ /**
+ * The internal cipher implementation (varies by blocksize)
+ */
+ private ThreefishCipher cipher;
+
+ private boolean forEncryption;
+
+ /**
+ * Constructs a new Threefish cipher, with a specified block size.
+ *
+ * @param blocksizeBits the block size in bits, one of {@link #BLOCKSIZE_256}, {@link #BLOCKSIZE_512},
+ * {@link #BLOCKSIZE_1024}.
+ */
+ public ThreefishEngine(final int blocksizeBits)
+ {
+ this.blocksizeBytes = (blocksizeBits / 8);
+ this.blocksizeWords = (this.blocksizeBytes / 8);
+ this.currentBlock = new long[blocksizeWords];
+
+ /*
+ * Provide room for original key words, extended key word and repeat of key words for modulo
+ * free lookup of key schedule words.
+ */
+ this.kw = new long[2 * blocksizeWords + 1];
+
+ switch (blocksizeBits)
+ {
+ case BLOCKSIZE_256:
+ cipher = new Threefish256Cipher(kw, t);
+ break;
+ case BLOCKSIZE_512:
+ cipher = new Threefish512Cipher(kw, t);
+ break;
+ case BLOCKSIZE_1024:
+ cipher = new Threefish1024Cipher(kw, t);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits");
+ }
+ }
+
+ /**
+ * Initialise the engine.
+ *
+ * @param params an instance of {@link TweakableBlockCipherParameters}, or {@link KeyParameter} (to
+ * use a 0 tweak)
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ final byte[] keyBytes;
+ final byte[] tweakBytes;
+
+ if (params instanceof TweakableBlockCipherParameters)
+ {
+ TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)params;
+ keyBytes = tParams.getKey().getKey();
+ tweakBytes = tParams.getTweak();
+ }
+ else if (params instanceof KeyParameter)
+ {
+ keyBytes = ((KeyParameter)params).getKey();
+ tweakBytes = null;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Threefish init - "
+ + params.getClass().getName());
+ }
+
+ long[] keyWords = null;
+ long[] tweakWords = null;
+
+ if (keyBytes != null)
+ {
+ if (keyBytes.length != this.blocksizeBytes)
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeBytes
+ + " bytes)");
+ }
+ keyWords = new long[blocksizeWords];
+ for (int i = 0; i < keyWords.length; i++)
+ {
+ keyWords[i] = bytesToWord(keyBytes, i * 8);
+ }
+ }
+ if (tweakBytes != null)
+ {
+ if (tweakBytes.length != TWEAK_SIZE_BYTES)
+ {
+ throw new IllegalArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes");
+ }
+ tweakWords = new long[]{bytesToWord(tweakBytes, 0), bytesToWord(tweakBytes, 8)};
+ }
+ init(forEncryption, keyWords, tweakWords);
+ }
+
+ /**
+ * Initialise the engine, specifying the key and tweak directly.
+ *
+ * @param forEncryption the cipher mode.
+ * @param key the words of the key, or <code>null</code> to use the current key.
+ * @param tweak the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak.
+ */
+ public void init(boolean forEncryption, final long[] key, final long[] tweak)
+ {
+ this.forEncryption = forEncryption;
+ if (key != null)
+ {
+ setKey(key);
+ }
+ if (tweak != null)
+ {
+ setTweak(tweak);
+ }
+ }
+
+ private void setKey(long[] key)
+ {
+ if (key.length != this.blocksizeWords)
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeWords
+ + " words)");
+ }
+
+ /*
+ * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512,
+ * 20k for 1024).
+ *
+ * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations
+ * used, to avoid expensive mod computations during cipher operation.
+ */
+
+ long knw = C_240;
+ for (int i = 0; i < blocksizeWords; i++)
+ {
+ kw[i] = key[i];
+ knw = knw ^ kw[i];
+ }
+ kw[blocksizeWords] = knw;
+ System.arraycopy(kw, 0, kw, blocksizeWords + 1, blocksizeWords);
+ }
+
+ private void setTweak(long[] tweak)
+ {
+ if (tweak.length != TWEAK_SIZE_WORDS)
+ {
+ throw new IllegalArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words.");
+ }
+
+ /*
+ * Tweak schedule partially repeated to avoid mod computations during cipher operation
+ */
+ t[0] = tweak[0];
+ t[1] = tweak[1];
+ t[2] = t[0] ^ t[1];
+ t[3] = t[0];
+ t[4] = t[1];
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Threefish-" + (blocksizeBytes * 8);
+ }
+
+ public int getBlockSize()
+ {
+ return blocksizeBytes;
+ }
+
+ public void reset()
+ {
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ if ((outOff + blocksizeBytes) > out.length)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if ((inOff + blocksizeBytes) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ currentBlock[i >> 3] = bytesToWord(in, inOff + i);
+ }
+ processBlock(this.currentBlock, this.currentBlock);
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ wordToBytes(this.currentBlock[i >> 3], out, outOff + i);
+ }
+
+ return blocksizeBytes;
+ }
+
+ /**
+ * Process a block of data represented as 64 bit words.
+ *
+ * @param in a block sized buffer of words to process.
+ * @param out a block sized buffer of words to receive the output of the operation.
+ * @return the number of 8 byte words processed (which will be the same as the block size).
+ * @throws DataLengthException if either the input or output is not block sized.
+ * @throws IllegalStateException if this engine is not initialised.
+ */
+ public int processBlock(long[] in, long[] out)
+ throws DataLengthException, IllegalStateException
+ {
+ if (kw[blocksizeWords] == 0)
+ {
+ throw new IllegalStateException("Threefish engine not initialised");
+ }
+
+ if (in.length != blocksizeWords)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+ if (out.length != blocksizeWords)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ cipher.encryptBlock(in, out);
+ }
+ else
+ {
+ cipher.decryptBlock(in, out);
+ }
+
+ return blocksizeWords;
+ }
+
+ /**
+ * Read a single 64 bit word from input in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static long bytesToWord(final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+
+ long word = 0;
+ int index = off;
+
+ word = (bytes[index++] & 0xffL);
+ word |= (bytes[index++] & 0xffL) << 8;
+ word |= (bytes[index++] & 0xffL) << 16;
+ word |= (bytes[index++] & 0xffL) << 24;
+ word |= (bytes[index++] & 0xffL) << 32;
+ word |= (bytes[index++] & 0xffL) << 40;
+ word |= (bytes[index++] & 0xffL) << 48;
+ word |= (bytes[index++] & 0xffL) << 56;
+
+ return word;
+ }
+
+ /**
+ * Write a 64 bit word to output in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static void wordToBytes(final long word, final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+ int index = off;
+
+ bytes[index++] = (byte)word;
+ bytes[index++] = (byte)(word >> 8);
+ bytes[index++] = (byte)(word >> 16);
+ bytes[index++] = (byte)(word >> 24);
+ bytes[index++] = (byte)(word >> 32);
+ bytes[index++] = (byte)(word >> 40);
+ bytes[index++] = (byte)(word >> 48);
+ bytes[index++] = (byte)(word >> 56);
+ }
+
+ /**
+ * Rotate left + xor part of the mix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long rotlXor(long x, int n, long xor)
+ {
+ return ((x << n) | (x >>> -n)) ^ xor;
+ }
+
+ /**
+ * Rotate xor + rotate right part of the unmix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long xorRotr(long x, int n, long xor)
+ {
+ long xored = x ^ xor;
+ return (xored >>> n) | (xored << -n);
+ }
+
+ private static abstract class ThreefishCipher
+ {
+ /**
+ * The extended + repeated tweak words
+ */
+ protected final long[] t;
+ /**
+ * The extended + repeated key words
+ */
+ protected final long[] kw;
+
+ protected ThreefishCipher(final long[] kw, final long[] t)
+ {
+ this.kw = kw;
+ this.t = t;
+ }
+
+ abstract void encryptBlock(long[] block, long[] out);
+
+ abstract void decryptBlock(long[] block, long[] out);
+
+ }
+
+ private static final class Threefish256Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 14, ROTATION_0_1 = 16;
+ private static final int ROTATION_1_0 = 52, ROTATION_1_1 = 57;
+ private static final int ROTATION_2_0 = 23, ROTATION_2_1 = 40;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 37;
+
+ private static final int ROTATION_4_0 = 25, ROTATION_4_1 = 33;
+ private static final int ROTATION_5_0 = 46, ROTATION_5_1 = 12;
+ private static final int ROTATION_6_0 = 58, ROTATION_6_1 = 22;
+ private static final int ROTATION_7_0 = 32, ROTATION_7_1 = 32;
+
+ public Threefish256Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 4 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1] + t[0];
+ b2 += kw[2] + t[1];
+ b3 += kw[3];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 2 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_256 / 4); d += 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 2 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_1_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_1_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_3_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_3_1, b2 += b1);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm5];
+ b1 += kw[dm5 + 1] + t[dm3];
+ b2 += kw[dm5 + 2] + t[dm3 + 1];
+ b3 += kw[dm5 + 3] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_5_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_5_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_7_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_7_1, b2 += b1);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm5 + 1];
+ b1 += kw[dm5 + 2] + t[dm3 + 1];
+ b2 += kw[dm5 + 3] + t[dm3 + 2];
+ b3 += kw[dm5 + 4] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm5 + 1];
+ b1 -= kw[dm5 + 2] + t[dm3 + 1];
+ b2 -= kw[dm5 + 3] + t[dm3 + 2];
+ b3 -= kw[dm5 + 4] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b3 = xorRotr(b3, ROTATION_7_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_7_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_5_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_5_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm5];
+ b1 -= kw[dm5 + 1] + t[dm3];
+ b2 -= kw[dm5 + 2] + t[dm3 + 1];
+ b3 -= kw[dm5 + 3] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b3 = xorRotr(b3, ROTATION_3_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_3_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_1_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_1_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1] + t[0];
+ b2 -= kw[2] + t[1];
+ b3 -= kw[3];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ }
+
+ }
+
+ private static final class Threefish512Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37;
+ private static final int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42;
+ private static final int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39;
+ private static final int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56;
+
+ private static final int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24;
+ private static final int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17;
+ private static final int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43;
+ private static final int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22;
+
+ protected Threefish512Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ public void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 8 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5] + t[0];
+ b6 += kw[6] + t[1];
+ b7 += kw[7];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_512 / 4); d += 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_1_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_1_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_1_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_1_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_2_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_2_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_3_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_3_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_3_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_3_3, b4 += b3);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm9];
+ b1 += kw[dm9 + 1];
+ b2 += kw[dm9 + 2];
+ b3 += kw[dm9 + 3];
+ b4 += kw[dm9 + 4];
+ b5 += kw[dm9 + 5] + t[dm3];
+ b6 += kw[dm9 + 6] + t[dm3 + 1];
+ b7 += kw[dm9 + 7] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_5_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_5_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_5_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_5_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_6_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_6_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_7_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_7_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_7_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_7_3, b4 += b3);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm9 + 1];
+ b1 += kw[dm9 + 2];
+ b2 += kw[dm9 + 3];
+ b3 += kw[dm9 + 4];
+ b4 += kw[dm9 + 5];
+ b5 += kw[dm9 + 6] + t[dm3 + 1];
+ b6 += kw[dm9 + 7] + t[dm3 + 2];
+ b7 += kw[dm9 + 8] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ }
+
+ public void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm9 + 1];
+ b1 -= kw[dm9 + 2];
+ b2 -= kw[dm9 + 3];
+ b3 -= kw[dm9 + 4];
+ b4 -= kw[dm9 + 5];
+ b5 -= kw[dm9 + 6] + t[dm3 + 1];
+ b6 -= kw[dm9 + 7] + t[dm3 + 2];
+ b7 -= kw[dm9 + 8] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b1 = xorRotr(b1, ROTATION_7_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_7_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_7_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_6_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_6_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_5_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_5_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_5_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_5_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm9];
+ b1 -= kw[dm9 + 1];
+ b2 -= kw[dm9 + 2];
+ b3 -= kw[dm9 + 3];
+ b4 -= kw[dm9 + 4];
+ b5 -= kw[dm9 + 5] + t[dm3];
+ b6 -= kw[dm9 + 6] + t[dm3 + 1];
+ b7 -= kw[dm9 + 7] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b1 = xorRotr(b1, ROTATION_3_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_3_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_3_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_2_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_2_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_1_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_1_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_1_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_1_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5] + t[0];
+ b6 -= kw[6] + t[1];
+ b7 -= kw[7];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ }
+ }
+
+ private static final class Threefish1024Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47;
+ private static final int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37;
+ private static final int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55;
+ private static final int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52;
+ private static final int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13;
+ private static final int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41;
+ private static final int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25;
+
+ private static final int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31;
+ private static final int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30;
+ private static final int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51;
+ private static final int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41;
+ private static final int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46;
+ private static final int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25;
+ private static final int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52;
+ private static final int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20;
+
+ public Threefish1024Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 16 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5];
+ b6 += kw[6];
+ b7 += kw[7];
+ b8 += kw[8];
+ b9 += kw[9];
+ b10 += kw[10];
+ b11 += kw[11];
+ b12 += kw[12];
+ b13 += kw[13] + t[0];
+ b14 += kw[14] + t[1];
+ b15 += kw[15];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_1024 / 4); d += 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_0_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_0_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_0_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_0_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_1_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_1_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_1_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_1_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_1_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_1_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_1_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_1_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_2_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_2_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_2_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_2_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_2_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_2_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_2_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_2_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_3_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_3_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_3_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_3_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_3_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_3_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_3_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_3_7, b12 += b7);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm17];
+ b1 += kw[dm17 + 1];
+ b2 += kw[dm17 + 2];
+ b3 += kw[dm17 + 3];
+ b4 += kw[dm17 + 4];
+ b5 += kw[dm17 + 5];
+ b6 += kw[dm17 + 6];
+ b7 += kw[dm17 + 7];
+ b8 += kw[dm17 + 8];
+ b9 += kw[dm17 + 9];
+ b10 += kw[dm17 + 10];
+ b11 += kw[dm17 + 11];
+ b12 += kw[dm17 + 12];
+ b13 += kw[dm17 + 13] + t[dm3];
+ b14 += kw[dm17 + 14] + t[dm3 + 1];
+ b15 += kw[dm17 + 15] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_4_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_4_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_4_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_4_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_5_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_5_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_5_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_5_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_5_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_5_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_5_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_5_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_6_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_6_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_6_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_6_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_6_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_6_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_6_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_6_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_7_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_7_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_7_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_7_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_7_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_7_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_7_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_7_7, b12 += b7);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm17 + 1];
+ b1 += kw[dm17 + 2];
+ b2 += kw[dm17 + 3];
+ b3 += kw[dm17 + 4];
+ b4 += kw[dm17 + 5];
+ b5 += kw[dm17 + 6];
+ b6 += kw[dm17 + 7];
+ b7 += kw[dm17 + 8];
+ b8 += kw[dm17 + 9];
+ b9 += kw[dm17 + 10];
+ b10 += kw[dm17 + 11];
+ b11 += kw[dm17 + 12];
+ b12 += kw[dm17 + 13];
+ b13 += kw[dm17 + 14] + t[dm3 + 1];
+ b14 += kw[dm17 + 15] + t[dm3 + 2];
+ b15 += kw[dm17 + 16] + d + 1;
+
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ out[8] = b8;
+ out[9] = b9;
+ out[10] = b10;
+ out[11] = b11;
+ out[12] = b12;
+ out[13] = b13;
+ out[14] = b14;
+ out[15] = b15;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm17 + 1];
+ b1 -= kw[dm17 + 2];
+ b2 -= kw[dm17 + 3];
+ b3 -= kw[dm17 + 4];
+ b4 -= kw[dm17 + 5];
+ b5 -= kw[dm17 + 6];
+ b6 -= kw[dm17 + 7];
+ b7 -= kw[dm17 + 8];
+ b8 -= kw[dm17 + 9];
+ b9 -= kw[dm17 + 10];
+ b10 -= kw[dm17 + 11];
+ b11 -= kw[dm17 + 12];
+ b12 -= kw[dm17 + 13];
+ b13 -= kw[dm17 + 14] + t[dm3 + 1];
+ b14 -= kw[dm17 + 15] + t[dm3 + 2];
+ b15 -= kw[dm17 + 16] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_7_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_7_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_7_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_7_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_7_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_7_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_7_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_6_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_6_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_6_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_6_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_6_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_6_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_6_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_6_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_5_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_5_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_5_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_5_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_5_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_5_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_5_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_5_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_4_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_4_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_4_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_4_7, b14);
+ b14 -= b15;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm17];
+ b1 -= kw[dm17 + 1];
+ b2 -= kw[dm17 + 2];
+ b3 -= kw[dm17 + 3];
+ b4 -= kw[dm17 + 4];
+ b5 -= kw[dm17 + 5];
+ b6 -= kw[dm17 + 6];
+ b7 -= kw[dm17 + 7];
+ b8 -= kw[dm17 + 8];
+ b9 -= kw[dm17 + 9];
+ b10 -= kw[dm17 + 10];
+ b11 -= kw[dm17 + 11];
+ b12 -= kw[dm17 + 12];
+ b13 -= kw[dm17 + 13] + t[dm3];
+ b14 -= kw[dm17 + 14] + t[dm3 + 1];
+ b15 -= kw[dm17 + 15] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_3_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_3_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_3_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_3_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_3_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_3_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_3_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_2_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_2_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_2_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_2_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_2_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_2_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_2_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_2_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_1_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_1_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_1_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_1_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_1_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_1_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_1_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_1_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_0_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_0_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_0_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_0_7, b14);
+ b14 -= b15;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5];
+ b6 -= kw[6];
+ b7 -= kw[7];
+ b8 -= kw[8];
+ b9 -= kw[9];
+ b10 -= kw[10];
+ b11 -= kw[11];
+ b12 -= kw[12];
+ b13 -= kw[13] + t[0];
+ b14 -= kw[14] + t[1];
+ b15 -= kw[15];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ state[8] = b8;
+ state[9] = b9;
+ state[10] = b10;
+ state[11] = b11;
+ state[12] = b12;
+ state[13] = b13;
+ state[14] = b14;
+ state[15] = b15;
+ }
+
+ }
+
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/TwofishEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/TwofishEngine.java
new file mode 100644
index 00000000..c5b84385
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/TwofishEngine.java
@@ -0,0 +1,680 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * A class that provides Twofish encryption operations.
+ *
+ * This Java implementation is based on the Java reference
+ * implementation provided by Bruce Schneier and developed
+ * by Raif S. Naffah.
+ */
+public final class TwofishEngine
+ implements BlockCipher
+{
+ private static final byte[][] P = {
+ { // p0
+ (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8,
+ (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76,
+ (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78,
+ (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38,
+ (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98,
+ (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C,
+ (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26,
+ (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48,
+ (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30,
+ (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23,
+ (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59,
+ (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82,
+ (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E,
+ (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C,
+ (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE,
+ (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61,
+ (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5,
+ (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B,
+ (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B,
+ (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1,
+ (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45,
+ (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66,
+ (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56,
+ (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7,
+ (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5,
+ (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA,
+ (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF,
+ (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71,
+ (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD,
+ (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8,
+ (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D,
+ (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7,
+ (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED,
+ (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2,
+ (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11,
+ (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90,
+ (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF,
+ (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB,
+ (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B,
+ (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF,
+ (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE,
+ (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B,
+ (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46,
+ (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64,
+ (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F,
+ (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A,
+ (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A,
+ (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A,
+ (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29,
+ (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02,
+ (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17,
+ (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D,
+ (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74,
+ (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72,
+ (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12,
+ (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34,
+ (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68,
+ (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8,
+ (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40,
+ (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4,
+ (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0,
+ (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00,
+ (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42,
+ (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0 },
+ { // p1
+ (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4,
+ (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8,
+ (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B,
+ (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B,
+ (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD,
+ (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1,
+ (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B,
+ (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F,
+ (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B,
+ (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D,
+ (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E,
+ (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5,
+ (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14,
+ (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3,
+ (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54,
+ (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51,
+ (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A,
+ (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96,
+ (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10,
+ (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C,
+ (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7,
+ (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70,
+ (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB,
+ (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8,
+ (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF,
+ (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC,
+ (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF,
+ (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2,
+ (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82,
+ (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9,
+ (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97,
+ (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17,
+ (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D,
+ (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3,
+ (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C,
+ (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E,
+ (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F,
+ (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49,
+ (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21,
+ (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9,
+ (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD,
+ (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01,
+ (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F,
+ (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48,
+ (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E,
+ (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19,
+ (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57,
+ (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64,
+ (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE,
+ (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5,
+ (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44,
+ (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69,
+ (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15,
+ (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E,
+ (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34,
+ (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC,
+ (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B,
+ (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB,
+ (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52,
+ (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9,
+ (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4,
+ (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2,
+ (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56,
+ (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 }
+ };
+
+ /**
+ * Define the fixed p0/p1 permutations used in keyed S-box lookup.
+ * By changing the following constant definitions, the S-boxes will
+ * automatically get changed in the Twofish engine.
+ */
+ private static final int P_00 = 1;
+ private static final int P_01 = 0;
+ private static final int P_02 = 0;
+ private static final int P_03 = P_01 ^ 1;
+ private static final int P_04 = 1;
+
+ private static final int P_10 = 0;
+ private static final int P_11 = 0;
+ private static final int P_12 = 1;
+ private static final int P_13 = P_11 ^ 1;
+ private static final int P_14 = 0;
+
+ private static final int P_20 = 1;
+ private static final int P_21 = 1;
+ private static final int P_22 = 0;
+ private static final int P_23 = P_21 ^ 1;
+ private static final int P_24 = 0;
+
+ private static final int P_30 = 0;
+ private static final int P_31 = 1;
+ private static final int P_32 = 1;
+ private static final int P_33 = P_31 ^ 1;
+ private static final int P_34 = 1;
+
+ /* Primitive polynomial for GF(256) */
+ private static final int GF256_FDBK = 0x169;
+ private static final int GF256_FDBK_2 = GF256_FDBK / 2;
+ private static final int GF256_FDBK_4 = GF256_FDBK / 4;
+
+ private static final int RS_GF_FDBK = 0x14D; // field generator
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private static final int ROUNDS = 16;
+ private static final int MAX_ROUNDS = 16; // bytes = 128 bits
+ private static final int BLOCK_SIZE = 16; // bytes = 128 bits
+ private static final int MAX_KEY_BITS = 256;
+
+ private static final int INPUT_WHITEN=0;
+ private static final int OUTPUT_WHITEN=INPUT_WHITEN+BLOCK_SIZE/4; // 4
+ private static final int ROUND_SUBKEYS=OUTPUT_WHITEN+BLOCK_SIZE/4;// 8
+
+ private static final int TOTAL_SUBKEYS=ROUND_SUBKEYS+2*MAX_ROUNDS;// 40
+
+ private static final int SK_STEP = 0x02020202;
+ private static final int SK_BUMP = 0x01010101;
+ private static final int SK_ROTL = 9;
+
+ private boolean encrypting = false;
+
+ private int[] gMDS0 = new int[MAX_KEY_BITS];
+ private int[] gMDS1 = new int[MAX_KEY_BITS];
+ private int[] gMDS2 = new int[MAX_KEY_BITS];
+ private int[] gMDS3 = new int[MAX_KEY_BITS];
+
+ /**
+ * gSubKeys[] and gSBox[] are eventually used in the
+ * encryption and decryption methods.
+ */
+ private int[] gSubKeys;
+ private int[] gSBox;
+
+ private int k64Cnt = 0;
+
+ private byte[] workingKey = null;
+
+ public TwofishEngine()
+ {
+ // calculate the MDS matrix
+ int[] m1 = new int[2];
+ int[] mX = new int[2];
+ int[] mY = new int[2];
+ int j;
+
+ for (int i=0; i< MAX_KEY_BITS ; i++)
+ {
+ j = P[0][i] & 0xff;
+ m1[0] = j;
+ mX[0] = Mx_X(j) & 0xff;
+ mY[0] = Mx_Y(j) & 0xff;
+
+ j = P[1][i] & 0xff;
+ m1[1] = j;
+ mX[1] = Mx_X(j) & 0xff;
+ mY[1] = Mx_Y(j) & 0xff;
+
+ gMDS0[i] = m1[P_00] | mX[P_00] << 8 |
+ mY[P_00] << 16 | mY[P_00] << 24;
+
+ gMDS1[i] = mY[P_10] | mY[P_10] << 8 |
+ mX[P_10] << 16 | m1[P_10] << 24;
+
+ gMDS2[i] = mX[P_20] | mY[P_20] << 8 |
+ m1[P_20] << 16 | mY[P_20] << 24;
+
+ gMDS3[i] = mX[P_30] | m1[P_30] << 8 |
+ mY[P_30] << 16 | mX[P_30] << 24;
+ }
+ }
+
+ /**
+ * initialise a Twofish cipher.
+ *
+ * @param encrypting whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean encrypting,
+ CipherParameters params)
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.encrypting = encrypting;
+ this.workingKey = ((KeyParameter)params).getKey();
+ this.k64Cnt = (this.workingKey.length / 8); // pre-padded ?
+ setKey(this.workingKey);
+
+ return;
+ }
+
+ throw new IllegalArgumentException("invalid parameter passed to Twofish init - " + params.getClass().getName());
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Twofish";
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Twofish not initialised");
+ }
+
+ if ((inOff + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (encrypting)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ if (this.workingKey != null)
+ {
+ setKey(this.workingKey);
+ }
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private void setKey(byte[] key)
+ {
+ int[] k32e = new int[MAX_KEY_BITS/64]; // 4
+ int[] k32o = new int[MAX_KEY_BITS/64]; // 4
+
+ int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4
+ gSubKeys = new int[TOTAL_SUBKEYS];
+
+ if (k64Cnt < 1)
+ {
+ throw new IllegalArgumentException("Key size less than 64 bits");
+ }
+
+ if (k64Cnt > 4)
+ {
+ throw new IllegalArgumentException("Key size larger than 256 bits");
+ }
+
+ /*
+ * k64Cnt is the number of 8 byte blocks (64 chunks)
+ * that are in the input key. The input key is a
+ * maximum of 32 bytes (256 bits), so the range
+ * for k64Cnt is 1..4
+ */
+ for (int i=0; i<k64Cnt ; i++)
+ {
+ int p = i* 8;
+
+ k32e[i] = BytesTo32Bits(key, p);
+ k32o[i] = BytesTo32Bits(key, p+4);
+
+ sBoxKeys[k64Cnt-1-i] = RS_MDS_Encode(k32e[i], k32o[i]);
+ }
+
+ int q,A,B;
+ for (int i=0; i < TOTAL_SUBKEYS / 2 ; i++)
+ {
+ q = i*SK_STEP;
+ A = F32(q, k32e);
+ B = F32(q+SK_BUMP, k32o);
+ B = B << 8 | B >>> 24;
+ A += B;
+ gSubKeys[i*2] = A;
+ A += B;
+ gSubKeys[i*2 + 1] = A << SK_ROTL | A >>> (32-SK_ROTL);
+ }
+
+ /*
+ * fully expand the table for speed
+ */
+ int k0 = sBoxKeys[0];
+ int k1 = sBoxKeys[1];
+ int k2 = sBoxKeys[2];
+ int k3 = sBoxKeys[3];
+ int b0, b1, b2, b3;
+ gSBox = new int[4*MAX_KEY_BITS];
+ for (int i=0; i<MAX_KEY_BITS; i++)
+ {
+ b0 = b1 = b2 = b3 = i;
+ switch (k64Cnt & 3)
+ {
+ case 1:
+ gSBox[i*2] = gMDS0[(P[P_01][b0] & 0xff) ^ b0(k0)];
+ gSBox[i*2+1] = gMDS1[(P[P_11][b1] & 0xff) ^ b1(k0)];
+ gSBox[i*2+0x200] = gMDS2[(P[P_21][b2] & 0xff) ^ b2(k0)];
+ gSBox[i*2+0x201] = gMDS3[(P[P_31][b3] & 0xff) ^ b3(k0)];
+ break;
+ case 0: // 256 bits of key
+ b0 = (P[P_04][b0] & 0xff) ^ b0(k3);
+ b1 = (P[P_14][b1] & 0xff) ^ b1(k3);
+ b2 = (P[P_24][b2] & 0xff) ^ b2(k3);
+ b3 = (P[P_34][b3] & 0xff) ^ b3(k3);
+ // fall through, having pre-processed b[0]..b[3] with k32[3]
+ case 3: // 192 bits of key
+ b0 = (P[P_03][b0] & 0xff) ^ b0(k2);
+ b1 = (P[P_13][b1] & 0xff) ^ b1(k2);
+ b2 = (P[P_23][b2] & 0xff) ^ b2(k2);
+ b3 = (P[P_33][b3] & 0xff) ^ b3(k2);
+ // fall through, having pre-processed b[0]..b[3] with k32[2]
+ case 2: // 128 bits of key
+ gSBox[i*2] = gMDS0[(P[P_01]
+ [(P[P_02][b0] & 0xff) ^ b0(k1)] & 0xff) ^ b0(k0)];
+ gSBox[i*2+1] = gMDS1[(P[P_11]
+ [(P[P_12][b1] & 0xff) ^ b1(k1)] & 0xff) ^ b1(k0)];
+ gSBox[i*2+0x200] = gMDS2[(P[P_21]
+ [(P[P_22][b2] & 0xff) ^ b2(k1)] & 0xff) ^ b2(k0)];
+ gSBox[i*2+0x201] = gMDS3[(P[P_31]
+ [(P[P_32][b3] & 0xff) ^ b3(k1)] & 0xff) ^ b3(k0)];
+ break;
+ }
+ }
+
+ /*
+ * the function exits having setup the gSBox with the
+ * input key material.
+ */
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ *
+ * encryptBlock uses the pre-calculated gSBox[] and subKey[]
+ * arrays.
+ */
+ private void encryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int x0 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
+ int x1 = BytesTo32Bits(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
+ int x2 = BytesTo32Bits(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
+ int x3 = BytesTo32Bits(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
+
+ int k = ROUND_SUBKEYS;
+ int t0, t1;
+ for (int r = 0; r < ROUNDS; r +=2)
+ {
+ t0 = Fe32_0(x0);
+ t1 = Fe32_3(x1);
+ x2 ^= t0 + t1 + gSubKeys[k++];
+ x2 = x2 >>>1 | x2 << 31;
+ x3 = (x3 << 1 | x3 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+
+ t0 = Fe32_0(x2);
+ t1 = Fe32_3(x3);
+ x0 ^= t0 + t1 + gSubKeys[k++];
+ x0 = x0 >>>1 | x0 << 31;
+ x1 = (x1 << 1 | x1 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+ }
+
+ Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
+ Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
+ Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
+ Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place
+ * the result in the provided buffer starting at the given offset.
+ * The input will be an exact multiple of our blocksize.
+ */
+ private void decryptBlock(
+ byte[] src,
+ int srcIndex,
+ byte[] dst,
+ int dstIndex)
+ {
+ int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
+ int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1];
+ int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2];
+ int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3];
+
+ int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
+ int t0, t1;
+ for (int r = 0; r< ROUNDS ; r +=2)
+ {
+ t0 = Fe32_0(x2);
+ t1 = Fe32_3(x3);
+ x1 ^= t0 + 2*t1 + gSubKeys[k--];
+ x0 = (x0 << 1 | x0 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
+ x1 = x1 >>>1 | x1 << 31;
+
+ t0 = Fe32_0(x0);
+ t1 = Fe32_3(x1);
+ x3 ^= t0 + 2*t1 + gSubKeys[k--];
+ x2 = (x2 << 1 | x2 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
+ x3 = x3 >>>1 | x3 << 31;
+ }
+
+ Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
+ Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
+ Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
+ Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
+ }
+
+ /*
+ * TODO: This can be optimised and made cleaner by combining
+ * the functionality in this function and applying it appropriately
+ * to the creation of the subkeys during key setup.
+ */
+ private int F32(int x, int[] k32)
+ {
+ int b0 = b0(x);
+ int b1 = b1(x);
+ int b2 = b2(x);
+ int b3 = b3(x);
+ int k0 = k32[0];
+ int k1 = k32[1];
+ int k2 = k32[2];
+ int k3 = k32[3];
+
+ int result = 0;
+ switch (k64Cnt & 3)
+ {
+ case 1:
+ result = gMDS0[(P[P_01][b0] & 0xff) ^ b0(k0)] ^
+ gMDS1[(P[P_11][b1] & 0xff) ^ b1(k0)] ^
+ gMDS2[(P[P_21][b2] & 0xff) ^ b2(k0)] ^
+ gMDS3[(P[P_31][b3] & 0xff) ^ b3(k0)];
+ break;
+ case 0: /* 256 bits of key */
+ b0 = (P[P_04][b0] & 0xff) ^ b0(k3);
+ b1 = (P[P_14][b1] & 0xff) ^ b1(k3);
+ b2 = (P[P_24][b2] & 0xff) ^ b2(k3);
+ b3 = (P[P_34][b3] & 0xff) ^ b3(k3);
+ case 3:
+ b0 = (P[P_03][b0] & 0xff) ^ b0(k2);
+ b1 = (P[P_13][b1] & 0xff) ^ b1(k2);
+ b2 = (P[P_23][b2] & 0xff) ^ b2(k2);
+ b3 = (P[P_33][b3] & 0xff) ^ b3(k2);
+ case 2:
+ result =
+ gMDS0[(P[P_01][(P[P_02][b0]&0xff)^b0(k1)]&0xff)^b0(k0)] ^
+ gMDS1[(P[P_11][(P[P_12][b1]&0xff)^b1(k1)]&0xff)^b1(k0)] ^
+ gMDS2[(P[P_21][(P[P_22][b2]&0xff)^b2(k1)]&0xff)^b2(k0)] ^
+ gMDS3[(P[P_31][(P[P_32][b3]&0xff)^b3(k1)]&0xff)^b3(k0)];
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Use (12, 8) Reed-Solomon code over GF(256) to produce
+ * a key S-box 32-bit entity from 2 key material 32-bit
+ * entities.
+ *
+ * @param k0 first 32-bit entity
+ * @param k1 second 32-bit entity
+ * @return Remainder polynomial generated using RS code
+ */
+ private int RS_MDS_Encode(int k0, int k1)
+ {
+ int r = k1;
+ for (int i = 0 ; i < 4 ; i++) // shift 1 byte at a time
+ {
+ r = RS_rem(r);
+ }
+ r ^= k0;
+ for (int i=0 ; i < 4 ; i++)
+ {
+ r = RS_rem(r);
+ }
+
+ return r;
+ }
+
+ /**
+ * Reed-Solomon code parameters: (12,8) reversible code:<p>
+ * <pre>
+ * g(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
+ * </pre>
+ * where a = primitive root of field generator 0x14D
+ */
+ private int RS_rem(int x)
+ {
+ int b = (x >>> 24) & 0xff;
+ int g2 = ((b << 1) ^
+ ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff;
+ int g3 = ((b >>> 1) ^
+ ((b & 0x01) != 0 ? (RS_GF_FDBK >>> 1) : 0)) ^ g2 ;
+ return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b);
+ }
+
+ private int LFSR1(int x)
+ {
+ return (x >> 1) ^
+ (((x & 0x01) != 0) ? GF256_FDBK_2 : 0);
+ }
+
+ private int LFSR2(int x)
+ {
+ return (x >> 2) ^
+ (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^
+ (((x & 0x01) != 0) ? GF256_FDBK_4 : 0);
+ }
+
+ private int Mx_X(int x)
+ {
+ return x ^ LFSR2(x);
+ } // 5B
+
+ private int Mx_Y(int x)
+ {
+ return x ^ LFSR1(x) ^ LFSR2(x);
+ } // EF
+
+ private int b0(int x)
+ {
+ return x & 0xff;
+ }
+
+ private int b1(int x)
+ {
+ return (x >>> 8) & 0xff;
+ }
+
+ private int b2(int x)
+ {
+ return (x >>> 16) & 0xff;
+ }
+
+ private int b3(int x)
+ {
+ return (x >>> 24) & 0xff;
+ }
+
+ private int Fe32_0(int x)
+ {
+ return gSBox[ 0x000 + 2*(x & 0xff) ] ^
+ gSBox[ 0x001 + 2*((x >>> 8) & 0xff) ] ^
+ gSBox[ 0x200 + 2*((x >>> 16) & 0xff) ] ^
+ gSBox[ 0x201 + 2*((x >>> 24) & 0xff) ];
+ }
+
+ private int Fe32_3(int x)
+ {
+ return gSBox[ 0x000 + 2*((x >>> 24) & 0xff) ] ^
+ gSBox[ 0x001 + 2*(x & 0xff) ] ^
+ gSBox[ 0x200 + 2*((x >>> 8) & 0xff) ] ^
+ gSBox[ 0x201 + 2*((x >>> 16) & 0xff) ];
+ }
+
+ private int BytesTo32Bits(byte[] b, int p)
+ {
+ return ((b[p] & 0xff)) |
+ ((b[p+1] & 0xff) << 8) |
+ ((b[p+2] & 0xff) << 16) |
+ ((b[p+3] & 0xff) << 24);
+ }
+
+ private void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset] = (byte)in;
+ b[offset + 1] = (byte)(in >> 8);
+ b[offset + 2] = (byte)(in >> 16);
+ b[offset + 3] = (byte)(in >> 24);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/VMPCEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/VMPCEngine.java
new file mode 100644
index 00000000..f1a4a564
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/VMPCEngine.java
@@ -0,0 +1,142 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.StreamCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+public class VMPCEngine implements StreamCipher
+{
+ /*
+ * variables to hold the state of the VMPC engine during encryption and
+ * decryption
+ */
+ protected byte n = 0;
+ protected byte[] P = null;
+ protected byte s = 0;
+
+ protected byte[] workingIV;
+ protected byte[] workingKey;
+
+ public String getAlgorithmName()
+ {
+ return "VMPC";
+ }
+
+ /**
+ * initialise a VMPC cipher.
+ *
+ * @param forEncryption
+ * whether or not we are for encryption.
+ * @param params
+ * the parameters required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ {
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException(
+ "VMPC init parameters must include an IV");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV) params;
+
+ if (!(ivParams.getParameters() instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException(
+ "VMPC init parameters must include a key");
+ }
+
+ KeyParameter key = (KeyParameter) ivParams.getParameters();
+
+ this.workingIV = ivParams.getIV();
+
+ if (workingIV == null || workingIV.length < 1 || workingIV.length > 768)
+ {
+ throw new IllegalArgumentException("VMPC requires 1 to 768 bytes of IV");
+ }
+
+ this.workingKey = key.getKey();
+
+ initKey(this.workingKey, this.workingIV);
+ }
+
+ protected void initKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+ n = 0;
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
+ int outOff)
+ {
+ if ((inOff + len) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + len) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < len; i++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ // encryption
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+
+ // xor
+ out[i + outOff] = (byte) (in[i + inOff] ^ z);
+ }
+
+ return len;
+ }
+
+ public void reset()
+ {
+ initKey(this.workingKey, this.workingIV);
+ }
+
+ public byte returnByte(byte in)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ // encryption
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+
+ // xor
+ return (byte) (in ^ z);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/VMPCKSA3Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/VMPCKSA3Engine.java
new file mode 100644
index 00000000..2e03fdbb
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/VMPCKSA3Engine.java
@@ -0,0 +1,45 @@
+package org.spongycastle.crypto.engines;
+
+public class VMPCKSA3Engine extends VMPCEngine
+{
+ public String getAlgorithmName()
+ {
+ return "VMPC-KSA3";
+ }
+
+ protected void initKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ s = 0;
+ P = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ P[i] = (byte) i;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ for (int m = 0; m < 768; m++)
+ {
+ s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
+ byte temp = P[m & 0xff];
+ P[m & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ }
+
+ n = 0;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/XSalsa20Engine.java b/core/src/main/java/org/spongycastle/crypto/engines/XSalsa20Engine.java
new file mode 100644
index 00000000..f70022b9
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/XSalsa20Engine.java
@@ -0,0 +1,65 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.util.Pack;
+
+/**
+ * Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce.
+ * <p>
+ * XSalsa20 requires a 256 bit key, and a 192 bit nonce.
+ */
+public class XSalsa20Engine extends Salsa20Engine
+{
+ public String getAlgorithmName()
+ {
+ return "XSalsa20";
+ }
+
+ protected int getNonceSize()
+ {
+ return 24;
+ }
+
+ /**
+ * XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce
+ * using a core Salsa20 function without input addition to produce 256 bit working key
+ * and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state.
+ */
+ protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ if (keyBytes == null)
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " doesn't support re-init with null key");
+ }
+
+ if (keyBytes.length != 32)
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires a 256 bit key");
+ }
+
+ // Set key for HSalsa20
+ super.setKey(keyBytes, ivBytes);
+
+ // Pack next 64 bits of IV into engine state instead of counter
+ engineState[8] = Pack.littleEndianToInt(ivBytes, 8);
+ engineState[9] = Pack.littleEndianToInt(ivBytes, 12);
+
+ // Process engine state to generate Salsa20 key
+ int[] hsalsa20Out = new int[engineState.length];
+ salsaCore(20, engineState, hsalsa20Out);
+
+ // Set new key, removing addition in last round of salsaCore
+ engineState[1] = hsalsa20Out[0] - engineState[0];
+ engineState[2] = hsalsa20Out[5] - engineState[5];
+ engineState[3] = hsalsa20Out[10] - engineState[10];
+ engineState[4] = hsalsa20Out[15] - engineState[15];
+
+ engineState[11] = hsalsa20Out[6] - engineState[6];
+ engineState[12] = hsalsa20Out[7] - engineState[7];
+ engineState[13] = hsalsa20Out[8] - engineState[8];
+ engineState[14] = hsalsa20Out[9] - engineState[9];
+
+ // Last 64 bits of input IV
+ engineState[6] = Pack.littleEndianToInt(ivBytes, 16);
+ engineState[7] = Pack.littleEndianToInt(ivBytes, 20);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/engines/XTEAEngine.java b/core/src/main/java/org/spongycastle/crypto/engines/XTEAEngine.java
new file mode 100644
index 00000000..5dcb4ea1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/engines/XTEAEngine.java
@@ -0,0 +1,188 @@
+package org.spongycastle.crypto.engines;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.params.KeyParameter;
+
+/**
+ * An XTEA engine.
+ */
+public class XTEAEngine
+ implements BlockCipher
+{
+ private static final int rounds = 32,
+ block_size = 8,
+// key_size = 16,
+ delta = 0x9E3779B9;
+
+ /*
+ * the expanded key array of 4 subkeys
+ */
+ private int[] _S = new int[4],
+ _sum0 = new int[32],
+ _sum1 = new int[32];
+ private boolean _initialised,
+ _forEncryption;
+
+ /**
+ * Create an instance of the TEA encryption algorithm
+ * and set some defaults
+ */
+ public XTEAEngine()
+ {
+ _initialised = false;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "XTEA";
+ }
+
+ public int getBlockSize()
+ {
+ return block_size;
+ }
+
+ /**
+ * initialise
+ *
+ * @param forEncryption whether or not we are for encryption.
+ * @param params the parameters required to set up the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("invalid parameter passed to TEA init - " + params.getClass().getName());
+ }
+
+ _forEncryption = forEncryption;
+ _initialised = true;
+
+ KeyParameter p = (KeyParameter)params;
+
+ setKey(p.getKey());
+ }
+
+ public int processBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ if (!_initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ }
+
+ if ((inOff + block_size) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + block_size) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ return (_forEncryption) ? encryptBlock(in, inOff, out, outOff)
+ : decryptBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * Re-key the cipher.
+ * <p>
+ * @param key the key to be used
+ */
+ private void setKey(
+ byte[] key)
+ {
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("Key size must be 128 bits.");
+ }
+
+ int i, j;
+ for (i = j = 0; i < 4; i++,j+=4)
+ {
+ _S[i] = bytesToInt(key, j);
+ }
+
+ for (i = j = 0; i < rounds; i++)
+ {
+ _sum0[i] = (j + _S[j & 3]);
+ j += delta;
+ _sum1[i] = (j + _S[j >>> 11 & 3]);
+ }
+ }
+
+ private int encryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ for (int i = 0; i < rounds; i++)
+ {
+ v0 += ((v1 << 4 ^ v1 >>> 5) + v1) ^ _sum0[i];
+ v1 += ((v0 << 4 ^ v0 >>> 5) + v0) ^ _sum1[i];
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int decryptBlock(
+ byte[] in,
+ int inOff,
+ byte[] out,
+ int outOff)
+ {
+ // Pack bytes into integers
+ int v0 = bytesToInt(in, inOff);
+ int v1 = bytesToInt(in, inOff + 4);
+
+ for (int i = rounds-1; i >= 0; i--)
+ {
+ v1 -= ((v0 << 4 ^ v0 >>> 5) + v0) ^ _sum1[i];
+ v0 -= ((v1 << 4 ^ v1 >>> 5) + v1) ^ _sum0[i];
+ }
+
+ unpackInt(v0, out, outOff);
+ unpackInt(v1, out, outOff + 4);
+
+ return block_size;
+ }
+
+ private int bytesToInt(byte[] in, int inOff)
+ {
+ return ((in[inOff++]) << 24) |
+ ((in[inOff++] & 255) << 16) |
+ ((in[inOff++] & 255) << 8) |
+ ((in[inOff] & 255));
+ }
+
+ private void unpackInt(int v, byte[] out, int outOff)
+ {
+ out[outOff++] = (byte)(v >>> 24);
+ out[outOff++] = (byte)(v >>> 16);
+ out[outOff++] = (byte)(v >>> 8);
+ out[outOff ] = (byte)v;
+ }
+}