summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/AssemblyInfo.cs2
-rw-r--r--crypto/src/asn1/misc/MiscObjectIdentifiers.cs9
-rw-r--r--crypto/src/asn1/ua/UAObjectIdentifiers.cs107
-rw-r--r--crypto/src/crypto/digests/Blake2bDigest.cs531
-rw-r--r--crypto/src/crypto/digests/Blake2sDigest.cs552
-rw-r--r--crypto/src/crypto/digests/DSTU7564Digest.cs665
-rw-r--r--crypto/src/crypto/engines/Dstu7624Engine.cs1283
-rw-r--r--crypto/src/crypto/generators/SCrypt.cs42
-rw-r--r--crypto/src/security/DigestUtilities.cs111
9 files changed, 2472 insertions, 830 deletions
diff --git a/crypto/src/AssemblyInfo.cs b/crypto/src/AssemblyInfo.cs

index 4887b00b0..87f48dffa 100644 --- a/crypto/src/AssemblyInfo.cs +++ b/crypto/src/AssemblyInfo.cs
@@ -18,7 +18,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("The Legion of the Bouncy Castle Inc.")] [assembly: AssemblyProduct("Bouncy Castle for .NET")] -[assembly: AssemblyCopyright("Copyright (C) 2000-2017")] +[assembly: AssemblyCopyright("Copyright (C) 2000-2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs
index 8128b6952..d344393dd 100644 --- a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs +++ b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs
@@ -75,5 +75,14 @@ namespace Org.BouncyCastle.Asn1.Misc public static readonly DerObjectIdentifier id_blake2b256 = blake2.Branch("1.8"); public static readonly DerObjectIdentifier id_blake2b384 = blake2.Branch("1.12"); public static readonly DerObjectIdentifier id_blake2b512 = blake2.Branch("1.16"); + + public static readonly DerObjectIdentifier id_blake2s128 = blake2.Branch("2.4"); + public static readonly DerObjectIdentifier id_blake2s160 = blake2.Branch("2.5"); + public static readonly DerObjectIdentifier id_blake2s224 = blake2.Branch("2.7"); + public static readonly DerObjectIdentifier id_blake2s256 = blake2.Branch("2.8"); + + // + // Scrypt + public static readonly DerObjectIdentifier id_scrypt = new DerObjectIdentifier("1.3.6.1.4.1.11591.4.11"); } } diff --git a/crypto/src/asn1/ua/UAObjectIdentifiers.cs b/crypto/src/asn1/ua/UAObjectIdentifiers.cs new file mode 100644
index 000000000..9beca3af5 --- /dev/null +++ b/crypto/src/asn1/ua/UAObjectIdentifiers.cs
@@ -0,0 +1,107 @@ +namespace Org.BouncyCastle.Asn1.UA +{ + /** + * Ukrainian object identifiers + * <p> + * {iso(1) member-body(2) Ukraine(804) root(2) security(1) cryptography(1) pki(1)} + * <p> + * { ... pki-alg(1) pki-alg-sym(3) Dstu4145WithGost34311(1) PB(1)} + * <p> + * DSTU4145 in polynomial basis has 2 oids, one for little-endian representation and one for big-endian + */ + public abstract class UAObjectIdentifiers + { + /** Base OID: 1.2.804.2.1.1.1 */ + public static readonly DerObjectIdentifier UaOid = new DerObjectIdentifier("1.2.804.2.1.1.1"); + + /** DSTU4145 Little Endian presentation. OID: 1.2.804.2.1.1.1.1.3.1.1 */ + public static readonly DerObjectIdentifier dstu4145le = UaOid.Branch("1.3.1.1"); + /** DSTU4145 Big Endian presentation. OID: 1.2.804.2.1.1.1.1.3.1.1.1 */ + public static readonly DerObjectIdentifier dstu4145be = UaOid.Branch("1.3.1.1.1.1"); + + /** DSTU7564 256-bit digest presentation. */ + public static readonly DerObjectIdentifier dstu7564digest_256 = UaOid.Branch("1.2.2.1"); + /** DSTU7564 384-bit digest presentation. */ + public static readonly DerObjectIdentifier dstu7564digest_384 = UaOid.Branch("1.2.2.2"); + /** DSTU7564 512-bit digest presentation. */ + public static readonly DerObjectIdentifier dstu7564digest_512 = UaOid.Branch("1.2.2.3"); + + /** DSTU7564 256-bit mac presentation. */ + public static readonly DerObjectIdentifier dstu7564mac_256 = UaOid.Branch("1.2.2.4"); + /** DSTU7564 384-bit mac presentation. */ + public static readonly DerObjectIdentifier dstu7564mac_384 = UaOid.Branch("1.2.2.5"); + /** DSTU7564 512-bit mac presentation. */ + public static readonly DerObjectIdentifier dstu7564mac_512 = UaOid.Branch("1.2.2.6"); + + + /** DSTU7624 in ECB mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ecb_128 = UaOid.Branch("1.1.3.1.1"); + /** DSTU7624 in ECB mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ecb_256 = UaOid.Branch("1.1.3.1.2"); + /** DSTU7624 in ECB mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ecb_512 = UaOid.Branch("1.1.3.1.3"); + + /** DSTU7624 in CTR mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ctr_128 = UaOid.Branch("1.1.3.2.1"); + /** DSTU7624 in CTR mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ctr_256 = UaOid.Branch("1.1.3.2.2"); + /** DSTU7624 in CTR mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ctr_512 = UaOid.Branch("1.1.3.2.3"); + + /** DSTU7624 in CFB mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cfb_128 = UaOid.Branch("1.1.3.3.1"); + /** DSTU7624 in CFB mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cfb_256 = UaOid.Branch("1.1.3.3.2"); + /** DSTU7624 in CFB mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cfb_512 = UaOid.Branch("1.1.3.3.3"); + + /** DSTU7624 in MAC mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cmac_128 = UaOid.Branch("1.1.3.4.1"); + /** DSTU7624 in MAC mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cmac_256 = UaOid.Branch("1.1.3.4.2"); + /** DSTU7624 in MAC mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cmac_512 = UaOid.Branch("1.1.3.4.3"); + + /** DSTU7624 in CBC mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cbc_128 = UaOid.Branch("1.1.3.5.1"); + /** DSTU7624 in CBC mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cbc_256 = UaOid.Branch("1.1.3.5.2"); + /** DSTU7624 in CBC mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624cbc_512 = UaOid.Branch("1.1.3.5.3"); + + /** DSTU7624 in OFB mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ofb_128 = UaOid.Branch("1.1.3.6.1"); + /** DSTU7624 in OFB mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ofb_256 = UaOid.Branch("1.1.3.6.2"); + /** DSTU7624 in OFB mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ofb_512 = UaOid.Branch("1.1.3.6.3"); + + /** DSTU7624 in GMAC (GCM witout encryption) mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624gmac_128 = UaOid.Branch("1.1.3.7.1"); + /** DSTU7624 in GMAC (GCM witout encryption) mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624gmac_256 = UaOid.Branch("1.1.3.7.2"); + /** DSTU7624 in GMAC (GCM witout encryption) mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624gmac_512 = UaOid.Branch("1.1.3.7.3"); + + /** DSTU7624 in CCM mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ccm_128 = UaOid.Branch("1.1.3.8.1"); + /** DSTU7624 in CCM mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ccm_256 = UaOid.Branch("1.1.3.8.2"); + /** DSTU7624 in CCM mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624ccm_512 = UaOid.Branch("1.1.3.8.3"); + + /** DSTU7624 in XTS mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624xts_128 = UaOid.Branch("1.1.3.9.1"); + /** DSTU7624 in XTS mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624xts_256 = UaOid.Branch("1.1.3.9.2"); + /** DSTU7624 in XTS mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624xts_512 = UaOid.Branch("1.1.3.9.3"); + + /** DSTU7624 in key wrap (KW) mode with 128 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624kw_128 = UaOid.Branch("1.1.3.10.1"); + /** DSTU7624 in key wrap (KW) mode with 256 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624kw_256 = UaOid.Branch("1.1.3.10.2"); + /** DSTU7624 in key wrap (KW) mode with 512 bit block/key presentation */ + public static readonly DerObjectIdentifier dstu7624kw_512 = UaOid.Branch("1.1.3.10.3"); + } +} diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs new file mode 100644
index 000000000..b8e4f272e --- /dev/null +++ b/crypto/src/crypto/digests/Blake2bDigest.cs
@@ -0,0 +1,531 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /* The BLAKE2 cryptographic hash function was designed by Jean- + Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian + Winnerlein. + + Reference Implementation and Description can be found at: https://blake2.net/ + Internet Draft: https://tools.ietf.org/html/draft-saarinen-blake2-02 + + This implementation does not support the Tree Hashing Mode. + + For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based + message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2. + + Algorithm | Target | Collision | Hash | Hash ASN.1 | + Identifier | Arch | Security | nn | OID Suffix | + ---------------+--------+-----------+------+------------+ + id-blake2b160 | 64-bit | 2**80 | 20 | x.1.20 | + id-blake2b256 | 64-bit | 2**128 | 32 | x.1.32 | + id-blake2b384 | 64-bit | 2**192 | 48 | x.1.48 | + id-blake2b512 | 64-bit | 2**256 | 64 | x.1.64 | + ---------------+--------+-----------+------+------------+ + */ + + /** + * Implementation of the cryptographic hash function Blakbe2b. + * <p> + * Blake2b offers a built-in keying mechanism to be used directly + * for authentication ("Prefix-MAC") rather than a HMAC construction. + * <p> + * Blake2b offers a built-in support for a salt for randomized hashing + * and a personal string for defining a unique hash function for each application. + * <p> + * BLAKE2b is optimized for 64-bit platforms and produces digests of any size + * between 1 and 64 bytes. + */ + public class Blake2bDigest + : IDigest + { + // Blake2b Initialization Vector: + private static readonly ulong[] blake2b_IV = + // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19. + // The same as SHA-512 IV. + { + 0x6a09e667f3bcc908UL, 0xbb67ae8584caa73bUL, 0x3c6ef372fe94f82bUL, + 0xa54ff53a5f1d36f1UL, 0x510e527fade682d1UL, 0x9b05688c2b3e6c1fUL, + 0x1f83d9abfb41bd6bUL, 0x5be0cd19137e2179UL + }; + + // Message word permutations: + private static readonly byte[,] blake2b_sigma = + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } + }; + + private const int ROUNDS = 12; // to use for Catenas H' + private const int BLOCK_LENGTH_BYTES = 128;// bytes + + // General parameters: + private int digestLength = 64; // 1- 64 bytes + private int keyLength = 0; // 0 - 64 bytes for keyed hashing for MAC + private byte[] salt = null;// new byte[16]; + private byte[] personalization = null;// new byte[16]; + + // the key + private byte[] key = null; + + // Tree hashing parameters: + // Because this class does not implement the Tree Hashing Mode, + // these parameters can be treated as constants (see init() function) + /* + * private int fanout = 1; // 0-255 private int depth = 1; // 1 - 255 + * private int leafLength= 0; private long nodeOffset = 0L; private int + * nodeDepth = 0; private int innerHashLength = 0; + */ + + // whenever this buffer overflows, it will be processed + // in the Compress() function. + // For performance issues, long messages will not use this buffer. + private byte[] buffer = null;// new byte[BLOCK_LENGTH_BYTES]; + // Position of last inserted byte: + private int bufferPos = 0;// a value from 0 up to 128 + + private ulong[] internalState = new ulong[16]; // In the Blake2b paper it is + // called: v + private ulong[] chainValue = null; // state vector, in the Blake2b paper it + // is called: h + + private ulong t0 = 0UL; // holds last significant bits, counter (counts bytes) + private ulong t1 = 0UL; // counter: Length up to 2^128 are supported + private ulong f0 = 0UL; // finalization flag, for last block: ~0L + + // For Tree Hashing Mode, not used here: + // private long f1 = 0L; // finalization flag, for last node: ~0L + + public Blake2bDigest() + : this(512) + { + } + + public Blake2bDigest(Blake2bDigest digest) + { + this.bufferPos = digest.bufferPos; + this.buffer = Arrays.Clone(digest.buffer); + this.keyLength = digest.keyLength; + this.key = Arrays.Clone(digest.key); + this.digestLength = digest.digestLength; + this.chainValue = Arrays.Clone(digest.chainValue); + this.personalization = Arrays.Clone(digest.personalization); + this.salt = Arrays.Clone(digest.salt); + this.t0 = digest.t0; + this.t1 = digest.t1; + this.f0 = digest.f0; + } + + /** + * Basic sized constructor - size in bits. + * + * @param digestSize size of the digest in bits + */ + public Blake2bDigest(int digestSize) + { + if (digestSize != 160 && digestSize != 256 && digestSize != 384 && digestSize != 512) + throw new ArgumentException("BLAKE2b digest restricted to one of [160, 256, 384, 512]"); + + buffer = new byte[BLOCK_LENGTH_BYTES]; + keyLength = 0; + this.digestLength = digestSize / 8; + Init(); + } + + /** + * Blake2b for authentication ("Prefix-MAC mode"). + * After calling the doFinal() method, the key will + * remain to be used for further computations of + * this instance. + * The key can be overwritten using the clearKey() method. + * + * @param key A key up to 64 bytes or null + */ + public Blake2bDigest(byte[] key) + { + buffer = new byte[BLOCK_LENGTH_BYTES]; + if (key != null) + { + this.key = new byte[key.Length]; + Array.Copy(key, 0, this.key, 0, key.Length); + + if (key.Length > 64) + throw new ArgumentException("Keys > 64 are not supported"); + + keyLength = key.Length; + Array.Copy(key, 0, buffer, 0, key.Length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + digestLength = 64; + Init(); + } + + /** + * Blake2b with key, required digest length (in bytes), salt and personalization. + * After calling the doFinal() method, the key, the salt and the personal string + * will remain and might be used for further computations with this instance. + * The key can be overwritten using the clearKey() method, the salt (pepper) + * can be overwritten using the clearSalt() method. + * + * @param key A key up to 64 bytes or null + * @param digestLength from 1 up to 64 bytes + * @param salt 16 bytes or null + * @param personalization 16 bytes or null + */ + public Blake2bDigest(byte[] key, int digestLength, byte[] salt, byte[] personalization) + { + if (digestLength < 1 || digestLength > 64) + throw new ArgumentException("Invalid digest length (required: 1 - 64)"); + + this.digestLength = digestLength; + this.buffer = new byte[BLOCK_LENGTH_BYTES]; + + if (salt != null) + { + if (salt.Length != 16) + throw new ArgumentException("salt length must be exactly 16 bytes"); + + this.salt = new byte[16]; + Array.Copy(salt, 0, this.salt, 0, salt.Length); + } + if (personalization != null) + { + if (personalization.Length != 16) + throw new ArgumentException("personalization length must be exactly 16 bytes"); + + this.personalization = new byte[16]; + Array.Copy(personalization, 0, this.personalization, 0, personalization.Length); + } + if (key != null) + { + if (key.Length > 64) + throw new ArgumentException("Keys > 64 are not supported"); + + this.key = new byte[key.Length]; + Array.Copy(key, 0, this.key, 0, key.Length); + + keyLength = key.Length; + Array.Copy(key, 0, buffer, 0, key.Length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + Init(); + } + + // initialize chainValue + private void Init() + { + if (chainValue == null) + { + chainValue = new ulong[8]; + + chainValue[0] = blake2b_IV[0] ^ (ulong)(digestLength | (keyLength << 8) | 0x1010000); + + // 0x1010000 = ((fanout << 16) | (depth << 24) | (leafLength << + // 32)); + // with fanout = 1; depth = 0; leafLength = 0; + chainValue[1] = blake2b_IV[1];// ^ nodeOffset; with nodeOffset = 0; + chainValue[2] = blake2b_IV[2];// ^ ( nodeDepth | (innerHashLength << 8) ); + // with nodeDepth = 0; innerHashLength = 0; + + chainValue[3] = blake2b_IV[3]; + + chainValue[4] = blake2b_IV[4]; + chainValue[5] = blake2b_IV[5]; + if (salt != null) + { + chainValue[4] ^= Pack.LE_To_UInt64(salt, 0); + chainValue[5] ^= Pack.LE_To_UInt64(salt, 8); + } + + chainValue[6] = blake2b_IV[6]; + chainValue[7] = blake2b_IV[7]; + if (personalization != null) + { + chainValue[6] ^= Pack.LE_To_UInt64(personalization, 0); + chainValue[7] ^= Pack.LE_To_UInt64(personalization, 8); + } + } + } + + private void InitializeInternalState() + { + // initialize v: + Array.Copy(chainValue, 0, internalState, 0, chainValue.Length); + Array.Copy(blake2b_IV, 0, internalState, chainValue.Length, 4); + internalState[12] = t0 ^ blake2b_IV[4]; + internalState[13] = t1 ^ blake2b_IV[5]; + internalState[14] = f0 ^ blake2b_IV[6]; + internalState[15] = blake2b_IV[7];// ^ f1 with f1 = 0 + } + + /** + * update the message digest with a single byte. + * + * @param b the input byte to be entered. + */ + public virtual void Update(byte b) + { + int remainingLength = 0; // left bytes of buffer + + // process the buffer if full else add to buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength == 0) + { // full buffer + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^64 + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// clear buffer + buffer[0] = b; + bufferPos = 1; + } + else + { + buffer[bufferPos] = b; + bufferPos++; + return; + } + } + + /** + * update the message digest with a block of bytes. + * + * @param message the byte array containing the data. + * @param offset the offset into the byte array where the data starts. + * @param len the length of the data. + */ + public virtual void BlockUpdate(byte[] message, int offset, int len) + { + if (message == null || len == 0) + return; + + int remainingLength = 0; // left bytes of buffer + + if (bufferPos != 0) + { // commenced, incomplete buffer + + // complete the buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength < len) + { // full buffer + at least 1 byte + Array.Copy(message, offset, buffer, bufferPos, + remainingLength); + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^64 + t1++; + } + Compress(buffer, 0); + bufferPos = 0; + Array.Clear(buffer, 0, buffer.Length);// clear buffer + } + else + { + Array.Copy(message, offset, buffer, bufferPos, len); + bufferPos += len; + return; + } + } + + // process blocks except last block (also if last block is full) + int messagePos; + int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES; + for (messagePos = offset + remainingLength; messagePos < blockWiseLastPos; messagePos += BLOCK_LENGTH_BYTES) + { // block wise 128 bytes + // without buffer: + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { + t1++; + } + Compress(message, messagePos); + } + + // fill the buffer with left bytes, this might be a full block + Array.Copy(message, messagePos, buffer, 0, offset + len + - messagePos); + bufferPos += offset + len - messagePos; + } + + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * Key, salt and personal string remain. + * + * @param out the array the digest is to be copied into. + * @param outOffset the offset into the out array the digest is to start at. + */ + public virtual int DoFinal(byte[] output, int outOffset) + { + f0 = 0xFFFFFFFFFFFFFFFFUL; + t0 += (ulong)bufferPos; + if (bufferPos > 0 && t0 == 0) + { + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null + Array.Clear(internalState, 0, internalState.Length); + + for (int i = 0; i < chainValue.Length && (i * 8 < digestLength); i++) + { + byte[] bytes = Pack.UInt64_To_LE(chainValue[i]); + + if (i * 8 < digestLength - 8) + { + Array.Copy(bytes, 0, output, outOffset + i * 8, 8); + } + else + { + Array.Copy(bytes, 0, output, outOffset + i * 8, digestLength - (i * 8)); + } + } + + Array.Clear(chainValue, 0, chainValue.Length); + + Reset(); + + return digestLength; + } + + /** + * Reset the digest back to it's initial state. + * The key, the salt and the personal string will + * remain for further computations. + */ + public virtual void Reset() + { + bufferPos = 0; + f0 = 0L; + t0 = 0L; + t1 = 0L; + chainValue = null; + Array.Clear(buffer, 0, buffer.Length); + if (key != null) + { + Array.Copy(key, 0, buffer, 0, key.Length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + Init(); + } + + private void Compress(byte[] message, int messagePos) + { + InitializeInternalState(); + + ulong[] m = new ulong[16]; + for (int j = 0; j < 16; j++) + { + m[j] = Pack.LE_To_UInt64(message, messagePos + j * 8); + } + + for (int round = 0; round < ROUNDS; round++) + { + // G apply to columns of internalState:m[blake2b_sigma[round][2 * blockPos]] /+1 + G(m[blake2b_sigma[round,0]], m[blake2b_sigma[round,1]], 0, 4, 8, 12); + G(m[blake2b_sigma[round,2]], m[blake2b_sigma[round,3]], 1, 5, 9, 13); + G(m[blake2b_sigma[round,4]], m[blake2b_sigma[round,5]], 2, 6, 10, 14); + G(m[blake2b_sigma[round,6]], m[blake2b_sigma[round,7]], 3, 7, 11, 15); + // G apply to diagonals of internalState: + G(m[blake2b_sigma[round,8]], m[blake2b_sigma[round,9]], 0, 5, 10, 15); + G(m[blake2b_sigma[round,10]], m[blake2b_sigma[round,11]], 1, 6, 11, 12); + G(m[blake2b_sigma[round,12]], m[blake2b_sigma[round,13]], 2, 7, 8, 13); + G(m[blake2b_sigma[round,14]], m[blake2b_sigma[round,15]], 3, 4, 9, 14); + } + + // update chain values: + for (int offset = 0; offset < chainValue.Length; offset++) + { + chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8]; + } + } + + private void G(ulong m1, ulong m2, int posA, int posB, int posC, int posD) + { + internalState[posA] = internalState[posA] + internalState[posB] + m1; + internalState[posD] = Rotr64(internalState[posD] ^ internalState[posA], 32); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = Rotr64(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE + internalState[posA] = internalState[posA] + internalState[posB] + m2; + internalState[posD] = Rotr64(internalState[posD] ^ internalState[posA], 16); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = Rotr64(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE + } + + private static ulong Rotr64(ulong x, int rot) + { + return x >> rot | x << -rot; + } + + /** + * return the algorithm name + * + * @return the algorithm name + */ + public virtual string AlgorithmName + { + get { return "BLAKE2b"; } + } + + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + public virtual int GetDigestSize() + { + return digestLength; + } + + /** + * Return the size in bytes of the internal buffer the digest applies it's compression + * function to. + * + * @return byte length of the digests internal buffer. + */ + public virtual int GetByteLength() + { + return BLOCK_LENGTH_BYTES; + } + + /** + * Overwrite the key + * if it is no longer used (zeroization) + */ + public virtual void ClearKey() + { + if (key != null) + { + Array.Clear(key, 0, key.Length); + Array.Clear(buffer, 0, buffer.Length); + } + } + + /** + * Overwrite the salt (pepper) if it + * is secret and no longer used (zeroization) + */ + public virtual void ClearSalt() + { + if (salt != null) + { + Array.Clear(salt, 0, salt.Length); + } + } + } +} diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs new file mode 100644
index 000000000..f31032874 --- /dev/null +++ b/crypto/src/crypto/digests/Blake2sDigest.cs
@@ -0,0 +1,552 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /* + The BLAKE2 cryptographic hash function was designed by Jean- + Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian + Winnerlein. + + Reference Implementation and Description can be found at: https://blake2.net/ + RFC: https://tools.ietf.org/html/rfc7693 + + This implementation does not support the Tree Hashing Mode. + + For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based + message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2. + + Algorithm | Target | Collision | Hash | Hash ASN.1 | + Identifier | Arch | Security | nn | OID Suffix | + ---------------+--------+-----------+------+------------+ + id-blake2s128 | 32-bit | 2**64 | 16 | x.2.4 | + id-blake2s160 | 32-bit | 2**80 | 20 | x.2.5 | + id-blake2s224 | 32-bit | 2**112 | 28 | x.2.7 | + id-blake2s256 | 32-bit | 2**128 | 32 | x.2.8 | + ---------------+--------+-----------+------+------------+ + */ + + /** + * Implementation of the cryptographic hash function BLAKE2s. + * <p/> + * BLAKE2s offers a built-in keying mechanism to be used directly + * for authentication ("Prefix-MAC") rather than a HMAC construction. + * <p/> + * BLAKE2s offers a built-in support for a salt for randomized hashing + * and a personal string for defining a unique hash function for each application. + * <p/> + * BLAKE2s is optimized for 32-bit platforms and produces digests of any size + * between 1 and 32 bytes. + */ + public class Blake2sDigest + : IDigest + { + /** + * BLAKE2s Initialization Vector + **/ + private static readonly uint[] blake2s_IV = + // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19. + // The same as SHA-256 IV. + { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, + 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19 + }; + + /** + * Message word permutations + **/ + private static readonly byte[,] blake2s_sigma = + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 } + }; + + private const int ROUNDS = 10; // to use for Catenas H' + private const int BLOCK_LENGTH_BYTES = 64;// bytes + + // General parameters: + private int digestLength = 32; // 1- 32 bytes + private int keyLength = 0; // 0 - 32 bytes for keyed hashing for MAC + private byte[] salt = null; + private byte[] personalization = null; + private byte[] key = null; + + // Tree hashing parameters: + // Because this class does not implement the Tree Hashing Mode, + // these parameters can be treated as constants (see Init() function) + /* + * private int fanout = 1; // 0-255 + * private int depth = 1; // 1 - 255 + * private int leafLength= 0; + * private long nodeOffset = 0L; + * private int nodeDepth = 0; + * private int innerHashLength = 0; + */ + + /** + * Whenever this buffer overflows, it will be processed in the Compress() + * function. For performance issues, long messages will not use this buffer. + */ + private byte[] buffer = null; + /** + * Position of last inserted byte + **/ + private int bufferPos = 0;// a value from 0 up to BLOCK_LENGTH_BYTES + + /** + * Internal state, in the BLAKE2 paper it is called v + **/ + private uint[] internalState = new uint[16]; + /** + * State vector, in the BLAKE2 paper it is called h + **/ + private uint[] chainValue = null; + + // counter (counts bytes): Length up to 2^64 are supported + /** + * holds least significant bits of counter + **/ + private uint t0 = 0; + /** + * holds most significant bits of counter + **/ + private uint t1 = 0; + /** + * finalization flag, for last block: ~0 + **/ + private uint f0 = 0; + + // For Tree Hashing Mode, not used here: + // private long f1 = 0L; // finalization flag, for last node: ~0L + + /** + * BLAKE2s-256 for hashing. + */ + public Blake2sDigest() + : this(256) + { + } + + public Blake2sDigest(Blake2sDigest digest) + { + this.bufferPos = digest.bufferPos; + this.buffer = Arrays.Clone(digest.buffer); + this.keyLength = digest.keyLength; + this.key = Arrays.Clone(digest.key); + this.digestLength = digest.digestLength; + this.chainValue = Arrays.Clone(digest.chainValue); + this.personalization = Arrays.Clone(digest.personalization); + } + + /** + * BLAKE2s for hashing. + * + * @param digestBits the desired digest length in bits. Must be one of + * [128, 160, 224, 256]. + */ + public Blake2sDigest(int digestBits) + { + if (digestBits != 128 && digestBits != 160 && digestBits != 224 && digestBits != 256) + throw new ArgumentException("BLAKE2s digest restricted to one of [128, 160, 224, 256]"); + + buffer = new byte[BLOCK_LENGTH_BYTES]; + keyLength = 0; + digestLength = digestBits / 8; + Init(); + } + + /** + * BLAKE2s for authentication ("Prefix-MAC mode"). + * <p/> + * After calling the doFinal() method, the key will remain to be used for + * further computations of this instance. The key can be overwritten using + * the clearKey() method. + * + * @param key a key up to 32 bytes or null + */ + public Blake2sDigest(byte[] key) + { + buffer = new byte[BLOCK_LENGTH_BYTES]; + if (key != null) + { + if (key.Length > 32) + throw new ArgumentException("Keys > 32 are not supported"); + + this.key = new byte[key.Length]; + Array.Copy(key, 0, this.key, 0, key.Length); + + keyLength = key.Length; + Array.Copy(key, 0, buffer, 0, key.Length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + digestLength = 32; + Init(); + } + + /** + * BLAKE2s with key, required digest length, salt and personalization. + * <p/> + * After calling the doFinal() method, the key, the salt and the personal + * string will remain and might be used for further computations with this + * instance. The key can be overwritten using the clearKey() method, the + * salt (pepper) can be overwritten using the clearSalt() method. + * + * @param key a key up to 32 bytes or null + * @param digestBytes from 1 up to 32 bytes + * @param salt 8 bytes or null + * @param personalization 8 bytes or null + */ + public Blake2sDigest(byte[] key, int digestBytes, byte[] salt, + byte[] personalization) + { + if (digestBytes < 1 || digestBytes > 32) + throw new ArgumentException("Invalid digest length (required: 1 - 32)"); + + this.digestLength = digestBytes; + this.buffer = new byte[BLOCK_LENGTH_BYTES]; + + if (salt != null) + { + if (salt.Length != 8) + throw new ArgumentException("Salt length must be exactly 8 bytes"); + + this.salt = new byte[8]; + Array.Copy(salt, 0, this.salt, 0, salt.Length); + } + if (personalization != null) + { + if (personalization.Length != 8) + throw new ArgumentException("Personalization length must be exactly 8 bytes"); + + this.personalization = new byte[8]; + Array.Copy(personalization, 0, this.personalization, 0, personalization.Length); + } + if (key != null) + { + if (key.Length > 32) + throw new ArgumentException("Keys > 32 bytes are not supported"); + + this.key = new byte[key.Length]; + Array.Copy(key, 0, this.key, 0, key.Length); + + keyLength = key.Length; + Array.Copy(key, 0, buffer, 0, key.Length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + Init(); + } + + // initialize chainValue + private void Init() + { + if (chainValue == null) + { + chainValue = new uint[8]; + + chainValue[0] = blake2s_IV[0] ^ (uint)(digestLength | (keyLength << 8) | 0x1010000); + // 0x1010000 = ((fanout << 16) | (depth << 24)); + // with fanout = 1; depth = 0; + chainValue[1] = blake2s_IV[1];// ^ leafLength; with leafLength = 0; + chainValue[2] = blake2s_IV[2];// ^ nodeOffset; with nodeOffset = 0; + chainValue[3] = blake2s_IV[3];// ^ ( (nodeOffset << 32) | (nodeDepth << 16) | (innerHashLength << 24) ); + // with nodeDepth = 0; innerHashLength = 0; + + chainValue[4] = blake2s_IV[4]; + chainValue[5] = blake2s_IV[5]; + if (salt != null) + { + chainValue[4] ^= Pack.LE_To_UInt32(salt, 0); + chainValue[5] ^= Pack.LE_To_UInt32(salt, 4); + } + + chainValue[6] = blake2s_IV[6]; + chainValue[7] = blake2s_IV[7]; + if (personalization != null) + { + chainValue[6] ^= Pack.LE_To_UInt32(personalization, 0); + chainValue[7] ^= Pack.LE_To_UInt32(personalization, 4); + } + } + } + + private void InitializeInternalState() + { + // initialize v: + Array.Copy(chainValue, 0, internalState, 0, chainValue.Length); + Array.Copy(blake2s_IV, 0, internalState, chainValue.Length, 4); + internalState[12] = t0 ^ blake2s_IV[4]; + internalState[13] = t1 ^ blake2s_IV[5]; + internalState[14] = f0 ^ blake2s_IV[6]; + internalState[15] = blake2s_IV[7];// ^ f1 with f1 = 0 + } + + /** + * Update the message digest with a single byte. + * + * @param b the input byte to be entered. + */ + public virtual void Update(byte b) + { + int remainingLength; // left bytes of buffer + + // process the buffer if full else add to buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength == 0) + { // full buffer + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^32 + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// clear buffer + buffer[0] = b; + bufferPos = 1; + } + else + { + buffer[bufferPos] = b; + bufferPos++; + } + } + + /** + * Update the message digest with a block of bytes. + * + * @param message the byte array containing the data. + * @param offset the offset into the byte array where the data starts. + * @param len the length of the data. + */ + public virtual void BlockUpdate(byte[] message, int offset, int len) + { + if (message == null || len == 0) + return; + + int remainingLength = 0; // left bytes of buffer + + if (bufferPos != 0) + { // commenced, incomplete buffer + + // complete the buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength < len) + { // full buffer + at least 1 byte + Array.Copy(message, offset, buffer, bufferPos, remainingLength); + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^32 + t1++; + } + Compress(buffer, 0); + bufferPos = 0; + Array.Clear(buffer, 0, buffer.Length);// clear buffer + } + else + { + Array.Copy(message, offset, buffer, bufferPos, len); + bufferPos += len; + return; + } + } + + // process blocks except last block (also if last block is full) + int messagePos; + int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES; + for (messagePos = offset + remainingLength; + messagePos < blockWiseLastPos; + messagePos += BLOCK_LENGTH_BYTES) + { // block wise 64 bytes + // without buffer: + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { + t1++; + } + Compress(message, messagePos); + } + + // fill the buffer with left bytes, this might be a full block + Array.Copy(message, messagePos, buffer, 0, offset + len + - messagePos); + bufferPos += offset + len - messagePos; + } + + /** + * Close the digest, producing the final digest value. The doFinal() call + * leaves the digest reset. Key, salt and personal string remain. + * + * @param out the array the digest is to be copied into. + * @param outOffset the offset into the out array the digest is to start at. + */ + public virtual int DoFinal(byte[] output, int outOffset) + { + f0 = 0xFFFFFFFFU; + t0 += (uint)bufferPos; + // bufferPos may be < 64, so (t0 == 0) does not work + // for 2^32 < message length > 2^32 - 63 + if ((t0 < 0) && (bufferPos > -t0)) + { + t1++; + } + Compress(buffer, 0); + Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null + Array.Clear(internalState, 0, internalState.Length); + + for (int i = 0; i < chainValue.Length && (i * 4 < digestLength); i++) + { + byte[] bytes = Pack.UInt32_To_LE(chainValue[i]); + + if (i * 4 < digestLength - 4) + { + Array.Copy(bytes, 0, output, outOffset + i * 4, 4); + } + else + { + Array.Copy(bytes, 0, output, outOffset + i * 4, digestLength - (i * 4)); + } + } + + Array.Clear(chainValue, 0, chainValue.Length); + + Reset(); + + return digestLength; + } + + /** + * Reset the digest back to its initial state. The key, the salt and the + * personal string will remain for further computations. + */ + public virtual void Reset() + { + bufferPos = 0; + f0 = 0; + t0 = 0; + t1 = 0; + chainValue = null; + Array.Clear(buffer, 0, buffer.Length); + if (key != null) + { + Array.Copy(key, 0, buffer, 0, key.Length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + Init(); + } + + private void Compress(byte[] message, int messagePos) + { + InitializeInternalState(); + + uint[] m = new uint[16]; + for (int j = 0; j < 16; j++) + { + m[j] = Pack.LE_To_UInt32(message, messagePos + j * 4); + } + + for (int round = 0; round < ROUNDS; round++) + { + + // G apply to columns of internalState:m[blake2s_sigma[round][2 * + // blockPos]] /+1 + G(m[blake2s_sigma[round,0]], m[blake2s_sigma[round,1]], 0, 4, 8, 12); + G(m[blake2s_sigma[round,2]], m[blake2s_sigma[round,3]], 1, 5, 9, 13); + G(m[blake2s_sigma[round,4]], m[blake2s_sigma[round,5]], 2, 6, 10, 14); + G(m[blake2s_sigma[round,6]], m[blake2s_sigma[round,7]], 3, 7, 11, 15); + // G apply to diagonals of internalState: + G(m[blake2s_sigma[round,8]], m[blake2s_sigma[round,9]], 0, 5, 10, 15); + G(m[blake2s_sigma[round,10]], m[blake2s_sigma[round,11]], 1, 6, 11, 12); + G(m[blake2s_sigma[round,12]], m[blake2s_sigma[round,13]], 2, 7, 8, 13); + G(m[blake2s_sigma[round,14]], m[blake2s_sigma[round,15]], 3, 4, 9, 14); + } + + // update chain values: + for (int offset = 0; offset < chainValue.Length; offset++) + { + chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8]; + } + } + + private void G(uint m1, uint m2, int posA, int posB, int posC, int posD) + { + internalState[posA] = internalState[posA] + internalState[posB] + m1; + internalState[posD] = rotr32(internalState[posD] ^ internalState[posA], 16); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = rotr32(internalState[posB] ^ internalState[posC], 12); + internalState[posA] = internalState[posA] + internalState[posB] + m2; + internalState[posD] = rotr32(internalState[posD] ^ internalState[posA], 8); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = rotr32(internalState[posB] ^ internalState[posC], 7); + } + + private uint rotr32(uint x, int rot) + { + return x >> rot | x << -rot; + } + + /** + * Return the algorithm name. + * + * @return the algorithm name + */ + public virtual string AlgorithmName + { + get { return "BLAKE2s"; } + } + + /** + * Return the size in bytes of the digest produced by this message digest. + * + * @return the size in bytes of the digest produced by this message digest. + */ + public virtual int GetDigestSize() + { + return digestLength; + } + + /** + * Return the size in bytes of the internal buffer the digest applies its + * compression function to. + * + * @return byte length of the digest's internal buffer. + */ + public virtual int GetByteLength() + { + return BLOCK_LENGTH_BYTES; + } + + /** + * Overwrite the key if it is no longer used (zeroization). + */ + public virtual void ClearKey() + { + if (key != null) + { + Array.Clear(key, 0, key.Length); + Array.Clear(buffer, 0, buffer.Length); + } + } + + /** + * Overwrite the salt (pepper) if it is secret and no longer used + * (zeroization). + */ + public virtual void ClearSalt() + { + if (salt != null) + { + Array.Clear(salt, 0, salt.Length); + } + } + } +} diff --git a/crypto/src/crypto/digests/DSTU7564Digest.cs b/crypto/src/crypto/digests/DSTU7564Digest.cs
index e641af6c2..b2d90799d 100644 --- a/crypto/src/crypto/digests/DSTU7564Digest.cs +++ b/crypto/src/crypto/digests/DSTU7564Digest.cs
@@ -3,38 +3,36 @@ using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; -//using Org.BouncyCastle.Utilities; - -using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Crypto.Digests { /** * implementation of Ukrainian DSTU 7564 hash function */ - public class Dstu7564Digest : IDigest, IMemoable + public class Dstu7564Digest + : IDigest, IMemoable { - private const int ROWS = 8; - private const int BITS_IN_BYTE = 8; - private const int NB_512 = 8; //Number of 8-byte words in state for <=256-bit hash code. private const int NB_1024 = 16; //Number of 8-byte words in state for <=512-bit hash code. private const int NR_512 = 10; //Number of rounds for 512-bit state. private const int NR_1024 = 14; //Number of rounds for 1024-bit state. - private const int STATE_BYTE_SIZE_512 = ROWS * NB_512; - private const int STATE_BYTE_SIZE_1024 = ROWS * NB_1024; - private int hashSize; private int blockSize; + private int columns; private int rounds; - private byte[] padded_; - private byte[][] state_; - private ulong inputLength; + + private ulong[] state; + private ulong[] tempState1; + private ulong[] tempState2; + + // TODO Guard against 'inputBlocks' overflow (2^64 blocks) + private ulong inputBlocks; private int bufOff; private byte[] buf; @@ -48,20 +46,23 @@ namespace Org.BouncyCastle.Crypto.Digests this.hashSize = digest.hashSize; this.blockSize = digest.blockSize; - this.columns = digest.columns; this.rounds = digest.rounds; - - this.padded_ = Arrays.Clone(digest.padded_); - this.state_ = new byte[digest.state_.Length][]; - - for (int i = 0; i != this.state_.Length; i++) + if (columns > 0 && columns == digest.columns) { - this.state_[i] = Arrays.Clone(digest.state_[i]); + Array.Copy(digest.state, 0, state, 0, columns); + Array.Copy(digest.buf, 0, buf, 0, blockSize); + } + else + { + this.columns = digest.columns; + this.state = Arrays.Clone(digest.state); + this.tempState1 = new ulong[columns]; + this.tempState2 = new ulong[columns]; + this.buf = Arrays.Clone(digest.buf); } - this.inputLength = digest.inputLength; + this.inputBlocks = digest.inputBlocks; this.bufOff = digest.bufOff; - this.buf = Arrays.Clone(digest.buf); } public Dstu7564Digest(int hashSizeBits) @@ -72,34 +73,28 @@ namespace Org.BouncyCastle.Crypto.Digests } else { - throw new ArgumentException("Hash size is not recommended. Use 256 or 384 or 512 size"); + throw new ArgumentException("Hash size is not recommended. Use 256/384/512 instead"); } if (hashSizeBits > 256) { - this.blockSize = 1024 / 8; this.columns = NB_1024; this.rounds = NR_1024; - this.state_ = new byte[STATE_BYTE_SIZE_1024][]; } else { - this.blockSize = 512 / 8; this.columns = NB_512; this.rounds = NR_512; - this.state_ = new byte[STATE_BYTE_SIZE_512][]; } - for (int i = 0; i < state_.Length; i++) - { - this.state_[i] = new byte[columns]; - } + this.blockSize = columns << 3; - this.state_[0][0] = (byte)state_.Length; + this.state = new ulong[columns]; + this.state[0] = (ulong)blockSize; - this.hashSize = hashSizeBits / 8; + this.tempState1 = new ulong[columns]; + this.tempState2 = new ulong[columns]; - this.padded_ = null; this.buf = new byte[blockSize]; } @@ -108,128 +103,104 @@ namespace Org.BouncyCastle.Crypto.Digests get { return "DSTU7564"; } } + public virtual int GetDigestSize() + { + return hashSize; + } + + public virtual int GetByteLength() + { + return blockSize; + } + + public virtual void Update(byte input) + { + buf[bufOff++] = input; + if (bufOff == blockSize) + { + ProcessBlock(buf, 0); + bufOff = 0; + ++inputBlocks; + } + } + public virtual void BlockUpdate(byte[] input, int inOff, int length) { while (bufOff != 0 && length > 0) { Update(input[inOff++]); - length--; + --length; } if (length > 0) { - while (length > blockSize) + while (length >= blockSize) { ProcessBlock(input, inOff); inOff += blockSize; - inputLength += (ulong)blockSize; length -= blockSize; + ++inputBlocks; } while (length > 0) { Update(input[inOff++]); - length--; + --length; } } } - protected virtual byte[] Pad(byte[] input, int inOff, int length) - { - byte[] padded; - if (blockSize - length < 13) // terminator byte + 96 bits of length - { - padded = new byte[2 * blockSize]; - } - else - { - padded = new byte[blockSize]; - } - - Array.Copy(input, inOff, padded, 0, length); - padded[length] = 0x80; - Pack.UInt64_To_LE(inputLength * 8, padded, padded.Length - 12); - - return padded; - } - - protected virtual void ProcessBlock(byte[] input, int inOff) + public virtual int DoFinal(byte[] output, int outOff) { - byte[][] temp1 = new byte[columns][]; - byte[][] temp2 = new byte[columns][]; - - int pos = inOff; - for (int i = 0; i < columns; i++) + // Apply padding: terminator byte and 96-bit length field { - byte[] S = state_[i]; - byte[] T1 = temp1[i] = new byte[ROWS]; - byte[] T2 = temp2[i] = new byte[ROWS]; + int inputBytes = bufOff; + buf[bufOff++] = (byte)0x80; - for (int j = 0; j < ROWS; ++j) + int lenPos = blockSize - 12; + if (bufOff > lenPos) { - byte inVal = input[pos++]; - T1[j] = (byte)(S[j] ^ inVal); - T2[j] = inVal; + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + bufOff = 0; + ProcessBlock(buf, 0); } - } - P(temp1); - Q(temp2); - - for (int i = 0; i < columns; ++i) - { - byte[] S = state_[i], T1 = temp1[i], T2 = temp2[i]; - for (int j = 0; j < ROWS; ++j) + while (bufOff < lenPos) { - S[j] ^= (byte)(T1[j] ^ T2[j]); + buf[bufOff++] = 0; } - } - } - - public virtual int DoFinal(byte[] output, int outOff) - { - padded_ = Pad(buf, 0, bufOff); - - int paddedLen = padded_.Length; - int paddedOff = 0; - while (paddedLen != 0) - { - ProcessBlock(padded_, paddedOff); - paddedOff += blockSize; - paddedLen -= blockSize; + ulong c = ((inputBlocks & 0xFFFFFFFFUL) * (ulong)blockSize + (uint)inputBytes) << 3; + Pack.UInt32_To_LE((uint)c, buf, bufOff); + bufOff += 4; + c >>= 32; + c += ((inputBlocks >> 32) * (ulong)blockSize) << 3; + Pack.UInt64_To_LE(c, buf, bufOff); + // bufOff += 8; + ProcessBlock(buf, 0); } - byte[][] temp = new byte[STATE_BYTE_SIZE_1024][]; - for (int i = 0; i < state_.Length; i++) { - temp[i] = new byte[ROWS]; - Array.Copy(state_[i], temp[i], ROWS); - } + Array.Copy(state, 0, tempState1, 0, columns); - P(temp); + P(tempState1); - for (int i = 0; i < ROWS; ++i) - { - for (int j = 0; j < columns; ++j) + for (int col = 0; col < columns; ++col) { - state_[j][i] ^= temp[j][i]; + state[col] ^= tempState1[col]; } } - byte[] stateLine = new byte[ROWS * columns]; - int stateLineIndex = 0; - for (int j = 0; j < columns; ++j) + int neededColumns = hashSize / 8; + for (int col = columns - neededColumns; col < columns; ++col) { - for (int i = 0; i < ROWS; ++i) - { - stateLine[stateLineIndex] = state_[j][i]; - stateLineIndex++; - } + Pack.UInt64_To_LE(state[col], output, outOff); + outOff += 8; } - Array.Copy(stateLine, stateLine.Length - hashSize, output, outOff, hashSize); - Reset(); return hashSize; @@ -237,183 +208,313 @@ namespace Org.BouncyCastle.Crypto.Digests public virtual void Reset() { - for (int bufferIndex = 0; bufferIndex < state_.Length; bufferIndex++) - { - state_[bufferIndex] = new byte[columns]; - } + Array.Clear(state, 0, state.Length); + state[0] = (ulong)blockSize; - state_[0][0] = (byte)state_.Length; - - inputLength = 0; + inputBlocks = 0; bufOff = 0; - - Arrays.Fill(buf, (byte)0); - - if (padded_ != null) - { - Arrays.Fill(padded_, (byte)0); - } } - public virtual int GetDigestSize() + protected virtual void ProcessBlock(byte[] input, int inOff) { - return hashSize; - } + int pos = inOff; + for (int col = 0; col < columns; ++col) + { + ulong word = Pack.LE_To_UInt64(input, pos); + pos += 8; - public virtual int GetByteLength() - { - return blockSize; - } + tempState1[col] = state[col] ^ word; + tempState2[col] = word; + } - public virtual void Update(byte input) - { - buf[bufOff++] = input; - if (bufOff == blockSize) + P(tempState1); + Q(tempState2); + + for (int col = 0; col < columns; ++col) { - ProcessBlock(buf, 0); - bufOff = 0; + state[col] ^= tempState1[col] ^ tempState2[col]; } - inputLength++; } - private void SubBytes(byte[][] state) + private void P(ulong[] s) { - int i, j; - for (i = 0; i < ROWS; ++i) + for (int round = 0; round < rounds; ++round) { - for (j = 0; j < columns; ++j) + ulong rc = (ulong)round; + + /* AddRoundConstants */ + for (int col = 0; col < columns; ++col) { - state[j][i] = sBoxes[i % 4][state[j][i]]; + s[col] ^= rc; + rc += 0x10L; } + + ShiftRows(s); + SubBytes(s); + MixColumns(s); } } - private void ShiftBytes(byte[][] state) + private void Q(ulong[] s) { - int i, j; - byte[] temp = new byte[NB_1024]; - int shift = -1; - for (i = 0; i < ROWS; ++i) + for (int round = 0; round < rounds; ++round) { - if ((i == ROWS - 1) && (columns == NB_1024)) - { - shift = 11; - } - else - { - ++shift; - } - for (j = 0; j < columns; ++j) - { - temp[(j + shift) % columns] = state[j][i]; - } - for (j = 0; j < columns; ++j) + /* AddRoundConstantsQ */ + ulong rc = ((ulong)(((columns - 1) << 4) ^ round) << 56) | 0x00F0F0F0F0F0F0F3UL; + + for (int col = 0; col < columns; ++col) { - state[j][i] = temp[j]; + s[col] += rc; + rc -= 0x1000000000000000L; } + + ShiftRows(s); + SubBytes(s); + MixColumns(s); } } - /* Pair-wise GF multiplication of 4 byte-pairs (at bits 0, 16, 32, 48 within x, y) */ - private static ulong MultiplyGFx4(ulong u, ulong v) + private static ulong MixColumn(ulong c) { - ulong r = u & ((v & 0x0001000100010001UL) * 0xFFFFUL); - - for (int i = 1; i < 8; ++i) - { - u <<= 1; - v >>= 1; - r ^= u & ((v & 0x0001000100010001L) * 0xFFFFL); - } - - // REDUCTION_POLYNOMIAL = 0x011d; /* x^8 + x^4 + x^3 + x^2 + 1 */ - - ulong hi = r & 0xFF00FF00FF00FF00UL; - r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8); - hi = r & 0x0F000F000F000F00UL; - r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8); - return r; + //// Calculate column multiplied by powers of 'x' + //ulong x0 = c; + //ulong x1 = ((x0 & 0x7F7F7F7F7F7F7F7FUL) << 1) ^ (((x0 & 0x8080808080808080UL) >> 7) * 0x1DUL); + //ulong x2 = ((x1 & 0x7F7F7F7F7F7F7F7FUL) << 1) ^ (((x1 & 0x8080808080808080UL) >> 7) * 0x1DUL); + //ulong x3 = ((x2 & 0x7F7F7F7F7F7F7F7FUL) << 1) ^ (((x2 & 0x8080808080808080UL) >> 7) * 0x1DUL); + + //// Calculate products with circulant matrix from (0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04) + //ulong m0 = x0; + //ulong m1 = x0; + //ulong m2 = x0 ^ x2; + //ulong m3 = x0; + //ulong m4 = x3; + //ulong m5 = x1 ^ x2; + //ulong m6 = x0 ^ x1 ^ x2; + //ulong m7 = x2; + + //// Assemble the rotated products + //return m0 + // ^ Rotate(8, m1) + // ^ Rotate(16, m2) + // ^ Rotate(24, m3) + // ^ Rotate(32, m4) + // ^ Rotate(40, m5) + // ^ Rotate(48, m6) + // ^ Rotate(56, m7); + + // Multiply elements by 'x' + ulong x1 = ((c & 0x7F7F7F7F7F7F7F7FUL) << 1) ^ (((c & 0x8080808080808080UL) >> 7) * 0x1DUL); + ulong u, v; + + u = Rotate(8, c) ^ c; + u ^= Rotate(16, u); + u ^= Rotate(48, c); + + v = u ^ c ^ x1; + + // Multiply elements by 'x^2' + v = ((v & 0x3F3F3F3F3F3F3F3FUL) << 2) ^ (((v & 0x8080808080808080UL) >> 6) * 0x1DUL) ^ (((v & 0x4040404040404040UL) >> 6) * 0x1DUL); + + return u ^ Rotate(32, v) ^ Rotate(40, x1) ^ Rotate(48, x1); } - private void MixColumns(byte[][] state) + private void MixColumns(ulong[] s) { for (int col = 0; col < columns; ++col) { - ulong colVal = Pack.LE_To_UInt64(state[col]); - ulong colEven = colVal & 0x00FF00FF00FF00FFUL; - ulong colOdd = (colVal >> 8) & 0x00FF00FF00FF00FFUL; - - //ulong rowMatrix = (mdsMatrix >> 8) | (mdsMatrix << 56); - ulong rowMatrix = mdsMatrix; - - ulong result = 0; - for (int row = 7; row >= 0; --row) - { - ulong product = MultiplyGFx4(colEven, rowMatrix & 0x00FF00FF00FF00FFUL); - - rowMatrix = (rowMatrix >> 8) | (rowMatrix << 56); - - product ^= MultiplyGFx4(colOdd, rowMatrix & 0x00FF00FF00FF00FFUL); - - product ^= (product >> 32); - product ^= (product >> 16); - - result <<= 8; - result |= (product & 0xFFUL); - } - - Pack.UInt64_To_LE(result, state[col]); + s[col] = MixColumn(s[col]); } } - private void AddRoundConstantP(byte[][] state, int round) + private static ulong Rotate(int n, ulong x) { - int i; - for (i = 0; i < columns; ++i) - { - state[i][0] ^= (byte)((i * 0x10) ^ round); - } + return (x >> n) | (x << -n); } - private void AddRoundConstantQ(byte[][] state, int round) + private void ShiftRows(ulong[] s) { - int j; - UInt64[] s = new UInt64[columns]; - - for (j = 0; j < columns; j++) + switch (columns) { - s[j] = Pack.LE_To_UInt64(state[j]); - - s[j] = s[j] + (0x00F0F0F0F0F0F0F3UL ^ ((((UInt64)(columns - j - 1) * 0x10UL) ^ (UInt64)round) << (7 * 8))); - - state[j] = Pack.UInt64_To_LE(s[j]); + case NB_512: + { + ulong c0 = s[0], c1 = s[1], c2 = s[2], c3 = s[3]; + ulong c4 = s[4], c5 = s[5], c6 = s[6], c7 = s[7]; + ulong d; + + d = (c0 ^ c4) & 0xFFFFFFFF00000000UL; c0 ^= d; c4 ^= d; + d = (c1 ^ c5) & 0x00FFFFFFFF000000UL; c1 ^= d; c5 ^= d; + d = (c2 ^ c6) & 0x0000FFFFFFFF0000UL; c2 ^= d; c6 ^= d; + d = (c3 ^ c7) & 0x000000FFFFFFFF00UL; c3 ^= d; c7 ^= d; + + d = (c0 ^ c2) & 0xFFFF0000FFFF0000UL; c0 ^= d; c2 ^= d; + d = (c1 ^ c3) & 0x00FFFF0000FFFF00UL; c1 ^= d; c3 ^= d; + d = (c4 ^ c6) & 0xFFFF0000FFFF0000UL; c4 ^= d; c6 ^= d; + d = (c5 ^ c7) & 0x00FFFF0000FFFF00UL; c5 ^= d; c7 ^= d; + + d = (c0 ^ c1) & 0xFF00FF00FF00FF00UL; c0 ^= d; c1 ^= d; + d = (c2 ^ c3) & 0xFF00FF00FF00FF00UL; c2 ^= d; c3 ^= d; + d = (c4 ^ c5) & 0xFF00FF00FF00FF00UL; c4 ^= d; c5 ^= d; + d = (c6 ^ c7) & 0xFF00FF00FF00FF00UL; c6 ^= d; c7 ^= d; + + s[0] = c0; s[1] = c1; s[2] = c2; s[3] = c3; + s[4] = c4; s[5] = c5; s[6] = c6; s[7] = c7; + break; } - } - - private void P(byte[][] state) - { - int i; - for (i = 0; i < rounds; ++i) + case NB_1024: { - AddRoundConstantP(state, i); - SubBytes(state); - ShiftBytes(state); - MixColumns(state); + ulong c00 = s[0], c01 = s[1], c02 = s[2], c03 = s[3]; + ulong c04 = s[4], c05 = s[5], c06 = s[6], c07 = s[7]; + ulong c08 = s[8], c09 = s[9], c10 = s[10], c11 = s[11]; + ulong c12 = s[12], c13 = s[13], c14 = s[14], c15 = s[15]; + ulong d; + + // NOTE: Row 7 is shifted by 11 + + d = (c00 ^ c08) & 0xFF00000000000000UL; c00 ^= d; c08 ^= d; + d = (c01 ^ c09) & 0xFF00000000000000UL; c01 ^= d; c09 ^= d; + d = (c02 ^ c10) & 0xFFFF000000000000UL; c02 ^= d; c10 ^= d; + d = (c03 ^ c11) & 0xFFFFFF0000000000UL; c03 ^= d; c11 ^= d; + d = (c04 ^ c12) & 0xFFFFFFFF00000000UL; c04 ^= d; c12 ^= d; + d = (c05 ^ c13) & 0x00FFFFFFFF000000UL; c05 ^= d; c13 ^= d; + d = (c06 ^ c14) & 0x00FFFFFFFFFF0000UL; c06 ^= d; c14 ^= d; + d = (c07 ^ c15) & 0x00FFFFFFFFFFFF00UL; c07 ^= d; c15 ^= d; + + d = (c00 ^ c04) & 0x00FFFFFF00000000UL; c00 ^= d; c04 ^= d; + d = (c01 ^ c05) & 0xFFFFFFFFFF000000UL; c01 ^= d; c05 ^= d; + d = (c02 ^ c06) & 0xFF00FFFFFFFF0000UL; c02 ^= d; c06 ^= d; + d = (c03 ^ c07) & 0xFF0000FFFFFFFF00UL; c03 ^= d; c07 ^= d; + d = (c08 ^ c12) & 0x00FFFFFF00000000UL; c08 ^= d; c12 ^= d; + d = (c09 ^ c13) & 0xFFFFFFFFFF000000UL; c09 ^= d; c13 ^= d; + d = (c10 ^ c14) & 0xFF00FFFFFFFF0000UL; c10 ^= d; c14 ^= d; + d = (c11 ^ c15) & 0xFF0000FFFFFFFF00UL; c11 ^= d; c15 ^= d; + + d = (c00 ^ c02) & 0xFFFF0000FFFF0000UL; c00 ^= d; c02 ^= d; + d = (c01 ^ c03) & 0x00FFFF0000FFFF00UL; c01 ^= d; c03 ^= d; + d = (c04 ^ c06) & 0xFFFF0000FFFF0000UL; c04 ^= d; c06 ^= d; + d = (c05 ^ c07) & 0x00FFFF0000FFFF00UL; c05 ^= d; c07 ^= d; + d = (c08 ^ c10) & 0xFFFF0000FFFF0000UL; c08 ^= d; c10 ^= d; + d = (c09 ^ c11) & 0x00FFFF0000FFFF00UL; c09 ^= d; c11 ^= d; + d = (c12 ^ c14) & 0xFFFF0000FFFF0000UL; c12 ^= d; c14 ^= d; + d = (c13 ^ c15) & 0x00FFFF0000FFFF00UL; c13 ^= d; c15 ^= d; + + d = (c00 ^ c01) & 0xFF00FF00FF00FF00UL; c00 ^= d; c01 ^= d; + d = (c02 ^ c03) & 0xFF00FF00FF00FF00UL; c02 ^= d; c03 ^= d; + d = (c04 ^ c05) & 0xFF00FF00FF00FF00UL; c04 ^= d; c05 ^= d; + d = (c06 ^ c07) & 0xFF00FF00FF00FF00UL; c06 ^= d; c07 ^= d; + d = (c08 ^ c09) & 0xFF00FF00FF00FF00UL; c08 ^= d; c09 ^= d; + d = (c10 ^ c11) & 0xFF00FF00FF00FF00UL; c10 ^= d; c11 ^= d; + d = (c12 ^ c13) & 0xFF00FF00FF00FF00UL; c12 ^= d; c13 ^= d; + d = (c14 ^ c15) & 0xFF00FF00FF00FF00UL; c14 ^= d; c15 ^= d; + + s[0] = c00; s[1] = c01; s[2] = c02; s[3] = c03; + s[4] = c04; s[5] = c05; s[6] = c06; s[7] = c07; + s[8] = c08; s[9] = c09; s[10] = c10; s[11] = c11; + s[12] = c12; s[13] = c13; s[14] = c14; s[15] = c15; + break; + } + default: + { + throw new InvalidOperationException("unsupported state size: only 512/1024 are allowed"); + } } } - private void Q(byte[][] state) + private void SubBytes(ulong[] s) { - int i; - for (i = 0; i < rounds; ++i) + for (int i = 0; i < columns; ++i) { - AddRoundConstantQ(state, i); - SubBytes(state); - ShiftBytes(state); - MixColumns(state); + ulong u = s[i]; + uint lo = (uint)u, hi = (uint)(u >> 32); + byte t0 = S0[lo & 0xFF]; + byte t1 = S1[(lo >> 8) & 0xFF]; + byte t2 = S2[(lo >> 16) & 0xFF]; + byte t3 = S3[lo >> 24]; + lo = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = S0[hi & 0xFF]; + byte t5 = S1[(hi >> 8) & 0xFF]; + byte t6 = S2[(hi >> 16) & 0xFF]; + byte t7 = S3[hi >> 24]; + hi = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + s[i] = (ulong)lo | ((ulong)hi << 32); } } + private static readonly byte[] S0 = new byte[] { + 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09, + 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39, + 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6, + 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1, + 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27, + 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41, + 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e, + 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55, + 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff, + 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1, + 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76, + 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26, + 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82, + 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8, + 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d, + 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80 + }; + + private static readonly byte[] S1 = new byte[] { + 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8, + 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d, + 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d, + 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc, + 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee, + 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca, + 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20, + 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f, + 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51, + 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98, + 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9, + 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05, + 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82, + 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad, + 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59, + 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7 + }; + + private static readonly byte[] S2 = new byte[] { + 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59, + 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1, + 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72, + 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90, + 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35, + 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48, + 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38, + 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33, + 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29, + 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83, + 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2, + 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43, + 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82, + 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91, + 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44, + 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67 + }; + + private static readonly byte[] S3 = new byte[] { + 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f, + 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd, + 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66, + 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf, + 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99, + 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71, + 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60, + 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b, + 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09, + 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2, + 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7, + 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a, + 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39, + 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef, + 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36, + 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61 + }; + public virtual IMemoable Copy() { return new Dstu7564Digest(this); @@ -425,87 +526,5 @@ namespace Org.BouncyCastle.Crypto.Digests CopyIn(d); } - - //private const ulong mdsMatrix = 0x0407060801050101UL; - private const ulong mdsMatrix = 0x0104070608010501UL; - - private static readonly byte[][] sBoxes = new byte[][] - { - new byte[] { - 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09, - 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39, - 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6, - 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1, - 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27, - 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41, - 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e, - 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55, - 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff, - 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1, - 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76, - 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26, - 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82, - 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8, - 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d, - 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80 - }, - - new byte[] { - 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8, - 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d, - 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d, - 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc, - 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee, - 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca, - 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20, - 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f, - 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51, - 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98, - 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9, - 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05, - 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82, - 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad, - 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59, - 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7 - }, - - new byte[]{ - 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59, - 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1, - 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72, - 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90, - 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35, - 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48, - 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38, - 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33, - 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29, - 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83, - 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2, - 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43, - 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82, - 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91, - 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44, - 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67 - }, - - new byte[]{ - 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f, - 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd, - 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66, - 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf, - 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99, - 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71, - 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60, - 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b, - 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09, - 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2, - 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7, - 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a, - 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39, - 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef, - 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36, - 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61 - } - }; } } diff --git a/crypto/src/crypto/engines/Dstu7624Engine.cs b/crypto/src/crypto/engines/Dstu7624Engine.cs
index 534a23bbf..4242d3a7a 100644 --- a/crypto/src/crypto/engines/Dstu7624Engine.cs +++ b/crypto/src/crypto/engines/Dstu7624Engine.cs
@@ -13,9 +13,6 @@ namespace Org.BouncyCastle.Crypto.Engines public class Dstu7624Engine : IBlockCipher { - private static readonly int BITS_IN_WORD = 64; - private static readonly int BITS_IN_BYTE = 8; - private ulong[] internalState; private ulong[] workingKey; private ulong[][] roundKeys; @@ -27,126 +24,91 @@ namespace Org.BouncyCastle.Crypto.Engines private int wordsInKey; /* Number of encryption rounds depending on key length */ - private static int ROUNDS_128 = 10; - private static int ROUNDS_256 = 14; - private static int ROUNDS_512 = 18; + private const int ROUNDS_128 = 10; + private const int ROUNDS_256 = 14; + private const int ROUNDS_512 = 18; - private int blockSizeBits; private int roundsAmount; private bool forEncryption; - private byte[] internalStateBytes; - private byte[] tempInternalStateBytes; - public Dstu7624Engine(int blockSizeBits) { /* DSTU7624 supports 128 | 256 | 512 key/block sizes */ if (blockSizeBits != 128 && blockSizeBits != 256 && blockSizeBits != 512) { - throw new ArgumentException("Unsupported block length: only 128/256/512 are allowed"); + throw new ArgumentException("unsupported block length: only 128/256/512 are allowed"); } - this.blockSizeBits = blockSizeBits; - wordsInBlock = blockSizeBits / BITS_IN_WORD; + wordsInBlock = blockSizeBits / 64; internalState = new ulong[wordsInBlock]; - - internalStateBytes = new byte[internalState.Length * 64 / BITS_IN_BYTE]; - tempInternalStateBytes = new byte[internalState.Length * 64 / BITS_IN_BYTE]; } #region INITIALIZATION + public virtual void Init(bool forEncryption, ICipherParameters parameters) { - if (parameters is KeyParameter) - { - this.forEncryption = forEncryption; - - byte[] keyBytes = ((KeyParameter)parameters).GetKey(); - int keyBitLength = keyBytes.Length * BITS_IN_BYTE; - int blockBitLength = wordsInBlock * BITS_IN_WORD; - - if (keyBitLength != 128 && keyBitLength != 256 && keyBitLength != 512) - { - throw new ArgumentException("unsupported key length: only 128/256/512 are allowed"); - } - - /* Limitations on key lengths depending on block lengths. See table 6.1 in standard */ - if (blockBitLength == 128) - { - if (keyBitLength == 512) - { - throw new ArgumentException("Unsupported key length"); - } - } - - if (blockBitLength == 256) - { - if (keyBitLength == 128) - { - throw new ArgumentException("Unsupported key length"); - } - } - - if (blockBitLength == 512) - { - if (keyBitLength != 512) - { - throw new ArgumentException("Unsupported key length"); - } - } + if (!(parameters is KeyParameter)) + throw new ArgumentException("Invalid parameter passed to Dstu7624Engine Init"); - switch (keyBitLength) - { - case 128: - roundsAmount = ROUNDS_128; - break; - case 256: - roundsAmount = ROUNDS_256; - break; - case 512: - roundsAmount = ROUNDS_512; - break; - } - - wordsInKey = keyBitLength / BITS_IN_WORD; - - /* +1 round key as defined in standard */ - roundKeys = new ulong[roundsAmount + 1][]; - for (int roundKeyIndex = 0; roundKeyIndex < roundKeys.Length; roundKeyIndex++) - { - roundKeys[roundKeyIndex] = new ulong[wordsInBlock]; - } + this.forEncryption = forEncryption; - workingKey = new ulong[wordsInKey]; + byte[] keyBytes = ((KeyParameter)parameters).GetKey(); + int keyBitLength = keyBytes.Length << 3; + int blockBitLength = wordsInBlock << 6; - if (keyBytes.Length != wordsInKey * BITS_IN_WORD / BITS_IN_BYTE) - { - throw new ArgumentException("Invalid key parameter passed to DSTU7624Engine init"); - } + if (keyBitLength != 128 && keyBitLength != 256 && keyBitLength != 512) + { + throw new ArgumentException("unsupported key length: only 128/256/512 are allowed"); + } - /* Unpack encryption key bytes to words */ - Pack.LE_To_UInt64(keyBytes, 0, workingKey); + /* Limitations on key lengths depending on block lengths. See table 6.1 in standard */ + if (keyBitLength != blockBitLength && keyBitLength != (2 * blockBitLength)) + { + throw new ArgumentException("Unsupported key length"); + } - ulong[] kt = new ulong[wordsInBlock]; + switch (keyBitLength) + { + case 128: + roundsAmount = ROUNDS_128; + break; + case 256: + roundsAmount = ROUNDS_256; + break; + case 512: + roundsAmount = ROUNDS_512; + break; + } - KeyExpandKT(workingKey, kt); + wordsInKey = keyBitLength / 64; - KeyExpandEven(workingKey, kt); + /* +1 round key as defined in standard */ + roundKeys = new ulong[roundsAmount + 1][]; + for (int roundKeyIndex = 0; roundKeyIndex < roundKeys.Length; roundKeyIndex++) + { + roundKeys[roundKeyIndex] = new ulong[wordsInBlock]; + } - KeyExpandOdd(); + workingKey = new ulong[wordsInKey]; - } - else if (parameters != null) + if (keyBytes.Length != wordsInKey * 8) { - throw new ArgumentException("invalid parameter passed to Dstu7624 init - " - + Platform.GetTypeName(parameters)); + throw new ArgumentException("Invalid key parameter passed to Dstu7624Engine Init"); } - this.forEncryption = forEncryption; + /* Unpack encryption key bytes to words */ + Pack.LE_To_UInt64(keyBytes, 0, workingKey); + + ulong[] tempKeys = new ulong[wordsInBlock]; + + /* KSA in DSTU7624 is strengthened to mitigate known weaknesses in AES KSA (eprint.iacr.org/2012/260.pdf) */ + WorkingKeyExpandKT(workingKey, tempKeys); + WorkingKeyExpandEven(workingKey, tempKeys); + WorkingKeyExpandOdd(); } - private void KeyExpandKT(ulong[] key, ulong[] kt) + private void WorkingKeyExpandKT(ulong[] workingKey, ulong[] tempKeys) { ulong[] k0 = new ulong[wordsInBlock]; ulong[] k1 = new ulong[wordsInBlock]; @@ -156,96 +118,114 @@ namespace Org.BouncyCastle.Crypto.Engines if (wordsInBlock == wordsInKey) { - Array.Copy(key, k0, k0.Length); - Array.Copy(key, k1, k1.Length); + Array.Copy(workingKey, 0, k0, 0, k0.Length); + Array.Copy(workingKey, 0, k1, 0, k1.Length); } else { - Array.Copy(key, 0, k0, 0, wordsInBlock); - Array.Copy(key, wordsInBlock, k1, 0, wordsInBlock); + Array.Copy(workingKey, 0, k0, 0, wordsInBlock); + Array.Copy(workingKey, wordsInBlock, k1, 0, wordsInBlock); } - AddRoundKeyExpand(k0); + + for (int wordIndex = 0; wordIndex < internalState.Length; wordIndex++) + { + internalState[wordIndex] += k0[wordIndex]; + } EncryptionRound(); - XorRoundKeyExpand(k1); + for (int wordIndex = 0; wordIndex < internalState.Length; wordIndex++) + { + internalState[wordIndex] ^= k1[wordIndex]; + } EncryptionRound(); - AddRoundKeyExpand(k0); + for (int wordIndex = 0; wordIndex < internalState.Length; wordIndex++) + { + internalState[wordIndex] += k0[wordIndex]; + } EncryptionRound(); - Array.Copy(internalState, kt, wordsInBlock); + Array.Copy(internalState, 0, tempKeys, 0, wordsInBlock); } - private void KeyExpandEven(ulong[] key, ulong[] kt) + private void WorkingKeyExpandEven(ulong[] workingKey, ulong[] tempKey) { - ulong[] initial_data = new ulong[wordsInKey]; - - ulong[] kt_round = new ulong[wordsInBlock]; - - ulong[] tmv = new ulong[wordsInBlock]; + ulong[] initialData = new ulong[wordsInKey]; + ulong[] tempRoundKey = new ulong[wordsInBlock]; int round = 0; - Array.Copy(key, initial_data, wordsInKey); + Array.Copy(workingKey, 0, initialData, 0, wordsInKey); - for (int i = 0; i < wordsInBlock; i++) - { - tmv[i] = 0x0001000100010001; - } + ulong tmv = 0x0001000100010001UL; while (true) { - Array.Copy(kt, internalState, wordsInBlock); - - AddRoundKeyExpand(tmv); - - Array.Copy(internalState, kt_round, wordsInBlock); - Array.Copy(initial_data, internalState, wordsInBlock); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + tempRoundKey[wordIndex] = tempKey[wordIndex] + tmv; + } - AddRoundKeyExpand(kt_round); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + internalState[wordIndex] = initialData[wordIndex] + tempRoundKey[wordIndex]; + } EncryptionRound(); - XorRoundKeyExpand(kt_round); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + internalState[wordIndex] ^= tempRoundKey[wordIndex]; + } EncryptionRound(); - AddRoundKeyExpand(kt_round); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + internalState[wordIndex] += tempRoundKey[wordIndex]; + } - Array.Copy(internalState, roundKeys[round], wordsInBlock); + Array.Copy(internalState, 0, roundKeys[round], 0, wordsInBlock); if (roundsAmount == round) { break; } + if (wordsInKey != wordsInBlock) { round += 2; + tmv <<= 1; - ShiftLeft(tmv); - - Array.Copy(kt, internalState, wordsInBlock); - - AddRoundKeyExpand(tmv); - - Array.Copy(internalState, kt_round, wordsInBlock); - Array.Copy(initial_data, wordsInBlock, internalState, 0, wordsInBlock); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + tempRoundKey[wordIndex] = tempKey[wordIndex] + tmv; + } - AddRoundKeyExpand(kt_round); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + internalState[wordIndex] = initialData[wordsInBlock + wordIndex] + tempRoundKey[wordIndex]; + } EncryptionRound(); - XorRoundKeyExpand(kt_round); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + internalState[wordIndex] ^= tempRoundKey[wordIndex]; + } EncryptionRound(); - AddRoundKeyExpand(kt_round); + for (int wordIndex = 0; wordIndex < wordsInBlock; wordIndex++) + { + internalState[wordIndex] += tempRoundKey[wordIndex]; + } - Array.Copy(internalState, roundKeys[round], wordsInBlock); + Array.Copy(internalState, 0, roundKeys[round], 0, wordsInBlock); if (roundsAmount == round) { @@ -254,89 +234,99 @@ namespace Org.BouncyCastle.Crypto.Engines } round += 2; - ShiftLeft(tmv); + tmv <<= 1; - //Rotate initial data array on 1 element left - ulong temp = initial_data[0]; - Array.Copy(initial_data, 1, initial_data, 0, initial_data.Length - 1); - initial_data[initial_data.Length - 1] = temp; + ulong temp = initialData[0]; + for (int i = 1; i < initialData.Length; ++i) + { + initialData[i - 1] = initialData[i]; + } + initialData[initialData.Length - 1] = temp; } } - private void KeyExpandOdd() + + private void WorkingKeyExpandOdd() { - for (int i = 1; i < roundsAmount; i += 2) + for (int roundIndex = 1; roundIndex < roundsAmount; roundIndex += 2) { - Array.Copy(roundKeys[i - 1], roundKeys[i], wordsInBlock); - RotateLeft(roundKeys[i]); + RotateLeft(roundKeys[roundIndex - 1], roundKeys[roundIndex]); } } - #endregion + #endregion public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) - throw new InvalidOperationException("Dstu7624 engine not initialised"); + throw new InvalidOperationException("Dstu7624Engine not initialised"); Check.DataLength(input, inOff, GetBlockSize(), "input buffer too short"); Check.OutputLength(output, outOff, GetBlockSize(), "output buffer too short"); if (forEncryption) { - Encrypt(input, inOff, output, outOff); - } - else - { - Decrypt(input, inOff, output, outOff); - } - - return GetBlockSize(); - } - - private void Encrypt(byte[] plain, int inOff, byte[] cipherText, int outOff) - { - Pack.LE_To_UInt64(plain, inOff, internalState); + /* Encrypt */ + switch (wordsInBlock) + { + case 2: + { + EncryptBlock_128(input, inOff, output, outOff); + break; + } + default: + { + Pack.LE_To_UInt64(input, inOff, internalState); + AddRoundKey(0); + for (int round = 0;;) + { + EncryptionRound(); - int round = 0; - AddRoundKey(round); + if (++round == roundsAmount) + { + break; + } - while (++round < roundsAmount) - { - EncryptionRound(); - XorRoundKey(round); + XorRoundKey(round); + } + AddRoundKey(roundsAmount); + Pack.UInt64_To_LE(internalState, output, outOff); + break; + } + } } - - EncryptionRound(); - AddRoundKey(round); - - Pack.UInt64_To_LE(internalState, cipherText, outOff); - } - - private void Decrypt(byte[] cipherText, int inOff, byte[] decryptedText, int outOff) - { - Pack.LE_To_UInt64(cipherText, inOff, internalState); - - int round = roundsAmount; - SubRoundKey(round); - - while (--round > 0) + else { - DecryptionRound(); - XorRoundKey(round); + /* Decrypt */ + switch (wordsInBlock) + { + case 2: + { + DecryptBlock_128(input, inOff, output, outOff); + break; + } + default: + { + Pack.LE_To_UInt64(input, inOff, internalState); + SubRoundKey(roundsAmount); + for (int round = roundsAmount;;) + { + DecryptionRound(); + + if (--round == 0) + { + break; + } + + XorRoundKey(round); + } + SubRoundKey(0); + Pack.UInt64_To_LE(internalState, output, outOff); + break; + } + } } - DecryptionRound(); - SubRoundKey(round); - - Pack.UInt64_To_LE(internalState, decryptedText, outOff); - } - - private void AddRoundKeyExpand(ulong[] value) - { - for (int i = 0; i < wordsInBlock; i++) - { - internalState[i] += value[i]; - } + return GetBlockSize(); } private void EncryptionRound() @@ -348,393 +338,737 @@ namespace Org.BouncyCastle.Crypto.Engines private void DecryptionRound() { - InvMixColumns(); + MixColumnsInv(); InvShiftRows(); InvSubBytes(); } - private void RotateLeft(ulong[] state_value) + private void DecryptBlock_128(byte[] input, int inOff, byte[] output, int outOff) { - int rotateBytesLength = 2 * state_value.Length + 3; - int bytesLength = state_value.Length * (BITS_IN_WORD / BITS_IN_BYTE); + ulong c0 = Pack.LE_To_UInt64(input, inOff); + ulong c1 = Pack.LE_To_UInt64(input, inOff + 8); - byte[] bytes = Pack.UInt64_To_LE(state_value); - byte[] buffer = new byte[rotateBytesLength]; + ulong[] roundKey = roundKeys[roundsAmount]; + c0 -= roundKey[0]; + c1 -= roundKey[1]; + + for (int round = roundsAmount;;) + { + c0 = MixColumnInv(c0); + c1 = MixColumnInv(c1); + + uint lo0 = (uint)c0, hi0 = (uint)(c0 >> 32); + uint lo1 = (uint)c1, hi1 = (uint)(c1 >> 32); + + { + byte t0 = T0[lo0 & 0xFF]; + byte t1 = T1[(lo0 >> 8) & 0xFF]; + byte t2 = T2[(lo0 >> 16) & 0xFF]; + byte t3 = T3[lo0 >> 24]; + lo0 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = T0[hi1 & 0xFF]; + byte t5 = T1[(hi1 >> 8) & 0xFF]; + byte t6 = T2[(hi1 >> 16) & 0xFF]; + byte t7 = T3[hi1 >> 24]; + hi1 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c0 = (ulong)lo0 | ((ulong)hi1 << 32); + } + + { + byte t0 = T0[lo1 & 0xFF]; + byte t1 = T1[(lo1 >> 8) & 0xFF]; + byte t2 = T2[(lo1 >> 16) & 0xFF]; + byte t3 = T3[lo1 >> 24]; + lo1 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = T0[hi0 & 0xFF]; + byte t5 = T1[(hi0 >> 8) & 0xFF]; + byte t6 = T2[(hi0 >> 16) & 0xFF]; + byte t7 = T3[hi0 >> 24]; + hi0 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c1 = (ulong)lo1 | ((ulong)hi0 << 32); + } - Array.Copy(bytes, buffer, rotateBytesLength); + if (--round == 0) + { + break; + } - Buffer.BlockCopy(bytes, rotateBytesLength, bytes, 0, bytesLength - rotateBytesLength); + roundKey = roundKeys[round]; + c0 ^= roundKey[0]; + c1 ^= roundKey[1]; + } - Array.Copy(buffer, 0, bytes, bytesLength - rotateBytesLength, rotateBytesLength); + roundKey = roundKeys[0]; + c0 -= roundKey[0]; + c1 -= roundKey[1]; - Pack.LE_To_UInt64(bytes, 0, state_value); + Pack.UInt64_To_LE(c0, output, outOff); + Pack.UInt64_To_LE(c1, output, outOff + 8); } - private void ShiftLeft(ulong[] state_value) + private void EncryptBlock_128(byte[] input, int inOff, byte[] output, int outOff) { - for (int i = 0; i < state_value.Length; i++) + ulong c0 = Pack.LE_To_UInt64(input, inOff); + ulong c1 = Pack.LE_To_UInt64(input, inOff + 8); + + ulong[] roundKey = roundKeys[0]; + c0 += roundKey[0]; + c1 += roundKey[1]; + + for (int round = 0;;) { - state_value[i] <<= 1; + uint lo0 = (uint)c0, hi0 = (uint)(c0 >> 32); + uint lo1 = (uint)c1, hi1 = (uint)(c1 >> 32); + + { + byte t0 = S0[lo0 & 0xFF]; + byte t1 = S1[(lo0 >> 8) & 0xFF]; + byte t2 = S2[(lo0 >> 16) & 0xFF]; + byte t3 = S3[lo0 >> 24]; + lo0 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = S0[hi1 & 0xFF]; + byte t5 = S1[(hi1 >> 8) & 0xFF]; + byte t6 = S2[(hi1 >> 16) & 0xFF]; + byte t7 = S3[hi1 >> 24]; + hi1 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c0 = (ulong)lo0 | ((ulong)hi1 << 32); + } + + { + byte t0 = S0[lo1 & 0xFF]; + byte t1 = S1[(lo1 >> 8) & 0xFF]; + byte t2 = S2[(lo1 >> 16) & 0xFF]; + byte t3 = S3[lo1 >> 24]; + lo1 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = S0[hi0 & 0xFF]; + byte t5 = S1[(hi0 >> 8) & 0xFF]; + byte t6 = S2[(hi0 >> 16) & 0xFF]; + byte t7 = S3[hi0 >> 24]; + hi0 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + c1 = (ulong)lo1 | ((ulong)hi0 << 32); + } + + c0 = MixColumn(c0); + c1 = MixColumn(c1); + + if (++round == roundsAmount) + { + break; + } + + roundKey = roundKeys[round]; + c0 ^= roundKey[0]; + c1 ^= roundKey[1]; } - Array.Reverse(state_value); + + roundKey = roundKeys[roundsAmount]; + c0 += roundKey[0]; + c1 += roundKey[1]; + + Pack.UInt64_To_LE(c0, output, outOff); + Pack.UInt64_To_LE(c1, output, outOff + 8); } - private void XorRoundKeyExpand(ulong[] value) + private void SubBytes() { for (int i = 0; i < wordsInBlock; i++) { - internalState[i] ^= value[i]; + ulong u = internalState[i]; + uint lo = (uint)u, hi = (uint)(u >> 32); + byte t0 = S0[lo & 0xFF]; + byte t1 = S1[(lo >> 8) & 0xFF]; + byte t2 = S2[(lo >> 16) & 0xFF]; + byte t3 = S3[lo >> 24]; + lo = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = S0[hi & 0xFF]; + byte t5 = S1[(hi >> 8) & 0xFF]; + byte t6 = S2[(hi >> 16) & 0xFF]; + byte t7 = S3[hi >> 24]; + hi = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + internalState[i] = (ulong)lo | ((ulong)hi << 32); } } - private void XorRoundKey(int round) + private void InvSubBytes() { - ulong[] roundKey = roundKeys[round]; for (int i = 0; i < wordsInBlock; i++) { - internalState[i] ^= roundKey[i]; + ulong u = internalState[i]; + uint lo = (uint)u, hi = (uint)(u >> 32); + byte t0 = T0[lo & 0xFF]; + byte t1 = T1[(lo >> 8) & 0xFF]; + byte t2 = T2[(lo >> 16) & 0xFF]; + byte t3 = T3[lo >> 24]; + lo = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24); + byte t4 = T0[hi & 0xFF]; + byte t5 = T1[(hi >> 8) & 0xFF]; + byte t6 = T2[(hi >> 16) & 0xFF]; + byte t7 = T3[hi >> 24]; + hi = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24); + internalState[i] = (ulong)lo | ((ulong)hi << 32); } } private void ShiftRows() { - int row, col; - int shift = -1; + switch (wordsInBlock) + { + case 2: + { + ulong c0 = internalState[0], c1 = internalState[1]; + ulong d; - byte[] stateBytes = Pack.UInt64_To_LE(internalState); - byte[] nstate = new byte[wordsInBlock * 8]; + d = (c0 ^ c1) & 0xFFFFFFFF00000000UL; c0 ^= d; c1 ^= d; - for (row = 0; row < 8; row++) + internalState[0] = c0; + internalState[1] = c1; + break; + } + case 4: { - if (row % (8 / wordsInBlock) == 0) - { - shift += 1; - } + ulong c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3]; + ulong d; - for (col = 0; col < wordsInBlock; col++) - { - nstate[row + ((col + shift) % wordsInBlock) * 8] = stateBytes[row + col * 8]; - } - } + d = (c0 ^ c2) & 0xFFFFFFFF00000000UL; c0 ^= d; c2 ^= d; + d = (c1 ^ c3) & 0x0000FFFFFFFF0000UL; c1 ^= d; c3 ^= d; - Pack.LE_To_UInt64(nstate, 0, internalState); + d = (c0 ^ c1) & 0xFFFF0000FFFF0000UL; c0 ^= d; c1 ^= d; + d = (c2 ^ c3) & 0xFFFF0000FFFF0000UL; c2 ^= d; c3 ^= d; + + internalState[0] = c0; + internalState[1] = c1; + internalState[2] = c2; + internalState[3] = c3; + break; + } + case 8: + { + ulong c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3]; + ulong c4 = internalState[4], c5 = internalState[5], c6 = internalState[6], c7 = internalState[7]; + ulong d; + + d = (c0 ^ c4) & 0xFFFFFFFF00000000UL; c0 ^= d; c4 ^= d; + d = (c1 ^ c5) & 0x00FFFFFFFF000000UL; c1 ^= d; c5 ^= d; + d = (c2 ^ c6) & 0x0000FFFFFFFF0000UL; c2 ^= d; c6 ^= d; + d = (c3 ^ c7) & 0x000000FFFFFFFF00UL; c3 ^= d; c7 ^= d; + + d = (c0 ^ c2) & 0xFFFF0000FFFF0000UL; c0 ^= d; c2 ^= d; + d = (c1 ^ c3) & 0x00FFFF0000FFFF00UL; c1 ^= d; c3 ^= d; + d = (c4 ^ c6) & 0xFFFF0000FFFF0000UL; c4 ^= d; c6 ^= d; + d = (c5 ^ c7) & 0x00FFFF0000FFFF00UL; c5 ^= d; c7 ^= d; + + d = (c0 ^ c1) & 0xFF00FF00FF00FF00UL; c0 ^= d; c1 ^= d; + d = (c2 ^ c3) & 0xFF00FF00FF00FF00UL; c2 ^= d; c3 ^= d; + d = (c4 ^ c5) & 0xFF00FF00FF00FF00UL; c4 ^= d; c5 ^= d; + d = (c6 ^ c7) & 0xFF00FF00FF00FF00UL; c6 ^= d; c7 ^= d; + + internalState[0] = c0; + internalState[1] = c1; + internalState[2] = c2; + internalState[3] = c3; + internalState[4] = c4; + internalState[5] = c5; + internalState[6] = c6; + internalState[7] = c7; + break; + } + default: + { + throw new InvalidOperationException("unsupported block length: only 128/256/512 are allowed"); + } + } } private void InvShiftRows() { - int row, col; - int shift = -1; + switch (wordsInBlock) + { + case 2: + { + ulong c0 = internalState[0], c1 = internalState[1]; + ulong d; - byte[] stateBytes = Pack.UInt64_To_LE(internalState); - byte[] nstate = new byte[wordsInBlock * 8]; + d = (c0 ^ c1) & 0xFFFFFFFF00000000UL; c0 ^= d; c1 ^= d; - for (row = 0; row < 8; row++) + internalState[0] = c0; + internalState[1] = c1; + break; + } + case 4: { - if (row % (8 / wordsInBlock) == 0) - { - shift += 1; - } + ulong c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3]; + ulong d; - for (col = 0; col < wordsInBlock; col++) - { - nstate[row + col * 8] = stateBytes[row + ((col + shift) % wordsInBlock) * 8]; - } - } + d = (c0 ^ c1) & 0xFFFF0000FFFF0000UL; c0 ^= d; c1 ^= d; + d = (c2 ^ c3) & 0xFFFF0000FFFF0000UL; c2 ^= d; c3 ^= d; + + d = (c0 ^ c2) & 0xFFFFFFFF00000000UL; c0 ^= d; c2 ^= d; + d = (c1 ^ c3) & 0x0000FFFFFFFF0000UL; c1 ^= d; c3 ^= d; - Pack.LE_To_UInt64(nstate, 0, internalState); + internalState[0] = c0; + internalState[1] = c1; + internalState[2] = c2; + internalState[3] = c3; + break; + } + case 8: + { + ulong c0 = internalState[0], c1 = internalState[1], c2 = internalState[2], c3 = internalState[3]; + ulong c4 = internalState[4], c5 = internalState[5], c6 = internalState[6], c7 = internalState[7]; + ulong d; + + d = (c0 ^ c1) & 0xFF00FF00FF00FF00UL; c0 ^= d; c1 ^= d; + d = (c2 ^ c3) & 0xFF00FF00FF00FF00UL; c2 ^= d; c3 ^= d; + d = (c4 ^ c5) & 0xFF00FF00FF00FF00UL; c4 ^= d; c5 ^= d; + d = (c6 ^ c7) & 0xFF00FF00FF00FF00UL; c6 ^= d; c7 ^= d; + + d = (c0 ^ c2) & 0xFFFF0000FFFF0000UL; c0 ^= d; c2 ^= d; + d = (c1 ^ c3) & 0x00FFFF0000FFFF00UL; c1 ^= d; c3 ^= d; + d = (c4 ^ c6) & 0xFFFF0000FFFF0000UL; c4 ^= d; c6 ^= d; + d = (c5 ^ c7) & 0x00FFFF0000FFFF00UL; c5 ^= d; c7 ^= d; + + d = (c0 ^ c4) & 0xFFFFFFFF00000000UL; c0 ^= d; c4 ^= d; + d = (c1 ^ c5) & 0x00FFFFFFFF000000UL; c1 ^= d; c5 ^= d; + d = (c2 ^ c6) & 0x0000FFFFFFFF0000UL; c2 ^= d; c6 ^= d; + d = (c3 ^ c7) & 0x000000FFFFFFFF00UL; c3 ^= d; c7 ^= d; + + internalState[0] = c0; + internalState[1] = c1; + internalState[2] = c2; + internalState[3] = c3; + internalState[4] = c4; + internalState[5] = c5; + internalState[6] = c6; + internalState[7] = c7; + break; + } + default: + { + throw new InvalidOperationException("unsupported block length: only 128/256/512 are allowed"); + } + } } private void AddRoundKey(int round) { + ulong[] roundKey = roundKeys[round]; for (int i = 0; i < wordsInBlock; ++i) { - internalState[i] += roundKeys[round][i]; + internalState[i] += roundKey[i]; } } private void SubRoundKey(int round) { + ulong[] roundKey = roundKeys[round]; for (int i = 0; i < wordsInBlock; ++i) { - internalState[i] -= roundKeys[round][i]; + internalState[i] -= roundKey[i]; } } - private void MixColumns() + private void XorRoundKey(int round) { - MatrixMultiply(mdsMatrix); + ulong[] roundKey = roundKeys[round]; + for (int i = 0; i < wordsInBlock; i++) + { + internalState[i] ^= roundKey[i]; + } } - private void InvMixColumns() + private static ulong MixColumn(ulong c) { - MatrixMultiply(mdsInvMatrix); + //// Calculate column multiplied by powers of 'x' + //ulong x0 = c; + //ulong x1 = MulX(x0); + //ulong x2 = MulX(x1); + //ulong x3 = MulX(x2); + + //// Calculate products with circulant matrix from (0x01, 0x01, 0x05, 0x01, 0x08, 0x06, 0x07, 0x04) + //ulong m0 = x0; + //ulong m1 = x0; + //ulong m2 = x0 ^ x2; + //ulong m3 = x0; + //ulong m4 = x3; + //ulong m5 = x1 ^ x2; + //ulong m6 = x0 ^ x1 ^ x2; + //ulong m7 = x2; + + //// Assemble the rotated products + //return m0 + // ^ Rotate(8, m1) + // ^ Rotate(16, m2) + // ^ Rotate(24, m3) + // ^ Rotate(32, m4) + // ^ Rotate(40, m5) + // ^ Rotate(48, m6) + // ^ Rotate(56, m7); + + ulong x1 = MulX(c); + ulong u, v; + + u = Rotate(8, c) ^ c; + u ^= Rotate(16, u); + u ^= Rotate(48, c); + + v = MulX2(u ^ c ^ x1); + + return u ^ Rotate(32, v) ^ Rotate(40, x1) ^ Rotate(48, x1); } - private void MatrixMultiply(ulong matrix) + private void MixColumns() { for (int col = 0; col < wordsInBlock; ++col) { - ulong colVal = internalState[col]; - ulong colEven = colVal & 0x00FF00FF00FF00FFUL; - ulong colOdd = (colVal >> 8) & 0x00FF00FF00FF00FFUL; - - //ulong rowMatrix = (matrix >> 8) | (matrix << 56); - ulong rowMatrix = matrix; - - ulong result = 0; - for (int row = 7; row >= 0; --row) - { - ulong product = MultiplyGFx4(colEven, rowMatrix & 0x00FF00FF00FF00FFUL); - - rowMatrix = (rowMatrix >> 8) | (rowMatrix << 56); - - product ^= MultiplyGFx4(colOdd, rowMatrix & 0x00FF00FF00FF00FFUL); - - product ^= (product >> 32); - product ^= (product >> 16); - - result <<= 8; - result |= (product & 0xFFUL); - } - - internalState[col] = result; + internalState[col] = MixColumn(internalState[col]); } } - /* Pair-wise GF multiplication of 4 byte-pairs (at bits 0, 16, 32, 48 within x, y) */ - private static ulong MultiplyGFx4(ulong u, ulong v) + private static ulong MixColumnInv(ulong c) { - ulong r = u & ((v & 0x0001000100010001UL) * 0xFFFFUL); +/* + // Calculate column multiplied by powers of 'x' + ulong x0 = c; + ulong x1 = MulX(x0); + ulong x2 = MulX(x1); + ulong x3 = MulX(x2); + ulong x4 = MulX(x3); + ulong x5 = MulX(x4); + ulong x6 = MulX(x5); + ulong x7 = MulX(x6); + + // Calculate products with circulant matrix from (0xAD,0x95,0x76,0xA8,0x2F,0x49,0xD7,0xCA) + //long m0 = x0 ^ x2 ^ x3 ^ x5 ^ x7; + //long m1 = x0 ^ x2 ^ x4 ^ x7; + //long m2 = x1 ^ x2 ^ x4 ^ x5 ^ x6; + //long m3 = x3 ^ x5 ^ x7; + //long m4 = x0 ^ x1 ^ x2 ^ x3 ^ x5; + //long m5 = x0 ^ x3 ^ x6; + //long m6 = x0 ^ x1 ^ x2 ^ x4 ^ x6 ^ x7; + //long m7 = x1 ^ x3 ^ x6 ^ x7; + + ulong m5 = x0 ^ x3 ^ x6; + x0 ^= x2; + ulong m3 = x3 ^ x5 ^ x7; + ulong m0 = m3 ^ x0; + ulong m6 = x0 ^ x4; + ulong m1 = m6 ^ x7; + x5 ^= x1; + x7 ^= x1 ^ x6; + ulong m2 = x2 ^ x4 ^ x5 ^ x6; + ulong m4 = x0 ^ x3 ^ x5; + m6 ^= x7; + ulong m7 = x3 ^ x7; + + // Assemble the rotated products + return m0 + ^ Rotate(8, m1) + ^ Rotate(16, m2) + ^ Rotate(24, m3) + ^ Rotate(32, m4) + ^ Rotate(40, m5) + ^ Rotate(48, m6) + ^ Rotate(56, m7); +*/ + + ulong u0 = c; + u0 ^= Rotate( 8, u0); + u0 ^= Rotate(32, u0); + u0 ^= Rotate(48, c); + + ulong t = u0 ^ c; + + ulong c48 = Rotate(48, c); + ulong c56 = Rotate(56, c); + + ulong u7 = t ^ c56; + ulong u6 = Rotate(56, t); + u6 ^= MulX(u7); + ulong u5 = Rotate(16, t) ^ c; + u5 ^= Rotate(40, MulX(u6) ^ c); + ulong u4 = t ^ c48; + u4 ^= MulX(u5); + ulong u3 = Rotate(16, u0); + u3 ^= MulX(u4); + ulong u2 = t ^ Rotate(24, c) ^ c48 ^ c56; + u2 ^= MulX(u3); + ulong u1 = Rotate(32, t) ^ c ^ c56; + u1 ^= MulX(u2); + u0 ^= MulX(Rotate(40, u1)); + + return u0; + } - for (int i = 1; i < 8; ++i) + private void MixColumnsInv() + { + for (int col = 0; col < wordsInBlock; ++col) { - u <<= 1; - v >>= 1; - r ^= u & ((v & 0x0001000100010001L) * 0xFFFFL); + internalState[col] = MixColumnInv(internalState[col]); } + } - // REDUCTION_POLYNOMIAL = 0x011d; /* x^8 + x^4 + x^3 + x^2 + 1 */ + private static ulong MulX(ulong n) + { + return ((n & 0x7F7F7F7F7F7F7F7FUL) << 1) ^ (((n & 0x8080808080808080UL) >> 7) * 0x1DUL); + } - ulong hi = r & 0xFF00FF00FF00FF00UL; - r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8); - hi = r & 0x0F000F000F000F00UL; - r ^= hi ^ (hi >> 4) ^ (hi >> 5) ^ (hi >> 6) ^ (hi >> 8); - return r; + private static ulong MulX2(ulong n) + { + return ((n & 0x3F3F3F3F3F3F3F3FUL) << 2) ^ (((n & 0x8080808080808080UL) >> 6) * 0x1DUL) ^ (((n & 0x4040404040404040UL) >> 6) * 0x1DUL); } - private void SubBytes() + //private static ulong MulX4(ulong n) + //{ + // ulong u = n & 0xF0F0F0F0F0F0F0F0UL; + // return ((n & 0x0F0F0F0F0F0F0F0FUL) << 4) ^ u ^ (u >> 1) ^ (u >> 2) ^ (u >> 4); + //} + + /* + * Pair-wise modular multiplication of 8 byte-pairs. + * + * REDUCTION_POLYNOMIAL is x^8 + x^4 + x^3 + x^2 + 1 + */ + //private static ulong MultiplyGFx8(ulong u, ulong v, int vMaxDegree) + //{ + // ulong r = u & ((v & 0x0101010101010101UL) * 0xFFUL); + // for (int i = 1; i <= vMaxDegree; ++i) + // { + // u = ((u & 0x7F7F7F7F7F7F7F7FUL) << 1) ^ (((u >> 7) & 0x0101010101010101UL) * 0x1DUL); + // v >>= 1; + + // r ^= u & ((v & 0x0101010101010101UL) * 0xFFUL); + // } + + // return r; + //} + + //private static ulong MultiplyMds(ulong u) + //{ + // ulong r = 0, s = 0, t = (u >> 8); + // r ^= u & 0x0000001F00000000UL; r <<= 1; + // s ^= t & 0x00000000E0000000UL; s <<= 1; + // r ^= u & 0x3F3F3F00003F0000UL; r <<= 1; + // s ^= t & 0x00C0C0C00000C000UL; s <<= 1; + // r ^= u & 0x007F7F0000000000UL; r <<= 1; + // s ^= t & 0x0000808000000000UL; s <<= 1; + // r ^= u & 0x00FF0000FFFFFFFFUL; + // r ^= s ^ (s << 2) ^ (s << 3) ^ (s << 4); + // return r; + //} + + private static ulong Rotate(int n, ulong x) { - for (int i = 0; i < wordsInBlock; i++) - { - internalState[i] = sboxesForEncryption[0][internalState[i] & 0x00000000000000FF] | - ((ulong)sboxesForEncryption[1][(internalState[i] & 0x000000000000FF00) >> 8] << 8) | - ((ulong)sboxesForEncryption[2][(internalState[i] & 0x0000000000FF0000) >> 16] << 16) | - ((ulong)sboxesForEncryption[3][(internalState[i] & 0x00000000FF000000) >> 24] << 24) | - ((ulong)sboxesForEncryption[0][(internalState[i] & 0x000000FF00000000) >> 32] << 32) | - ((ulong)sboxesForEncryption[1][(internalState[i] & 0x0000FF0000000000) >> 40] << 40) | - ((ulong)sboxesForEncryption[2][(internalState[i] & 0x00FF000000000000) >> 48] << 48) | - ((ulong)sboxesForEncryption[3][(internalState[i] & 0xFF00000000000000) >> 56] << 56); - } + return (x >> n) | (x << -n); } - private void InvSubBytes() + private void RotateLeft(ulong[] x, ulong[] z) { - for (int i = 0; i < wordsInBlock; i++) + switch (wordsInBlock) { - internalState[i] = sboxesForDecryption[0][internalState[i] & 0x00000000000000FF] | - ((ulong)sboxesForDecryption[1][(internalState[i] & 0x000000000000FF00) >> 8] << 8) | - ((ulong)sboxesForDecryption[2][(internalState[i] & 0x0000000000FF0000) >> 16] << 16) | - ((ulong)sboxesForDecryption[3][(internalState[i] & 0x00000000FF000000) >> 24] << 24) | - ((ulong)sboxesForDecryption[0][(internalState[i] & 0x000000FF00000000) >> 32] << 32) | - ((ulong)sboxesForDecryption[1][(internalState[i] & 0x0000FF0000000000) >> 40] << 40) | - ((ulong)sboxesForDecryption[2][(internalState[i] & 0x00FF000000000000) >> 48] << 48) | - ((ulong)sboxesForDecryption[3][(internalState[i] & 0xFF00000000000000) >> 56] << 56); + case 2: + { + ulong x0 = x[0], x1 = x[1]; + z[0] = (x0 >> 56) | (x1 << 8); + z[1] = (x1 >> 56) | (x0 << 8); + break; + } + case 4: + { + ulong x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + z[0] = (x1 >> 24) | (x2 << 40); + z[1] = (x2 >> 24) | (x3 << 40); + z[2] = (x3 >> 24) | (x0 << 40); + z[3] = (x0 >> 24) | (x1 << 40); + break; + } + case 8: + { + ulong x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + ulong x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7]; + z[0] = (x2 >> 24) | (x3 << 40); + z[1] = (x3 >> 24) | (x4 << 40); + z[2] = (x4 >> 24) | (x5 << 40); + z[3] = (x5 >> 24) | (x6 << 40); + z[4] = (x6 >> 24) | (x7 << 40); + z[5] = (x7 >> 24) | (x0 << 40); + z[6] = (x0 >> 24) | (x1 << 40); + z[7] = (x1 >> 24) | (x2 << 40); + break; + } + default: + { + throw new InvalidOperationException("unsupported block length: only 128/256/512 are allowed"); + } } } #region TABLES AND S-BOXES - //private const ulong mdsMatrix = 0x0407060801050101UL; - //private const ulong mdsInvMatrix = 0xCAD7492FA87695ADUL; - private const ulong mdsMatrix = 0x0104070608010501UL; - private const ulong mdsInvMatrix = 0xADCAD7492FA87695UL; + private const ulong mdsMatrix = 0x0407060801050101UL; + private const ulong mdsInvMatrix = 0xCAD7492FA87695ADUL; + + private static readonly byte[] S0 = new byte[]{ + 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09, + 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39, + 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6, + 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1, + 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27, + 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41, + 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e, + 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55, + 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff, + 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1, + 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76, + 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26, + 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82, + 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8, + 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d, + 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80 + }; - private byte[][] sboxesForEncryption = - { - new byte[] - { - 0xa8, 0x43, 0x5f, 0x06, 0x6b, 0x75, 0x6c, 0x59, 0x71, 0xdf, 0x87, 0x95, 0x17, 0xf0, 0xd8, 0x09, - 0x6d, 0xf3, 0x1d, 0xcb, 0xc9, 0x4d, 0x2c, 0xaf, 0x79, 0xe0, 0x97, 0xfd, 0x6f, 0x4b, 0x45, 0x39, - 0x3e, 0xdd, 0xa3, 0x4f, 0xb4, 0xb6, 0x9a, 0x0e, 0x1f, 0xbf, 0x15, 0xe1, 0x49, 0xd2, 0x93, 0xc6, - 0x92, 0x72, 0x9e, 0x61, 0xd1, 0x63, 0xfa, 0xee, 0xf4, 0x19, 0xd5, 0xad, 0x58, 0xa4, 0xbb, 0xa1, - 0xdc, 0xf2, 0x83, 0x37, 0x42, 0xe4, 0x7a, 0x32, 0x9c, 0xcc, 0xab, 0x4a, 0x8f, 0x6e, 0x04, 0x27, - 0x2e, 0xe7, 0xe2, 0x5a, 0x96, 0x16, 0x23, 0x2b, 0xc2, 0x65, 0x66, 0x0f, 0xbc, 0xa9, 0x47, 0x41, - 0x34, 0x48, 0xfc, 0xb7, 0x6a, 0x88, 0xa5, 0x53, 0x86, 0xf9, 0x5b, 0xdb, 0x38, 0x7b, 0xc3, 0x1e, - 0x22, 0x33, 0x24, 0x28, 0x36, 0xc7, 0xb2, 0x3b, 0x8e, 0x77, 0xba, 0xf5, 0x14, 0x9f, 0x08, 0x55, - 0x9b, 0x4c, 0xfe, 0x60, 0x5c, 0xda, 0x18, 0x46, 0xcd, 0x7d, 0x21, 0xb0, 0x3f, 0x1b, 0x89, 0xff, - 0xeb, 0x84, 0x69, 0x3a, 0x9d, 0xd7, 0xd3, 0x70, 0x67, 0x40, 0xb5, 0xde, 0x5d, 0x30, 0x91, 0xb1, - 0x78, 0x11, 0x01, 0xe5, 0x00, 0x68, 0x98, 0xa0, 0xc5, 0x02, 0xa6, 0x74, 0x2d, 0x0b, 0xa2, 0x76, - 0xb3, 0xbe, 0xce, 0xbd, 0xae, 0xe9, 0x8a, 0x31, 0x1c, 0xec, 0xf1, 0x99, 0x94, 0xaa, 0xf6, 0x26, - 0x2f, 0xef, 0xe8, 0x8c, 0x35, 0x03, 0xd4, 0x7f, 0xfb, 0x05, 0xc1, 0x5e, 0x90, 0x20, 0x3d, 0x82, - 0xf7, 0xea, 0x0a, 0x0d, 0x7e, 0xf8, 0x50, 0x1a, 0xc4, 0x07, 0x57, 0xb8, 0x3c, 0x62, 0xe3, 0xc8, - 0xac, 0x52, 0x64, 0x10, 0xd0, 0xd9, 0x13, 0x0c, 0x12, 0x29, 0x51, 0xb9, 0xcf, 0xd6, 0x73, 0x8d, - 0x81, 0x54, 0xc0, 0xed, 0x4e, 0x44, 0xa7, 0x2a, 0x85, 0x25, 0xe6, 0xca, 0x7c, 0x8b, 0x56, 0x80 - }, - - new byte[] - { - 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8, - 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d, - 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d, - 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc, - 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee, - 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca, - 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20, - 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f, - 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51, - 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98, - 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9, - 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05, - 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82, - 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad, - 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59, - 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7 - }, - - new byte[] - { - 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59, - 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1, - 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72, - 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90, - 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35, - 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48, - 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38, - 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33, - 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29, - 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83, - 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2, - 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43, - 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82, - 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91, - 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44, - 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67 - }, - - new byte[] - { - 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f, - 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd, - 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66, - 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf, - 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99, - 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71, - 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60, - 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b, - 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09, - 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2, - 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7, - 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a, - 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39, - 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef, - 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36, - 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61 - } + private static readonly byte[] S1 = new byte[]{ + 0xce, 0xbb, 0xeb, 0x92, 0xea, 0xcb, 0x13, 0xc1, 0xe9, 0x3a, 0xd6, 0xb2, 0xd2, 0x90, 0x17, 0xf8, + 0x42, 0x15, 0x56, 0xb4, 0x65, 0x1c, 0x88, 0x43, 0xc5, 0x5c, 0x36, 0xba, 0xf5, 0x57, 0x67, 0x8d, + 0x31, 0xf6, 0x64, 0x58, 0x9e, 0xf4, 0x22, 0xaa, 0x75, 0x0f, 0x02, 0xb1, 0xdf, 0x6d, 0x73, 0x4d, + 0x7c, 0x26, 0x2e, 0xf7, 0x08, 0x5d, 0x44, 0x3e, 0x9f, 0x14, 0xc8, 0xae, 0x54, 0x10, 0xd8, 0xbc, + 0x1a, 0x6b, 0x69, 0xf3, 0xbd, 0x33, 0xab, 0xfa, 0xd1, 0x9b, 0x68, 0x4e, 0x16, 0x95, 0x91, 0xee, + 0x4c, 0x63, 0x8e, 0x5b, 0xcc, 0x3c, 0x19, 0xa1, 0x81, 0x49, 0x7b, 0xd9, 0x6f, 0x37, 0x60, 0xca, + 0xe7, 0x2b, 0x48, 0xfd, 0x96, 0x45, 0xfc, 0x41, 0x12, 0x0d, 0x79, 0xe5, 0x89, 0x8c, 0xe3, 0x20, + 0x30, 0xdc, 0xb7, 0x6c, 0x4a, 0xb5, 0x3f, 0x97, 0xd4, 0x62, 0x2d, 0x06, 0xa4, 0xa5, 0x83, 0x5f, + 0x2a, 0xda, 0xc9, 0x00, 0x7e, 0xa2, 0x55, 0xbf, 0x11, 0xd5, 0x9c, 0xcf, 0x0e, 0x0a, 0x3d, 0x51, + 0x7d, 0x93, 0x1b, 0xfe, 0xc4, 0x47, 0x09, 0x86, 0x0b, 0x8f, 0x9d, 0x6a, 0x07, 0xb9, 0xb0, 0x98, + 0x18, 0x32, 0x71, 0x4b, 0xef, 0x3b, 0x70, 0xa0, 0xe4, 0x40, 0xff, 0xc3, 0xa9, 0xe6, 0x78, 0xf9, + 0x8b, 0x46, 0x80, 0x1e, 0x38, 0xe1, 0xb8, 0xa8, 0xe0, 0x0c, 0x23, 0x76, 0x1d, 0x25, 0x24, 0x05, + 0xf1, 0x6e, 0x94, 0x28, 0x9a, 0x84, 0xe8, 0xa3, 0x4f, 0x77, 0xd3, 0x85, 0xe2, 0x52, 0xf2, 0x82, + 0x50, 0x7a, 0x2f, 0x74, 0x53, 0xb3, 0x61, 0xaf, 0x39, 0x35, 0xde, 0xcd, 0x1f, 0x99, 0xac, 0xad, + 0x72, 0x2c, 0xdd, 0xd0, 0x87, 0xbe, 0x5e, 0xa6, 0xec, 0x04, 0xc6, 0x03, 0x34, 0xfb, 0xdb, 0x59, + 0xb6, 0xc2, 0x01, 0xf0, 0x5a, 0xed, 0xa7, 0x66, 0x21, 0x7f, 0x8a, 0x27, 0xc7, 0xc0, 0x29, 0xd7 }; - private byte[][] sboxesForDecryption = - { - new byte[] - { - 0xa4, 0xa2, 0xa9, 0xc5, 0x4e, 0xc9, 0x03, 0xd9, 0x7e, 0x0f, 0xd2, 0xad, 0xe7, 0xd3, 0x27, 0x5b, - 0xe3, 0xa1, 0xe8, 0xe6, 0x7c, 0x2a, 0x55, 0x0c, 0x86, 0x39, 0xd7, 0x8d, 0xb8, 0x12, 0x6f, 0x28, - 0xcd, 0x8a, 0x70, 0x56, 0x72, 0xf9, 0xbf, 0x4f, 0x73, 0xe9, 0xf7, 0x57, 0x16, 0xac, 0x50, 0xc0, - 0x9d, 0xb7, 0x47, 0x71, 0x60, 0xc4, 0x74, 0x43, 0x6c, 0x1f, 0x93, 0x77, 0xdc, 0xce, 0x20, 0x8c, - 0x99, 0x5f, 0x44, 0x01, 0xf5, 0x1e, 0x87, 0x5e, 0x61, 0x2c, 0x4b, 0x1d, 0x81, 0x15, 0xf4, 0x23, - 0xd6, 0xea, 0xe1, 0x67, 0xf1, 0x7f, 0xfe, 0xda, 0x3c, 0x07, 0x53, 0x6a, 0x84, 0x9c, 0xcb, 0x02, - 0x83, 0x33, 0xdd, 0x35, 0xe2, 0x59, 0x5a, 0x98, 0xa5, 0x92, 0x64, 0x04, 0x06, 0x10, 0x4d, 0x1c, - 0x97, 0x08, 0x31, 0xee, 0xab, 0x05, 0xaf, 0x79, 0xa0, 0x18, 0x46, 0x6d, 0xfc, 0x89, 0xd4, 0xc7, - 0xff, 0xf0, 0xcf, 0x42, 0x91, 0xf8, 0x68, 0x0a, 0x65, 0x8e, 0xb6, 0xfd, 0xc3, 0xef, 0x78, 0x4c, - 0xcc, 0x9e, 0x30, 0x2e, 0xbc, 0x0b, 0x54, 0x1a, 0xa6, 0xbb, 0x26, 0x80, 0x48, 0x94, 0x32, 0x7d, - 0xa7, 0x3f, 0xae, 0x22, 0x3d, 0x66, 0xaa, 0xf6, 0x00, 0x5d, 0xbd, 0x4a, 0xe0, 0x3b, 0xb4, 0x17, - 0x8b, 0x9f, 0x76, 0xb0, 0x24, 0x9a, 0x25, 0x63, 0xdb, 0xeb, 0x7a, 0x3e, 0x5c, 0xb3, 0xb1, 0x29, - 0xf2, 0xca, 0x58, 0x6e, 0xd8, 0xa8, 0x2f, 0x75, 0xdf, 0x14, 0xfb, 0x13, 0x49, 0x88, 0xb2, 0xec, - 0xe4, 0x34, 0x2d, 0x96, 0xc6, 0x3a, 0xed, 0x95, 0x0e, 0xe5, 0x85, 0x6b, 0x40, 0x21, 0x9b, 0x09, - 0x19, 0x2b, 0x52, 0xde, 0x45, 0xa3, 0xfa, 0x51, 0xc2, 0xb5, 0xd1, 0x90, 0xb9, 0xf3, 0x37, 0xc1, - 0x0d, 0xba, 0x41, 0x11, 0x38, 0x7b, 0xbe, 0xd0, 0xd5, 0x69, 0x36, 0xc8, 0x62, 0x1b, 0x82, 0x8f - }, - - new byte[] - { - 0x83, 0xf2, 0x2a, 0xeb, 0xe9, 0xbf, 0x7b, 0x9c, 0x34, 0x96, 0x8d, 0x98, 0xb9, 0x69, 0x8c, 0x29, - 0x3d, 0x88, 0x68, 0x06, 0x39, 0x11, 0x4c, 0x0e, 0xa0, 0x56, 0x40, 0x92, 0x15, 0xbc, 0xb3, 0xdc, - 0x6f, 0xf8, 0x26, 0xba, 0xbe, 0xbd, 0x31, 0xfb, 0xc3, 0xfe, 0x80, 0x61, 0xe1, 0x7a, 0x32, 0xd2, - 0x70, 0x20, 0xa1, 0x45, 0xec, 0xd9, 0x1a, 0x5d, 0xb4, 0xd8, 0x09, 0xa5, 0x55, 0x8e, 0x37, 0x76, - 0xa9, 0x67, 0x10, 0x17, 0x36, 0x65, 0xb1, 0x95, 0x62, 0x59, 0x74, 0xa3, 0x50, 0x2f, 0x4b, 0xc8, - 0xd0, 0x8f, 0xcd, 0xd4, 0x3c, 0x86, 0x12, 0x1d, 0x23, 0xef, 0xf4, 0x53, 0x19, 0x35, 0xe6, 0x7f, - 0x5e, 0xd6, 0x79, 0x51, 0x22, 0x14, 0xf7, 0x1e, 0x4a, 0x42, 0x9b, 0x41, 0x73, 0x2d, 0xc1, 0x5c, - 0xa6, 0xa2, 0xe0, 0x2e, 0xd3, 0x28, 0xbb, 0xc9, 0xae, 0x6a, 0xd1, 0x5a, 0x30, 0x90, 0x84, 0xf9, - 0xb2, 0x58, 0xcf, 0x7e, 0xc5, 0xcb, 0x97, 0xe4, 0x16, 0x6c, 0xfa, 0xb0, 0x6d, 0x1f, 0x52, 0x99, - 0x0d, 0x4e, 0x03, 0x91, 0xc2, 0x4d, 0x64, 0x77, 0x9f, 0xdd, 0xc4, 0x49, 0x8a, 0x9a, 0x24, 0x38, - 0xa7, 0x57, 0x85, 0xc7, 0x7c, 0x7d, 0xe7, 0xf6, 0xb7, 0xac, 0x27, 0x46, 0xde, 0xdf, 0x3b, 0xd7, - 0x9e, 0x2b, 0x0b, 0xd5, 0x13, 0x75, 0xf0, 0x72, 0xb6, 0x9d, 0x1b, 0x01, 0x3f, 0x44, 0xe5, 0x87, - 0xfd, 0x07, 0xf1, 0xab, 0x94, 0x18, 0xea, 0xfc, 0x3a, 0x82, 0x5f, 0x05, 0x54, 0xdb, 0x00, 0x8b, - 0xe3, 0x48, 0x0c, 0xca, 0x78, 0x89, 0x0a, 0xff, 0x3e, 0x5b, 0x81, 0xee, 0x71, 0xe2, 0xda, 0x2c, - 0xb8, 0xb5, 0xcc, 0x6e, 0xa8, 0x6b, 0xad, 0x60, 0xc6, 0x08, 0x04, 0x02, 0xe8, 0xf5, 0x4f, 0xa4, - 0xf3, 0xc0, 0xce, 0x43, 0x25, 0x1c, 0x21, 0x33, 0x0f, 0xaf, 0x47, 0xed, 0x66, 0x63, 0x93, 0xaa - }, - - new byte[] - { - 0x45, 0xd4, 0x0b, 0x43, 0xf1, 0x72, 0xed, 0xa4, 0xc2, 0x38, 0xe6, 0x71, 0xfd, 0xb6, 0x3a, 0x95, - 0x50, 0x44, 0x4b, 0xe2, 0x74, 0x6b, 0x1e, 0x11, 0x5a, 0xc6, 0xb4, 0xd8, 0xa5, 0x8a, 0x70, 0xa3, - 0xa8, 0xfa, 0x05, 0xd9, 0x97, 0x40, 0xc9, 0x90, 0x98, 0x8f, 0xdc, 0x12, 0x31, 0x2c, 0x47, 0x6a, - 0x99, 0xae, 0xc8, 0x7f, 0xf9, 0x4f, 0x5d, 0x96, 0x6f, 0xf4, 0xb3, 0x39, 0x21, 0xda, 0x9c, 0x85, - 0x9e, 0x3b, 0xf0, 0xbf, 0xef, 0x06, 0xee, 0xe5, 0x5f, 0x20, 0x10, 0xcc, 0x3c, 0x54, 0x4a, 0x52, - 0x94, 0x0e, 0xc0, 0x28, 0xf6, 0x56, 0x60, 0xa2, 0xe3, 0x0f, 0xec, 0x9d, 0x24, 0x83, 0x7e, 0xd5, - 0x7c, 0xeb, 0x18, 0xd7, 0xcd, 0xdd, 0x78, 0xff, 0xdb, 0xa1, 0x09, 0xd0, 0x76, 0x84, 0x75, 0xbb, - 0x1d, 0x1a, 0x2f, 0xb0, 0xfe, 0xd6, 0x34, 0x63, 0x35, 0xd2, 0x2a, 0x59, 0x6d, 0x4d, 0x77, 0xe7, - 0x8e, 0x61, 0xcf, 0x9f, 0xce, 0x27, 0xf5, 0x80, 0x86, 0xc7, 0xa6, 0xfb, 0xf8, 0x87, 0xab, 0x62, - 0x3f, 0xdf, 0x48, 0x00, 0x14, 0x9a, 0xbd, 0x5b, 0x04, 0x92, 0x02, 0x25, 0x65, 0x4c, 0x53, 0x0c, - 0xf2, 0x29, 0xaf, 0x17, 0x6c, 0x41, 0x30, 0xe9, 0x93, 0x55, 0xf7, 0xac, 0x68, 0x26, 0xc4, 0x7d, - 0xca, 0x7a, 0x3e, 0xa0, 0x37, 0x03, 0xc1, 0x36, 0x69, 0x66, 0x08, 0x16, 0xa7, 0xbc, 0xc5, 0xd3, - 0x22, 0xb7, 0x13, 0x46, 0x32, 0xe8, 0x57, 0x88, 0x2b, 0x81, 0xb2, 0x4e, 0x64, 0x1c, 0xaa, 0x91, - 0x58, 0x2e, 0x9b, 0x5c, 0x1b, 0x51, 0x73, 0x42, 0x23, 0x01, 0x6e, 0xf3, 0x0d, 0xbe, 0x3d, 0x0a, - 0x2d, 0x1f, 0x67, 0x33, 0x19, 0x7b, 0x5e, 0xea, 0xde, 0x8b, 0xcb, 0xa9, 0x8c, 0x8d, 0xad, 0x49, - 0x82, 0xe4, 0xba, 0xc3, 0x15, 0xd1, 0xe0, 0x89, 0xfc, 0xb1, 0xb9, 0xb5, 0x07, 0x79, 0xb8, 0xe1 - }, - - new byte[] - { - 0xb2, 0xb6, 0x23, 0x11, 0xa7, 0x88, 0xc5, 0xa6, 0x39, 0x8f, 0xc4, 0xe8, 0x73, 0x22, 0x43, 0xc3, - 0x82, 0x27, 0xcd, 0x18, 0x51, 0x62, 0x2d, 0xf7, 0x5c, 0x0e, 0x3b, 0xfd, 0xca, 0x9b, 0x0d, 0x0f, - 0x79, 0x8c, 0x10, 0x4c, 0x74, 0x1c, 0x0a, 0x8e, 0x7c, 0x94, 0x07, 0xc7, 0x5e, 0x14, 0xa1, 0x21, - 0x57, 0x50, 0x4e, 0xa9, 0x80, 0xd9, 0xef, 0x64, 0x41, 0xcf, 0x3c, 0xee, 0x2e, 0x13, 0x29, 0xba, - 0x34, 0x5a, 0xae, 0x8a, 0x61, 0x33, 0x12, 0xb9, 0x55, 0xa8, 0x15, 0x05, 0xf6, 0x03, 0x06, 0x49, - 0xb5, 0x25, 0x09, 0x16, 0x0c, 0x2a, 0x38, 0xfc, 0x20, 0xf4, 0xe5, 0x7f, 0xd7, 0x31, 0x2b, 0x66, - 0x6f, 0xff, 0x72, 0x86, 0xf0, 0xa3, 0x2f, 0x78, 0x00, 0xbc, 0xcc, 0xe2, 0xb0, 0xf1, 0x42, 0xb4, - 0x30, 0x5f, 0x60, 0x04, 0xec, 0xa5, 0xe3, 0x8b, 0xe7, 0x1d, 0xbf, 0x84, 0x7b, 0xe6, 0x81, 0xf8, - 0xde, 0xd8, 0xd2, 0x17, 0xce, 0x4b, 0x47, 0xd6, 0x69, 0x6c, 0x19, 0x99, 0x9a, 0x01, 0xb3, 0x85, - 0xb1, 0xf9, 0x59, 0xc2, 0x37, 0xe9, 0xc8, 0xa0, 0xed, 0x4f, 0x89, 0x68, 0x6d, 0xd5, 0x26, 0x91, - 0x87, 0x58, 0xbd, 0xc9, 0x98, 0xdc, 0x75, 0xc0, 0x76, 0xf5, 0x67, 0x6b, 0x7e, 0xeb, 0x52, 0xcb, - 0xd1, 0x5b, 0x9f, 0x0b, 0xdb, 0x40, 0x92, 0x1a, 0xfa, 0xac, 0xe4, 0xe1, 0x71, 0x1f, 0x65, 0x8d, - 0x97, 0x9e, 0x95, 0x90, 0x5d, 0xb7, 0xc1, 0xaf, 0x54, 0xfb, 0x02, 0xe0, 0x35, 0xbb, 0x3a, 0x4d, - 0xad, 0x2c, 0x3d, 0x56, 0x08, 0x1b, 0x4a, 0x93, 0x6a, 0xab, 0xb8, 0x7a, 0xf2, 0x7d, 0xda, 0x3f, - 0xfe, 0x3e, 0xbe, 0xea, 0xaa, 0x44, 0xc6, 0xd0, 0x36, 0x48, 0x70, 0x96, 0x77, 0x24, 0x53, 0xdf, - 0xf3, 0x83, 0x28, 0x32, 0x45, 0x1e, 0xa4, 0xd3, 0xa2, 0x46, 0x6e, 0x9c, 0xdd, 0x63, 0xd4, 0x9d - } + private static readonly byte[] S2 = new byte[]{ + 0x93, 0xd9, 0x9a, 0xb5, 0x98, 0x22, 0x45, 0xfc, 0xba, 0x6a, 0xdf, 0x02, 0x9f, 0xdc, 0x51, 0x59, + 0x4a, 0x17, 0x2b, 0xc2, 0x94, 0xf4, 0xbb, 0xa3, 0x62, 0xe4, 0x71, 0xd4, 0xcd, 0x70, 0x16, 0xe1, + 0x49, 0x3c, 0xc0, 0xd8, 0x5c, 0x9b, 0xad, 0x85, 0x53, 0xa1, 0x7a, 0xc8, 0x2d, 0xe0, 0xd1, 0x72, + 0xa6, 0x2c, 0xc4, 0xe3, 0x76, 0x78, 0xb7, 0xb4, 0x09, 0x3b, 0x0e, 0x41, 0x4c, 0xde, 0xb2, 0x90, + 0x25, 0xa5, 0xd7, 0x03, 0x11, 0x00, 0xc3, 0x2e, 0x92, 0xef, 0x4e, 0x12, 0x9d, 0x7d, 0xcb, 0x35, + 0x10, 0xd5, 0x4f, 0x9e, 0x4d, 0xa9, 0x55, 0xc6, 0xd0, 0x7b, 0x18, 0x97, 0xd3, 0x36, 0xe6, 0x48, + 0x56, 0x81, 0x8f, 0x77, 0xcc, 0x9c, 0xb9, 0xe2, 0xac, 0xb8, 0x2f, 0x15, 0xa4, 0x7c, 0xda, 0x38, + 0x1e, 0x0b, 0x05, 0xd6, 0x14, 0x6e, 0x6c, 0x7e, 0x66, 0xfd, 0xb1, 0xe5, 0x60, 0xaf, 0x5e, 0x33, + 0x87, 0xc9, 0xf0, 0x5d, 0x6d, 0x3f, 0x88, 0x8d, 0xc7, 0xf7, 0x1d, 0xe9, 0xec, 0xed, 0x80, 0x29, + 0x27, 0xcf, 0x99, 0xa8, 0x50, 0x0f, 0x37, 0x24, 0x28, 0x30, 0x95, 0xd2, 0x3e, 0x5b, 0x40, 0x83, + 0xb3, 0x69, 0x57, 0x1f, 0x07, 0x1c, 0x8a, 0xbc, 0x20, 0xeb, 0xce, 0x8e, 0xab, 0xee, 0x31, 0xa2, + 0x73, 0xf9, 0xca, 0x3a, 0x1a, 0xfb, 0x0d, 0xc1, 0xfe, 0xfa, 0xf2, 0x6f, 0xbd, 0x96, 0xdd, 0x43, + 0x52, 0xb6, 0x08, 0xf3, 0xae, 0xbe, 0x19, 0x89, 0x32, 0x26, 0xb0, 0xea, 0x4b, 0x64, 0x84, 0x82, + 0x6b, 0xf5, 0x79, 0xbf, 0x01, 0x5f, 0x75, 0x63, 0x1b, 0x23, 0x3d, 0x68, 0x2a, 0x65, 0xe8, 0x91, + 0xf6, 0xff, 0x13, 0x58, 0xf1, 0x47, 0x0a, 0x7f, 0xc5, 0xa7, 0xe7, 0x61, 0x5a, 0x06, 0x46, 0x44, + 0x42, 0x04, 0xa0, 0xdb, 0x39, 0x86, 0x54, 0xaa, 0x8c, 0x34, 0x21, 0x8b, 0xf8, 0x0c, 0x74, 0x67 + }; + + private static readonly byte[] S3 = new byte[]{ + 0x68, 0x8d, 0xca, 0x4d, 0x73, 0x4b, 0x4e, 0x2a, 0xd4, 0x52, 0x26, 0xb3, 0x54, 0x1e, 0x19, 0x1f, + 0x22, 0x03, 0x46, 0x3d, 0x2d, 0x4a, 0x53, 0x83, 0x13, 0x8a, 0xb7, 0xd5, 0x25, 0x79, 0xf5, 0xbd, + 0x58, 0x2f, 0x0d, 0x02, 0xed, 0x51, 0x9e, 0x11, 0xf2, 0x3e, 0x55, 0x5e, 0xd1, 0x16, 0x3c, 0x66, + 0x70, 0x5d, 0xf3, 0x45, 0x40, 0xcc, 0xe8, 0x94, 0x56, 0x08, 0xce, 0x1a, 0x3a, 0xd2, 0xe1, 0xdf, + 0xb5, 0x38, 0x6e, 0x0e, 0xe5, 0xf4, 0xf9, 0x86, 0xe9, 0x4f, 0xd6, 0x85, 0x23, 0xcf, 0x32, 0x99, + 0x31, 0x14, 0xae, 0xee, 0xc8, 0x48, 0xd3, 0x30, 0xa1, 0x92, 0x41, 0xb1, 0x18, 0xc4, 0x2c, 0x71, + 0x72, 0x44, 0x15, 0xfd, 0x37, 0xbe, 0x5f, 0xaa, 0x9b, 0x88, 0xd8, 0xab, 0x89, 0x9c, 0xfa, 0x60, + 0xea, 0xbc, 0x62, 0x0c, 0x24, 0xa6, 0xa8, 0xec, 0x67, 0x20, 0xdb, 0x7c, 0x28, 0xdd, 0xac, 0x5b, + 0x34, 0x7e, 0x10, 0xf1, 0x7b, 0x8f, 0x63, 0xa0, 0x05, 0x9a, 0x43, 0x77, 0x21, 0xbf, 0x27, 0x09, + 0xc3, 0x9f, 0xb6, 0xd7, 0x29, 0xc2, 0xeb, 0xc0, 0xa4, 0x8b, 0x8c, 0x1d, 0xfb, 0xff, 0xc1, 0xb2, + 0x97, 0x2e, 0xf8, 0x65, 0xf6, 0x75, 0x07, 0x04, 0x49, 0x33, 0xe4, 0xd9, 0xb9, 0xd0, 0x42, 0xc7, + 0x6c, 0x90, 0x00, 0x8e, 0x6f, 0x50, 0x01, 0xc5, 0xda, 0x47, 0x3f, 0xcd, 0x69, 0xa2, 0xe2, 0x7a, + 0xa7, 0xc6, 0x93, 0x0f, 0x0a, 0x06, 0xe6, 0x2b, 0x96, 0xa3, 0x1c, 0xaf, 0x6a, 0x12, 0x84, 0x39, + 0xe7, 0xb0, 0x82, 0xf7, 0xfe, 0x9d, 0x87, 0x5c, 0x81, 0x35, 0xde, 0xb4, 0xa5, 0xfc, 0x80, 0xef, + 0xcb, 0xbb, 0x6b, 0x76, 0xba, 0x5a, 0x7d, 0x78, 0x0b, 0x95, 0xe3, 0xad, 0x74, 0x98, 0x3b, 0x36, + 0x64, 0x6d, 0xdc, 0xf0, 0x59, 0xa9, 0x4c, 0x17, 0x7f, 0x91, 0xb8, 0xc9, 0x57, 0x1b, 0xe0, 0x61 + }; + + private static readonly byte[] T0 = new byte[]{ + 0xa4, 0xa2, 0xa9, 0xc5, 0x4e, 0xc9, 0x03, 0xd9, 0x7e, 0x0f, 0xd2, 0xad, 0xe7, 0xd3, 0x27, 0x5b, + 0xe3, 0xa1, 0xe8, 0xe6, 0x7c, 0x2a, 0x55, 0x0c, 0x86, 0x39, 0xd7, 0x8d, 0xb8, 0x12, 0x6f, 0x28, + 0xcd, 0x8a, 0x70, 0x56, 0x72, 0xf9, 0xbf, 0x4f, 0x73, 0xe9, 0xf7, 0x57, 0x16, 0xac, 0x50, 0xc0, + 0x9d, 0xb7, 0x47, 0x71, 0x60, 0xc4, 0x74, 0x43, 0x6c, 0x1f, 0x93, 0x77, 0xdc, 0xce, 0x20, 0x8c, + 0x99, 0x5f, 0x44, 0x01, 0xf5, 0x1e, 0x87, 0x5e, 0x61, 0x2c, 0x4b, 0x1d, 0x81, 0x15, 0xf4, 0x23, + 0xd6, 0xea, 0xe1, 0x67, 0xf1, 0x7f, 0xfe, 0xda, 0x3c, 0x07, 0x53, 0x6a, 0x84, 0x9c, 0xcb, 0x02, + 0x83, 0x33, 0xdd, 0x35, 0xe2, 0x59, 0x5a, 0x98, 0xa5, 0x92, 0x64, 0x04, 0x06, 0x10, 0x4d, 0x1c, + 0x97, 0x08, 0x31, 0xee, 0xab, 0x05, 0xaf, 0x79, 0xa0, 0x18, 0x46, 0x6d, 0xfc, 0x89, 0xd4, 0xc7, + 0xff, 0xf0, 0xcf, 0x42, 0x91, 0xf8, 0x68, 0x0a, 0x65, 0x8e, 0xb6, 0xfd, 0xc3, 0xef, 0x78, 0x4c, + 0xcc, 0x9e, 0x30, 0x2e, 0xbc, 0x0b, 0x54, 0x1a, 0xa6, 0xbb, 0x26, 0x80, 0x48, 0x94, 0x32, 0x7d, + 0xa7, 0x3f, 0xae, 0x22, 0x3d, 0x66, 0xaa, 0xf6, 0x00, 0x5d, 0xbd, 0x4a, 0xe0, 0x3b, 0xb4, 0x17, + 0x8b, 0x9f, 0x76, 0xb0, 0x24, 0x9a, 0x25, 0x63, 0xdb, 0xeb, 0x7a, 0x3e, 0x5c, 0xb3, 0xb1, 0x29, + 0xf2, 0xca, 0x58, 0x6e, 0xd8, 0xa8, 0x2f, 0x75, 0xdf, 0x14, 0xfb, 0x13, 0x49, 0x88, 0xb2, 0xec, + 0xe4, 0x34, 0x2d, 0x96, 0xc6, 0x3a, 0xed, 0x95, 0x0e, 0xe5, 0x85, 0x6b, 0x40, 0x21, 0x9b, 0x09, + 0x19, 0x2b, 0x52, 0xde, 0x45, 0xa3, 0xfa, 0x51, 0xc2, 0xb5, 0xd1, 0x90, 0xb9, 0xf3, 0x37, 0xc1, + 0x0d, 0xba, 0x41, 0x11, 0x38, 0x7b, 0xbe, 0xd0, 0xd5, 0x69, 0x36, 0xc8, 0x62, 0x1b, 0x82, 0x8f + }; + + private static readonly byte[] T1 = new byte[]{ + 0x83, 0xf2, 0x2a, 0xeb, 0xe9, 0xbf, 0x7b, 0x9c, 0x34, 0x96, 0x8d, 0x98, 0xb9, 0x69, 0x8c, 0x29, + 0x3d, 0x88, 0x68, 0x06, 0x39, 0x11, 0x4c, 0x0e, 0xa0, 0x56, 0x40, 0x92, 0x15, 0xbc, 0xb3, 0xdc, + 0x6f, 0xf8, 0x26, 0xba, 0xbe, 0xbd, 0x31, 0xfb, 0xc3, 0xfe, 0x80, 0x61, 0xe1, 0x7a, 0x32, 0xd2, + 0x70, 0x20, 0xa1, 0x45, 0xec, 0xd9, 0x1a, 0x5d, 0xb4, 0xd8, 0x09, 0xa5, 0x55, 0x8e, 0x37, 0x76, + 0xa9, 0x67, 0x10, 0x17, 0x36, 0x65, 0xb1, 0x95, 0x62, 0x59, 0x74, 0xa3, 0x50, 0x2f, 0x4b, 0xc8, + 0xd0, 0x8f, 0xcd, 0xd4, 0x3c, 0x86, 0x12, 0x1d, 0x23, 0xef, 0xf4, 0x53, 0x19, 0x35, 0xe6, 0x7f, + 0x5e, 0xd6, 0x79, 0x51, 0x22, 0x14, 0xf7, 0x1e, 0x4a, 0x42, 0x9b, 0x41, 0x73, 0x2d, 0xc1, 0x5c, + 0xa6, 0xa2, 0xe0, 0x2e, 0xd3, 0x28, 0xbb, 0xc9, 0xae, 0x6a, 0xd1, 0x5a, 0x30, 0x90, 0x84, 0xf9, + 0xb2, 0x58, 0xcf, 0x7e, 0xc5, 0xcb, 0x97, 0xe4, 0x16, 0x6c, 0xfa, 0xb0, 0x6d, 0x1f, 0x52, 0x99, + 0x0d, 0x4e, 0x03, 0x91, 0xc2, 0x4d, 0x64, 0x77, 0x9f, 0xdd, 0xc4, 0x49, 0x8a, 0x9a, 0x24, 0x38, + 0xa7, 0x57, 0x85, 0xc7, 0x7c, 0x7d, 0xe7, 0xf6, 0xb7, 0xac, 0x27, 0x46, 0xde, 0xdf, 0x3b, 0xd7, + 0x9e, 0x2b, 0x0b, 0xd5, 0x13, 0x75, 0xf0, 0x72, 0xb6, 0x9d, 0x1b, 0x01, 0x3f, 0x44, 0xe5, 0x87, + 0xfd, 0x07, 0xf1, 0xab, 0x94, 0x18, 0xea, 0xfc, 0x3a, 0x82, 0x5f, 0x05, 0x54, 0xdb, 0x00, 0x8b, + 0xe3, 0x48, 0x0c, 0xca, 0x78, 0x89, 0x0a, 0xff, 0x3e, 0x5b, 0x81, 0xee, 0x71, 0xe2, 0xda, 0x2c, + 0xb8, 0xb5, 0xcc, 0x6e, 0xa8, 0x6b, 0xad, 0x60, 0xc6, 0x08, 0x04, 0x02, 0xe8, 0xf5, 0x4f, 0xa4, + 0xf3, 0xc0, 0xce, 0x43, 0x25, 0x1c, 0x21, 0x33, 0x0f, 0xaf, 0x47, 0xed, 0x66, 0x63, 0x93, 0xaa + }; + + private static readonly byte[] T2 = new byte[]{ + 0x45, 0xd4, 0x0b, 0x43, 0xf1, 0x72, 0xed, 0xa4, 0xc2, 0x38, 0xe6, 0x71, 0xfd, 0xb6, 0x3a, 0x95, + 0x50, 0x44, 0x4b, 0xe2, 0x74, 0x6b, 0x1e, 0x11, 0x5a, 0xc6, 0xb4, 0xd8, 0xa5, 0x8a, 0x70, 0xa3, + 0xa8, 0xfa, 0x05, 0xd9, 0x97, 0x40, 0xc9, 0x90, 0x98, 0x8f, 0xdc, 0x12, 0x31, 0x2c, 0x47, 0x6a, + 0x99, 0xae, 0xc8, 0x7f, 0xf9, 0x4f, 0x5d, 0x96, 0x6f, 0xf4, 0xb3, 0x39, 0x21, 0xda, 0x9c, 0x85, + 0x9e, 0x3b, 0xf0, 0xbf, 0xef, 0x06, 0xee, 0xe5, 0x5f, 0x20, 0x10, 0xcc, 0x3c, 0x54, 0x4a, 0x52, + 0x94, 0x0e, 0xc0, 0x28, 0xf6, 0x56, 0x60, 0xa2, 0xe3, 0x0f, 0xec, 0x9d, 0x24, 0x83, 0x7e, 0xd5, + 0x7c, 0xeb, 0x18, 0xd7, 0xcd, 0xdd, 0x78, 0xff, 0xdb, 0xa1, 0x09, 0xd0, 0x76, 0x84, 0x75, 0xbb, + 0x1d, 0x1a, 0x2f, 0xb0, 0xfe, 0xd6, 0x34, 0x63, 0x35, 0xd2, 0x2a, 0x59, 0x6d, 0x4d, 0x77, 0xe7, + 0x8e, 0x61, 0xcf, 0x9f, 0xce, 0x27, 0xf5, 0x80, 0x86, 0xc7, 0xa6, 0xfb, 0xf8, 0x87, 0xab, 0x62, + 0x3f, 0xdf, 0x48, 0x00, 0x14, 0x9a, 0xbd, 0x5b, 0x04, 0x92, 0x02, 0x25, 0x65, 0x4c, 0x53, 0x0c, + 0xf2, 0x29, 0xaf, 0x17, 0x6c, 0x41, 0x30, 0xe9, 0x93, 0x55, 0xf7, 0xac, 0x68, 0x26, 0xc4, 0x7d, + 0xca, 0x7a, 0x3e, 0xa0, 0x37, 0x03, 0xc1, 0x36, 0x69, 0x66, 0x08, 0x16, 0xa7, 0xbc, 0xc5, 0xd3, + 0x22, 0xb7, 0x13, 0x46, 0x32, 0xe8, 0x57, 0x88, 0x2b, 0x81, 0xb2, 0x4e, 0x64, 0x1c, 0xaa, 0x91, + 0x58, 0x2e, 0x9b, 0x5c, 0x1b, 0x51, 0x73, 0x42, 0x23, 0x01, 0x6e, 0xf3, 0x0d, 0xbe, 0x3d, 0x0a, + 0x2d, 0x1f, 0x67, 0x33, 0x19, 0x7b, 0x5e, 0xea, 0xde, 0x8b, 0xcb, 0xa9, 0x8c, 0x8d, 0xad, 0x49, + 0x82, 0xe4, 0xba, 0xc3, 0x15, 0xd1, 0xe0, 0x89, 0xfc, 0xb1, 0xb9, 0xb5, 0x07, 0x79, 0xb8, 0xe1 + }; + + private static readonly byte[] T3 = new byte[]{ + 0xb2, 0xb6, 0x23, 0x11, 0xa7, 0x88, 0xc5, 0xa6, 0x39, 0x8f, 0xc4, 0xe8, 0x73, 0x22, 0x43, 0xc3, + 0x82, 0x27, 0xcd, 0x18, 0x51, 0x62, 0x2d, 0xf7, 0x5c, 0x0e, 0x3b, 0xfd, 0xca, 0x9b, 0x0d, 0x0f, + 0x79, 0x8c, 0x10, 0x4c, 0x74, 0x1c, 0x0a, 0x8e, 0x7c, 0x94, 0x07, 0xc7, 0x5e, 0x14, 0xa1, 0x21, + 0x57, 0x50, 0x4e, 0xa9, 0x80, 0xd9, 0xef, 0x64, 0x41, 0xcf, 0x3c, 0xee, 0x2e, 0x13, 0x29, 0xba, + 0x34, 0x5a, 0xae, 0x8a, 0x61, 0x33, 0x12, 0xb9, 0x55, 0xa8, 0x15, 0x05, 0xf6, 0x03, 0x06, 0x49, + 0xb5, 0x25, 0x09, 0x16, 0x0c, 0x2a, 0x38, 0xfc, 0x20, 0xf4, 0xe5, 0x7f, 0xd7, 0x31, 0x2b, 0x66, + 0x6f, 0xff, 0x72, 0x86, 0xf0, 0xa3, 0x2f, 0x78, 0x00, 0xbc, 0xcc, 0xe2, 0xb0, 0xf1, 0x42, 0xb4, + 0x30, 0x5f, 0x60, 0x04, 0xec, 0xa5, 0xe3, 0x8b, 0xe7, 0x1d, 0xbf, 0x84, 0x7b, 0xe6, 0x81, 0xf8, + 0xde, 0xd8, 0xd2, 0x17, 0xce, 0x4b, 0x47, 0xd6, 0x69, 0x6c, 0x19, 0x99, 0x9a, 0x01, 0xb3, 0x85, + 0xb1, 0xf9, 0x59, 0xc2, 0x37, 0xe9, 0xc8, 0xa0, 0xed, 0x4f, 0x89, 0x68, 0x6d, 0xd5, 0x26, 0x91, + 0x87, 0x58, 0xbd, 0xc9, 0x98, 0xdc, 0x75, 0xc0, 0x76, 0xf5, 0x67, 0x6b, 0x7e, 0xeb, 0x52, 0xcb, + 0xd1, 0x5b, 0x9f, 0x0b, 0xdb, 0x40, 0x92, 0x1a, 0xfa, 0xac, 0xe4, 0xe1, 0x71, 0x1f, 0x65, 0x8d, + 0x97, 0x9e, 0x95, 0x90, 0x5d, 0xb7, 0xc1, 0xaf, 0x54, 0xfb, 0x02, 0xe0, 0x35, 0xbb, 0x3a, 0x4d, + 0xad, 0x2c, 0x3d, 0x56, 0x08, 0x1b, 0x4a, 0x93, 0x6a, 0xab, 0xb8, 0x7a, 0xf2, 0x7d, 0xda, 0x3f, + 0xfe, 0x3e, 0xbe, 0xea, 0xaa, 0x44, 0xc6, 0xd0, 0x36, 0x48, 0x70, 0x96, 0x77, 0x24, 0x53, 0xdf, + 0xf3, 0x83, 0x28, 0x32, 0x45, 0x1e, 0xa4, 0xd3, 0xa2, 0x46, 0x6e, 0x9c, 0xdd, 0x63, 0xd4, 0x9d }; #endregion public virtual string AlgorithmName { - get { return "Dstu7624"; } + get { return "DSTU7624"; } } public virtual int GetBlockSize() { - return blockSizeBits / BITS_IN_BYTE; + return wordsInBlock << 3; } public virtual bool IsPartialBlockOkay @@ -744,6 +1078,7 @@ namespace Org.BouncyCastle.Crypto.Engines public virtual void Reset() { + Array.Clear(internalState, 0, internalState.Length); } } } diff --git a/crypto/src/crypto/generators/SCrypt.cs b/crypto/src/crypto/generators/SCrypt.cs
index efa74d735..64a36df63 100644 --- a/crypto/src/crypto/generators/SCrypt.cs +++ b/crypto/src/crypto/generators/SCrypt.cs
@@ -8,12 +8,46 @@ using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Generators { - public class SCrypt + /// <summary>Implementation of the scrypt a password-based key derivation function.</summary> + /// <remarks> + /// Scrypt was created by Colin Percival and is specified in + /// <a href="http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01">draft-josefsson-scrypt-kd</a>. + /// </remarks> + public class SCrypt { - // TODO Validate arguments - public static byte[] Generate(byte[] P, byte[] S, int N, int r, int p, int dkLen) + /// <summary>Generate a key using the scrypt key derivation function.</summary> + /// <param name="P">the bytes of the pass phrase.</param> + /// <param name="S">the salt to use for this invocation.</param> + /// <param name="N">CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than + /// <code>2^(128 * r / 8)</code>.</param> + /// <param name="r">the block size, must be >= 1.</param> + /// <param name="p">Parallelization parameter. Must be a positive integer less than or equal to + /// <code>Int32.MaxValue / (128 * r * 8)</code>.</param> + /// <param name="dkLen">the length of the key to generate.</param> + /// <returns>the generated key.</returns> + public static byte[] Generate(byte[] P, byte[] S, int N, int r, int p, int dkLen) { - return MFcrypt(P, S, N, r, p, dkLen); + if (P == null) + throw new ArgumentNullException("Passphrase P must be provided."); + if (S == null) + throw new ArgumentNullException("Salt S must be provided."); + if (N <= 1) + throw new ArgumentException("Cost parameter N must be > 1."); + // Only value of r that cost (as an int) could be exceeded for is 1 + if (r == 1 && N >= 65536) + throw new ArgumentException("Cost parameter N must be > 1 and < 65536."); + if (r < 1) + throw new ArgumentException("Block size r must be >= 1."); + int maxParallel = Int32.MaxValue / (128 * r * 8); + if (p < 1 || p > maxParallel) + { + throw new ArgumentException("Parallelisation parameter p must be >= 1 and <= " + maxParallel + + " (based on block size r of " + r + ")"); + } + if (dkLen < 1) + throw new ArgumentException("Generated key length dkLen must be >= 1."); + + return MFcrypt(P, S, N, r, p, dkLen); } private static byte[] MFcrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen) diff --git a/crypto/src/security/DigestUtilities.cs b/crypto/src/security/DigestUtilities.cs
index 7ddf6c8e4..c6adbdd95 100644 --- a/crypto/src/security/DigestUtilities.cs +++ b/crypto/src/security/DigestUtilities.cs
@@ -3,10 +3,14 @@ using System.Collections; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.GM; +using Org.BouncyCastle.Asn1.Misc; using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Rosstandart; using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.UA; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto; @@ -20,7 +24,11 @@ namespace Org.BouncyCastle.Security public sealed class DigestUtilities { private enum DigestAlgorithm { + BLAKE2B_160, BLAKE2B_256, BLAKE2B_384, BLAKE2B_512, + BLAKE2S_128, BLAKE2S_160, BLAKE2S_224, BLAKE2S_256, + DSTU7564_256, DSTU7564_384, DSTU7564_512, GOST3411, + GOST3411_2012_256, GOST3411_2012_512, KECCAK_224, KECCAK_256, KECCAK_288, KECCAK_384, KECCAK_512, MD2, MD4, MD5, RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, @@ -28,6 +36,7 @@ namespace Org.BouncyCastle.Security SHA_512_224, SHA_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256, + SM3, TIGER, WHIRLPOOL, }; @@ -81,6 +90,24 @@ namespace Org.BouncyCastle.Security algorithms[NistObjectIdentifiers.IdShake128.Id] = "SHAKE128"; algorithms[NistObjectIdentifiers.IdShake256.Id] = "SHAKE256"; + algorithms[GMObjectIdentifiers.sm3.Id] = "SM3"; + + algorithms[MiscObjectIdentifiers.id_blake2b160.Id] = "BLAKE2B-160"; + algorithms[MiscObjectIdentifiers.id_blake2b256.Id] = "BLAKE2B-256"; + algorithms[MiscObjectIdentifiers.id_blake2b384.Id] = "BLAKE2B-384"; + algorithms[MiscObjectIdentifiers.id_blake2b512.Id] = "BLAKE2B-512"; + algorithms[MiscObjectIdentifiers.id_blake2s128.Id] = "BLAKE2S-128"; + algorithms[MiscObjectIdentifiers.id_blake2s160.Id] = "BLAKE2S-160"; + algorithms[MiscObjectIdentifiers.id_blake2s224.Id] = "BLAKE2S-224"; + algorithms[MiscObjectIdentifiers.id_blake2s256.Id] = "BLAKE2S-256"; + + algorithms[RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Id] = "GOST3411-2012-256"; + algorithms[RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Id] = "GOST3411-2012-512"; + + algorithms[UAObjectIdentifiers.dstu7564digest_256.Id] = "DSTU7564-256"; + algorithms[UAObjectIdentifiers.dstu7564digest_384.Id] = "DSTU7564-384"; + algorithms[UAObjectIdentifiers.dstu7564digest_512.Id] = "DSTU7564-512"; + oids["MD2"] = PkcsObjectIdentifiers.MD2; oids["MD4"] = PkcsObjectIdentifiers.MD4; oids["MD5"] = PkcsObjectIdentifiers.MD5; @@ -101,6 +128,20 @@ namespace Org.BouncyCastle.Security oids["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; oids["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; oids["GOST3411"] = CryptoProObjectIdentifiers.GostR3411; + oids["SM3"] = GMObjectIdentifiers.sm3; + oids["BLAKE2B-160"] = MiscObjectIdentifiers.id_blake2b160; + oids["BLAKE2B-256"] = MiscObjectIdentifiers.id_blake2b256; + oids["BLAKE2B-384"] = MiscObjectIdentifiers.id_blake2b384; + oids["BLAKE2B-512"] = MiscObjectIdentifiers.id_blake2b512; + oids["BLAKE2S-128"] = MiscObjectIdentifiers.id_blake2s128; + oids["BLAKE2S-160"] = MiscObjectIdentifiers.id_blake2s160; + oids["BLAKE2S-224"] = MiscObjectIdentifiers.id_blake2s224; + oids["BLAKE2S-256"] = MiscObjectIdentifiers.id_blake2s256; + oids["GOST3411-2012-256"] = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256; + oids["GOST3411-2012-512"] = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512; + oids["DSTU7564-256"] = UAObjectIdentifiers.dstu7564digest_256; + oids["DSTU7564-384"] = UAObjectIdentifiers.dstu7564digest_384; + oids["DSTU7564-512"] = UAObjectIdentifiers.dstu7564digest_512; } /// <summary> @@ -153,34 +194,48 @@ namespace Org.BouncyCastle.Security switch (digestAlgorithm) { - case DigestAlgorithm.GOST3411: return new Gost3411Digest(); - case DigestAlgorithm.KECCAK_224: return new KeccakDigest(224); - case DigestAlgorithm.KECCAK_256: return new KeccakDigest(256); - case DigestAlgorithm.KECCAK_288: return new KeccakDigest(288); - case DigestAlgorithm.KECCAK_384: return new KeccakDigest(384); - case DigestAlgorithm.KECCAK_512: return new KeccakDigest(512); - case DigestAlgorithm.MD2: return new MD2Digest(); - case DigestAlgorithm.MD4: return new MD4Digest(); - case DigestAlgorithm.MD5: return new MD5Digest(); - case DigestAlgorithm.RIPEMD128: return new RipeMD128Digest(); - case DigestAlgorithm.RIPEMD160: return new RipeMD160Digest(); - case DigestAlgorithm.RIPEMD256: return new RipeMD256Digest(); - case DigestAlgorithm.RIPEMD320: return new RipeMD320Digest(); - case DigestAlgorithm.SHA_1: return new Sha1Digest(); - case DigestAlgorithm.SHA_224: return new Sha224Digest(); - case DigestAlgorithm.SHA_256: return new Sha256Digest(); - case DigestAlgorithm.SHA_384: return new Sha384Digest(); - case DigestAlgorithm.SHA_512: return new Sha512Digest(); - case DigestAlgorithm.SHA_512_224: return new Sha512tDigest(224); - case DigestAlgorithm.SHA_512_256: return new Sha512tDigest(256); - case DigestAlgorithm.SHA3_224: return new Sha3Digest(224); - case DigestAlgorithm.SHA3_256: return new Sha3Digest(256); - case DigestAlgorithm.SHA3_384: return new Sha3Digest(384); - case DigestAlgorithm.SHA3_512: return new Sha3Digest(512); - case DigestAlgorithm.SHAKE128: return new ShakeDigest(128); - case DigestAlgorithm.SHAKE256: return new ShakeDigest(256); - case DigestAlgorithm.TIGER: return new TigerDigest(); - case DigestAlgorithm.WHIRLPOOL: return new WhirlpoolDigest(); + case DigestAlgorithm.BLAKE2B_160: return new Blake2bDigest(160); + case DigestAlgorithm.BLAKE2B_256: return new Blake2bDigest(256); + case DigestAlgorithm.BLAKE2B_384: return new Blake2bDigest(384); + case DigestAlgorithm.BLAKE2B_512: return new Blake2bDigest(512); + case DigestAlgorithm.BLAKE2S_128: return new Blake2sDigest(128); + case DigestAlgorithm.BLAKE2S_160: return new Blake2sDigest(160); + case DigestAlgorithm.BLAKE2S_224: return new Blake2sDigest(224); + case DigestAlgorithm.BLAKE2S_256: return new Blake2sDigest(256); + case DigestAlgorithm.DSTU7564_256: return new Dstu7564Digest(256); + case DigestAlgorithm.DSTU7564_384: return new Dstu7564Digest(384); + case DigestAlgorithm.DSTU7564_512: return new Dstu7564Digest(512); + case DigestAlgorithm.GOST3411: return new Gost3411Digest(); + case DigestAlgorithm.GOST3411_2012_256: return new GOST3411_2012_256Digest(); + case DigestAlgorithm.GOST3411_2012_512: return new GOST3411_2012_512Digest(); + case DigestAlgorithm.KECCAK_224: return new KeccakDigest(224); + case DigestAlgorithm.KECCAK_256: return new KeccakDigest(256); + case DigestAlgorithm.KECCAK_288: return new KeccakDigest(288); + case DigestAlgorithm.KECCAK_384: return new KeccakDigest(384); + case DigestAlgorithm.KECCAK_512: return new KeccakDigest(512); + case DigestAlgorithm.MD2: return new MD2Digest(); + case DigestAlgorithm.MD4: return new MD4Digest(); + case DigestAlgorithm.MD5: return new MD5Digest(); + case DigestAlgorithm.RIPEMD128: return new RipeMD128Digest(); + case DigestAlgorithm.RIPEMD160: return new RipeMD160Digest(); + case DigestAlgorithm.RIPEMD256: return new RipeMD256Digest(); + case DigestAlgorithm.RIPEMD320: return new RipeMD320Digest(); + case DigestAlgorithm.SHA_1: return new Sha1Digest(); + case DigestAlgorithm.SHA_224: return new Sha224Digest(); + case DigestAlgorithm.SHA_256: return new Sha256Digest(); + case DigestAlgorithm.SHA_384: return new Sha384Digest(); + case DigestAlgorithm.SHA_512: return new Sha512Digest(); + case DigestAlgorithm.SHA_512_224: return new Sha512tDigest(224); + case DigestAlgorithm.SHA_512_256: return new Sha512tDigest(256); + case DigestAlgorithm.SHA3_224: return new Sha3Digest(224); + case DigestAlgorithm.SHA3_256: return new Sha3Digest(256); + case DigestAlgorithm.SHA3_384: return new Sha3Digest(384); + case DigestAlgorithm.SHA3_512: return new Sha3Digest(512); + case DigestAlgorithm.SHAKE128: return new ShakeDigest(128); + case DigestAlgorithm.SHAKE256: return new ShakeDigest(256); + case DigestAlgorithm.SM3: return new SM3Digest(); + case DigestAlgorithm.TIGER: return new TigerDigest(); + case DigestAlgorithm.WHIRLPOOL: return new WhirlpoolDigest(); } } catch (ArgumentException)