summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2016-10-06 06:32:01 +0200
committerEugen Wissner <belka@caraus.de>2016-10-06 06:32:01 +0200
commite9a0a93d3c05051438bbf41f0fd5218cd4e3fac7 (patch)
treeeb0db50553795583672d483c4933b49c8278ee6c /source
parentda5dc276d587c5f8f469d14f2db6981383143358 (diff)
downloadtanya-e9a0a93d3c05051438bbf41f0fd5218cd4e3fac7.tar.gz
Add DES block cipher
Diffstat (limited to 'source')
-rw-r--r--source/tanya/crypto/cipher.d279
-rw-r--r--source/tanya/crypto/des.d607
-rw-r--r--source/tanya/crypto/mode.d279
-rw-r--r--source/tanya/crypto/package.d18
-rw-r--r--source/tanya/crypto/symmetric.d177
5 files changed, 1081 insertions, 279 deletions
diff --git a/source/tanya/crypto/cipher.d b/source/tanya/crypto/cipher.d
deleted file mode 100644
index c74013b..0000000
--- a/source/tanya/crypto/cipher.d
+++ /dev/null
@@ -1,279 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * Copyright: Eugene Wissner 2016.
- * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
- * Mozilla Public License, v. 2.0).
- * Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
- */
-module tanya.crypto.cipher;
-
-import tanya.memory;
-import std.algorithm.iteration;
-import std.typecons;
-
-/**
- * Supported padding mode.
- *
- * See_Also:
- * $(D_PSYMBOL applyPadding)
- */
-enum Mode
-{
- zero,
- pkcs7,
- ansiX923,
-}
-
-/**
- * Params:
- * input = Sequence that should be padded.
- * mode = Padding mode.
- * blockSize = Block size.
- * allocator = Allocator that should be used if the block should be extended
- * or a new block should be added.
- *
- * Returns: The function modifies the initial array and returns it.
- *
- * See_Also:
- * $(D_PSYMBOL Mode)
- */
-ubyte[] applyPadding(ref ubyte[] input,
- in Mode mode,
- in ushort blockSize,
- shared Allocator allocator = defaultAllocator)
-in
-{
- assert(blockSize > 0 && blockSize <= 256);
- assert(blockSize % 64 == 0);
- assert(input.length > 0);
-}
-body
-{
- immutable rest = cast(ubyte) input.length % blockSize;
- immutable size_t lastBlock = input.length - (rest > 0 ? rest : blockSize);
- immutable needed = cast(ubyte) (rest > 0 ? blockSize - rest : 0);
-
- final switch (mode) with (Mode)
- {
- case zero:
- allocator.expandArray(input, needed);
- break;
- case pkcs7:
- if (needed)
- {
- allocator.expandArray(input, needed);
- input[input.length - needed ..$].each!((ref e) => e = needed);
- }
- else
- {
- allocator.expandArray(input, blockSize);
- }
- break;
- case ansiX923:
- allocator.expandArray(input, needed ? needed : blockSize);
- input[$ - 1] = needed;
- break;
- }
-
- return input;
-}
-
-///
-unittest
-{
- { // Zeros
- auto input = defaultAllocator.makeArray!ubyte(50);
-
- applyPadding(input, Mode.zero, 64);
- assert(input.length == 64);
-
- applyPadding(input, Mode.zero, 64);
- assert(input.length == 64);
- assert(input[63] == 0);
-
- defaultAllocator.dispose(input);
- }
- { // PKCS#7
- auto input = defaultAllocator.makeArray!ubyte(50);
- for (ubyte i; i < 40; ++i)
- {
- input[i] = i;
- }
-
- applyPadding(input, Mode.pkcs7, 64);
- assert(input.length == 64);
- for (ubyte i; i < 64; ++i)
- {
- if (i >= 40 && i < 50)
- {
- assert(input[i] == 0);
- }
- else if (i >= 50)
- {
- assert(input[i] == 14);
- }
- else
- {
- assert(input[i] == i);
- }
- }
-
- applyPadding(input, Mode.pkcs7, 64);
- assert(input.length == 128);
- for (ubyte i; i < 128; ++i)
- {
- if (i >= 64 || (i >= 40 && i < 50))
- {
- assert(input[i] == 0);
- }
- else if (i >= 50 && i < 64)
- {
- assert(input[i] == 14);
- }
- else
- {
- assert(input[i] == i);
- }
- }
-
- defaultAllocator.dispose(input);
- }
- { // ANSI X.923
- auto input = defaultAllocator.makeArray!ubyte(50);
- for (ubyte i; i < 40; ++i)
- {
- input[i] = i;
- }
-
- applyPadding(input, Mode.ansiX923, 64);
- assert(input.length == 64);
- for (ubyte i; i < 64; ++i)
- {
- if (i < 40)
- {
- assert(input[i] == i);
- }
- else if (i == 63)
- {
- assert(input[i] == 14);
- }
- else
- {
- assert(input[i] == 0);
- }
- }
-
- applyPadding(input, Mode.pkcs7, 64);
- assert(input.length == 128);
- for (ubyte i = 0; i < 128; ++i)
- {
- if (i < 40)
- {
- assert(input[i] == i);
- }
- else if (i == 63)
- {
- assert(input[i] == 14);
- }
- else
- {
- assert(input[i] == 0);
- }
- }
-
- defaultAllocator.dispose(input);
- }
-}
-
-/**
- * Params:
- * input = Sequence that should be padded.
- * mode = Padding mode.
- * blockSize = Block size.
- * allocator = Allocator that should be used for freeing the space allocated
- * for the padding.
- *
- * Returns: The function modifies the initial array and returns it.
- *
- * See_Also:
- * $(D_PSYMBOL applyPadding)
- */
-ref ubyte[] removePadding(ref ubyte[] input,
- in Mode mode,
- in ushort blockSize,
- shared Allocator allocator = defaultAllocator)
-in
-{
- assert(input.length != 0);
- assert(input.length % 64 == 0);
-}
-body
-{
- final switch (mode) with (Mode)
- {
- case zero:
- break;
- case pkcs7:
- case ansiX923:
- immutable last = input[$ - 1];
-
- allocator.shrinkArray(input, last ? last : blockSize);
- break;
- }
-
- return input;
-}
-
-///
-unittest
-{
- { // Zeros
- auto input = defaultAllocator.makeArray!ubyte(50);
- auto inputDup = defaultAllocator.makeArray!ubyte(50);
-
- applyPadding(input, Mode.zero, 64);
- applyPadding(inputDup, Mode.zero, 64);
-
- removePadding(input, Mode.zero, 64);
- assert(input == inputDup);
-
- defaultAllocator.dispose(input);
- defaultAllocator.dispose(inputDup);
-
- }
- { // PKCS#7
- auto input = defaultAllocator.makeArray!ubyte(50);
- auto inputDup = defaultAllocator.makeArray!ubyte(50);
- for (ubyte i; i < 40; ++i)
- {
- input[i] = i;
- inputDup[i] = i;
- }
-
- applyPadding(input, Mode.pkcs7, 64);
- removePadding(input, Mode.pkcs7, 64);
- assert(input == inputDup);
-
- defaultAllocator.dispose(input);
- defaultAllocator.dispose(inputDup);
- }
- { // ANSI X.923
- auto input = defaultAllocator.makeArray!ubyte(50);
- auto inputDup = defaultAllocator.makeArray!ubyte(50);
- for (ubyte i; i < 40; ++i)
- {
- input[i] = i;
- inputDup[i] = i;
- }
-
- applyPadding(input, Mode.pkcs7, 64);
- removePadding(input, Mode.pkcs7, 64);
- assert(input == inputDup);
-
- defaultAllocator.dispose(input);
- defaultAllocator.dispose(inputDup);
- }
-}
diff --git a/source/tanya/crypto/des.d b/source/tanya/crypto/des.d
new file mode 100644
index 0000000..a67ba6f
--- /dev/null
+++ b/source/tanya/crypto/des.d
@@ -0,0 +1,607 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Copyright: Eugene Wissner 2016.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
+ */
+module tanya.crypto.des;
+
+import tanya.container.bit;
+import tanya.crypto.symmetric;
+
+/// Initial permutation table.
+private immutable ubyte[64] ipTable = [58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7];
+
+/// Final permutation table.
+private immutable ubyte[64] fpTable = [40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25];
+
+/// Key permutation table 1.
+private immutable ubyte[64] pc1Table = [57, 49, 41, 33, 25, 17, 9, 1,
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 60, 52, 44, 36, 63, 55, 47, 39,
+ 31, 23, 15, 7, 62, 54, 46, 38,
+ 30, 22, 14, 6, 61, 53, 45, 37,
+ 29, 21, 13, 5, 28, 20, 12, 4];
+
+/// Key permutation table 2.
+private immutable ubyte[48] pc2Table = [14, 17, 11, 24, 1, 5, 3, 28,
+ 15, 6, 21, 10, 23, 19, 12, 4,
+ 26, 8, 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55, 30, 40,
+ 51, 45, 33, 48, 44, 49, 39, 56,
+ 34, 53, 46, 42, 50, 36, 29, 32];
+
+/// Expansion table.
+private immutable ubyte[48] expansionTable = [32, 1, 2, 3, 4, 5, 4, 5,
+ 6, 7, 8, 9, 8, 9, 10, 11,
+ 12, 13, 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21, 20, 21,
+ 22, 23, 24, 25, 24, 25, 26, 27,
+ 28, 29, 28, 29, 30, 31, 32, 1];
+
+/// Final input block permutation.
+private immutable ubyte[32] pTable = [16, 7, 20, 21, 29, 12, 28, 17,
+ 1, 15, 23, 26, 5, 18, 31, 10,
+ 2, 8, 24, 14, 32, 27, 3, 9,
+ 19, 13, 30, 6, 22, 11, 4, 25];
+
+/// The (in)famous S-boxes.
+private immutable ubyte[64][8] sBox = [[
+ 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
+ 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
+ 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
+ 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13,
+ ],[
+ 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
+ 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
+ 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
+ 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9,
+ ],[
+ 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
+ 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
+ 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
+ 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12,
+ ],[
+ 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
+ 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
+ 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
+ 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14,
+ ],[
+ 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
+ 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
+ 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
+ 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3,
+ ],[
+ 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
+ 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
+ 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
+ 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13,
+ ],[
+ 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
+ 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
+ 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
+ 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12,
+ ],[
+ 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
+ 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
+ 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
+ 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11,
+ ]];
+
+/**
+ * Data Encryption Standard.
+ *
+ * Params:
+ * L = Number of keys.
+ */
+class DES(ushort L = 1) : BlockCipher
+ if (L == 1)
+{
+ mixin FixedBlockSize!8;
+ mixin KeyLength!8;
+
+ private enum expansionBlockSize = 6;
+ private enum pc1KeyLength = 7;
+ private enum subkeyLength = 6;
+ private ubyte[] key_;
+
+ /**
+ * Params:
+ * key = Key.
+ */
+ @property void key(ubyte[] key) pure nothrow @safe @nogc
+ in
+ {
+ assert(key.length >= minKeyLength);
+ assert(key.length <= maxKeyLength);
+ }
+ body
+ {
+ key_ = key;
+ }
+
+ /**
+ * Encrypts a block.
+ *
+ * Params:
+ * plain = Plain text, input.
+ * cipher = Cipher text, output.
+ */
+ void encrypt(in ubyte[] plain, ubyte[] cipher)
+ nothrow
+ in
+ {
+ assert(plain.length == blockSize);
+ assert(cipher.length == blockSize);
+ }
+ body
+ {
+ operateBlock!(Direction.encryption)(plain, cipher);
+ }
+
+ /**
+ * Decrypts a block.
+ *
+ * Params:
+ * cipher = Cipher text, input.
+ * plain = Plain text, output.
+ */
+ void decrypt(in ubyte[] cipher, ubyte[] plain)
+ nothrow
+ in
+ {
+ assert(plain.length == blockSize);
+ assert(cipher.length == blockSize);
+ }
+ body
+ {
+ operateBlock!(Direction.decryption)(cipher, plain);
+ }
+
+ private void operateBlock(Direction D)(in ubyte[] source, ref ubyte[] target)
+ {
+ ubyte[blockSize_] ipBlock;
+ ubyte[expansionBlockSize] expansionBlock;
+ ubyte[4] substitutionBlock;
+ ubyte[4] pBoxTarget;
+ ubyte[pc1KeyLength] pc1Key;
+ ubyte[subkeyLength] subkey;
+
+ // Initial permutation
+ permute(source, ipBlock, ipTable, blockSize);
+
+ // Key schedule computation
+ permute(key_, pc1Key, pc1Table, pc1KeyLength);
+
+ // Feistel function
+ for (ubyte round; round < 16; ++round)
+ {
+ auto bitVector = BitVector(expansionBlock);
+ /* Expansion. This permutation only looks at the first 4 bytes (32
+ bits of ipBlock); 16 of these are repeated in expansion table.*/
+ permute(BitVector(ipBlock[4..$]), bitVector, expansionTable, 6);
+
+ // Key mixing
+ static if (D == Direction.encryption)
+ {
+ rotateLeft(pc1Key);
+ if (!(round <= 1 || round == 8 || round == 15))
+ {
+ // Rotate twice.
+ rotateLeft(pc1Key);
+ }
+ }
+ permute(pc1Key, subkey, pc2Table, subkeyLength);
+ static if (D == Direction.decryption)
+ {
+ rotateRight(pc1Key);
+ if (!(round >= 14 || round == 7 || round == 0))
+ {
+ // Rotate twice.
+ rotateRight(pc1Key);
+ }
+ }
+
+ bitVector ^= subkey;
+
+ // Substitution; copy from updated expansion block to ciphertext block
+ substitutionBlock[0] = cast(ubyte) (sBox[0][(expansionBlock[0] & 0xfc ) >> 2] << 4);
+ substitutionBlock[0] |= sBox[1][(expansionBlock[0] & 0x03) << 4 | (expansionBlock[1] & 0xf0) >> 4];
+ substitutionBlock[1] = cast(ubyte) (sBox[2][(expansionBlock[1] & 0x0f) << 2 | (expansionBlock[2] & 0xc0) >> 6] << 4);
+ substitutionBlock[1] |= sBox[3][(expansionBlock[2] & 0x3f)];
+ substitutionBlock[2] = cast(ubyte) (sBox[4][(expansionBlock[3] & 0xfc) >> 2 ] << 4);
+ substitutionBlock[2] |= sBox[5][(expansionBlock[3] & 0x03) << 4 | (expansionBlock[4] & 0xf0) >> 4];
+ substitutionBlock[3] = cast(ubyte) (sBox[6][(expansionBlock[4] & 0x0F) << 2 | (expansionBlock[5] & 0xc0) >> 6] << 4);
+ substitutionBlock[3] |= sBox[7][(expansionBlock[5] & 0x3f)];
+
+ // Permutation
+ bitVector = BitVector(substitutionBlock);
+ permute(bitVector, pBoxTarget, pTable, blockSize / 2);
+
+ // Swap the halves.
+ substitutionBlock = ipBlock[0..4];
+ ipBlock[0..4] = ipBlock[4..$];
+
+ bitVector ^= pBoxTarget;
+ ipBlock[4..$] = substitutionBlock;
+ }
+
+ substitutionBlock = ipBlock[0..4];
+ ipBlock[0..4] = ipBlock[4..$];
+ ipBlock[4..$] = substitutionBlock;
+
+ // Final permutaion (undo initial permuation).
+ permute(ipBlock, target, fpTable, blockSize);
+ }
+
+ /**
+ * Performs the left rotation operation on the key.
+ *
+ * Params:
+ * key = The key to rotate.
+ */
+ private void rotateLeft(ref ubyte[7] key) const pure nothrow @safe @nogc
+ {
+ immutable carryLeft = (key[0] & 0x80) >> 3;
+
+ key[0] = cast(ubyte) ((key[0] << 1) | ((key[1] & 0x80) >> 7));
+ key[1] = cast(ubyte) ((key[1] << 1) | ((key[2] & 0x80) >> 7));
+ key[2] = cast(ubyte) ((key[2] << 1) | ((key[3] & 0x80) >> 7));
+
+ immutable carryRight = (key[3] & 0x08) >> 3;
+ key[3] = cast(ubyte) ((((key[3] << 1) | ((key[4] & 0x80) >> 7)) & ~0x10) | carryLeft);
+
+ key[4] = cast(ubyte) ((key[4] << 1) | ((key[5] & 0x80) >> 7));
+ key[5] = cast(ubyte) ((key[5] << 1) | ((key[6] & 0x80) >> 7));
+ key[6] = cast(ubyte) ((key[6] << 1) | carryRight);
+ }
+
+ /**
+ * Performs the right rotation operation on the key.
+ *
+ * Params:
+ * key = The key to rotate.
+ */
+ private void rotateRight(ref ubyte[7] key) const pure nothrow @safe @nogc
+ {
+ immutable carryRight = (key[6] & 0x01) << 3;
+
+ key[6] = cast(ubyte) ((key[6] >> 1) | ((key[5] & 0x01) << 7));
+ key[5] = cast(ubyte) ((key[5] >> 1) | ((key[4] & 0x01) << 7));
+ key[4] = cast(ubyte) ((key[4] >> 1) | ((key[3] & 0x01) << 7));
+
+ immutable carryLeft = (key[3] & 0x10) << 3;
+ key[3] = cast(ubyte) ((((key[3] >> 1) | ((key[2] & 0x01) << 7)) & ~0x08) | carryRight);
+
+ key[2] = cast(ubyte) ((key[2] >> 1) | ((key[1] & 0x01) << 7));
+ key[1] = cast(ubyte) ((key[1] >> 1) | ((key[0] & 0x01) << 7));
+ key[0] = cast(ubyte) ((key[0] >> 1) | carryLeft);
+ }
+
+ private void permute(in ubyte[] source, ubyte[] target, immutable(ubyte[]) permuteTable, size_t length)
+ const pure nothrow @safe @nogc
+ {
+ const sourceVector = const BitVector(source);
+ auto targetVector = BitVector(target);
+
+ permute(sourceVector, targetVector, permuteTable, length);
+ }
+
+ private void permute(in BitVector source, ubyte[] target, immutable(ubyte[]) permuteTable, size_t length)
+ const pure nothrow @safe @nogc
+ {
+ auto targetVector = BitVector(target);
+
+ permute(source, targetVector, permuteTable, length);
+ }
+
+ private void permute(in BitVector source, ref BitVector target, immutable(ubyte[]) permuteTable, size_t length)
+ const pure nothrow @safe @nogc
+ {
+ for (uint i; i < length * 8; ++i)
+ {
+ target[i] = source[permuteTable[i] - 1];
+ }
+ }
+}
+
+version (unittest)
+{
+ import std.typecons;
+
+ /* Test vectors for DES. Source:
+ "Validating the Correctness of Hardware
+ Implementations of the NBS Data Encryption Standard"
+ NBS Special Publication 500-20, 1980. Appendix B */
+
+ // Initial and reverse Permutation and Expansion tests. Encrypt.
+ ubyte[8][64] desTestVectors1 = [
+ [0x95, 0xf8, 0xa5, 0xe5, 0xdd, 0x31, 0xd9, 0x00],
+ [0xdd, 0x7f, 0x12, 0x1c, 0xa5, 0x01, 0x56, 0x19],
+ [0x2e, 0x86, 0x53, 0x10, 0x4f, 0x38, 0x34, 0xea],
+ [0x4b, 0xd3, 0x88, 0xff, 0x6c, 0xd8, 0x1d, 0x4f],
+ [0x20, 0xb9, 0xe7, 0x67, 0xb2, 0xfb, 0x14, 0x56],
+ [0x55, 0x57, 0x93, 0x80, 0xd7, 0x71, 0x38, 0xef],
+ [0x6c, 0xc5, 0xde, 0xfa, 0xaf, 0x04, 0x51, 0x2f],
+ [0x0d, 0x9f, 0x27, 0x9b, 0xa5, 0xd8, 0x72, 0x60],
+ [0xd9, 0x03, 0x1b, 0x02, 0x71, 0xbd, 0x5a, 0x0a],
+ [0x42, 0x42, 0x50, 0xb3, 0x7c, 0x3d, 0xd9, 0x51],
+ [0xb8, 0x06, 0x1b, 0x7e, 0xcd, 0x9a, 0x21, 0xe5],
+ [0xf1, 0x5d, 0x0f, 0x28, 0x6b, 0x65, 0xbd, 0x28],
+ [0xad, 0xd0, 0xcc, 0x8d, 0x6e, 0x5d, 0xeb, 0xa1],
+ [0xe6, 0xd5, 0xf8, 0x27, 0x52, 0xad, 0x63, 0xd1],
+ [0xec, 0xbf, 0xe3, 0xbd, 0x3f, 0x59, 0x1a, 0x5e],
+ [0xf3, 0x56, 0x83, 0x43, 0x79, 0xd1, 0x65, 0xcd],
+ [0x2b, 0x9f, 0x98, 0x2f, 0x20, 0x03, 0x7f, 0xa9],
+ [0x88, 0x9d, 0xe0, 0x68, 0xa1, 0x6f, 0x0b, 0xe6],
+ [0xe1, 0x9e, 0x27, 0x5d, 0x84, 0x6a, 0x12, 0x98],
+ [0x32, 0x9a, 0x8e, 0xd5, 0x23, 0xd7, 0x1a, 0xec],
+ [0xe7, 0xfc, 0xe2, 0x25, 0x57, 0xd2, 0x3c, 0x97],
+ [0x12, 0xa9, 0xf5, 0x81, 0x7f, 0xf2, 0xd6, 0x5d],
+ [0xa4, 0x84, 0xc3, 0xad, 0x38, 0xdc, 0x9c, 0x19],
+ [0xfb, 0xe0, 0x0a, 0x8a, 0x1e, 0xf8, 0xad, 0x72],
+ [0x75, 0x0d, 0x07, 0x94, 0x07, 0x52, 0x13, 0x63],
+ [0x64, 0xfe, 0xed, 0x9c, 0x72, 0x4c, 0x2f, 0xaf],
+ [0xf0, 0x2b, 0x26, 0x3b, 0x32, 0x8e, 0x2b, 0x60],
+ [0x9d, 0x64, 0x55, 0x5a, 0x9a, 0x10, 0xb8, 0x52],
+ [0xd1, 0x06, 0xff, 0x0b, 0xed, 0x52, 0x55, 0xd7],
+ [0xe1, 0x65, 0x2c, 0x6b, 0x13, 0x8c, 0x64, 0xa5],
+ [0xe4, 0x28, 0x58, 0x11, 0x86, 0xec, 0x8f, 0x46],
+ [0xae, 0xb5, 0xf5, 0xed, 0xe2, 0x2d, 0x1a, 0x36],
+ [0xe9, 0x43, 0xd7, 0x56, 0x8a, 0xec, 0x0c, 0x5c],
+ [0xdf, 0x98, 0xc8, 0x27, 0x6f, 0x54, 0xb0, 0x4b],
+ [0xb1, 0x60, 0xe4, 0x68, 0x0f, 0x6c, 0x69, 0x6f],
+ [0xfa, 0x07, 0x52, 0xb0, 0x7d, 0x9c, 0x4a, 0xb8],
+ [0xca, 0x3a, 0x2b, 0x03, 0x6d, 0xbc, 0x85, 0x02],
+ [0x5e, 0x09, 0x05, 0x51, 0x7b, 0xb5, 0x9b, 0xcf],
+ [0x81, 0x4e, 0xeb, 0x3b, 0x91, 0xd9, 0x07, 0x26],
+ [0x4d, 0x49, 0xdb, 0x15, 0x32, 0x91, 0x9c, 0x9f],
+ [0x25, 0xeb, 0x5f, 0xc3, 0xf8, 0xcf, 0x06, 0x21],
+ [0xab, 0x6a, 0x20, 0xc0, 0x62, 0x0d, 0x1c, 0x6f],
+ [0x79, 0xe9, 0x0d, 0xbc, 0x98, 0xf9, 0x2c, 0xca],
+ [0x86, 0x6e, 0xce, 0xdd, 0x80, 0x72, 0xbb, 0x0e],
+ [0x8b, 0x54, 0x53, 0x6f, 0x2f, 0x3e, 0x64, 0xa8],
+ [0xea, 0x51, 0xd3, 0x97, 0x55, 0x95, 0xb8, 0x6b],
+ [0xca, 0xff, 0xc6, 0xac, 0x45, 0x42, 0xde, 0x31],
+ [0x8d, 0xd4, 0x5a, 0x2d, 0xdf, 0x90, 0x79, 0x6c],
+ [0x10, 0x29, 0xd5, 0x5e, 0x88, 0x0e, 0xc2, 0xd0],
+ [0x5d, 0x86, 0xcb, 0x23, 0x63, 0x9d, 0xbe, 0xa9],
+ [0x1d, 0x1c, 0xa8, 0x53, 0xae, 0x7c, 0x0c, 0x5f],
+ [0xce, 0x33, 0x23, 0x29, 0x24, 0x8f, 0x32, 0x28],
+ [0x84, 0x05, 0xd1, 0xab, 0xe2, 0x4f, 0xb9, 0x42],
+ [0xe6, 0x43, 0xd7, 0x80, 0x90, 0xca, 0x42, 0x07],
+ [0x48, 0x22, 0x1b, 0x99, 0x37, 0x74, 0x8a, 0x23],
+ [0xdd, 0x7c, 0x0b, 0xbd, 0x61, 0xfa, 0xfd, 0x54],
+ [0x2f, 0xbc, 0x29, 0x1a, 0x57, 0x0d, 0xb5, 0xc4],
+ [0xe0, 0x7c, 0x30, 0xd7, 0xe4, 0xe2, 0x6e, 0x12],
+ [0x09, 0x53, 0xe2, 0x25, 0x8e, 0x8e, 0x90, 0xa1],
+ [0x5b, 0x71, 0x1b, 0xc4, 0xce, 0xeb, 0xf2, 0xee],
+ [0xcc, 0x08, 0x3f, 0x1e, 0x6d, 0x9e, 0x85, 0xf6],
+ [0xd2, 0xfd, 0x88, 0x67, 0xd5, 0x0d, 0x2d, 0xfe],
+ [0x06, 0xe7, 0xea, 0x22, 0xce, 0x92, 0x70, 0x8f],
+ [0x16, 0x6b, 0x40, 0xb4, 0x4a, 0xba, 0x4b, 0xd6],
+ ];
+
+ // Key Permutation test. Encrypt.
+ // Test of right-shifts. Decrypt.
+ ubyte[8][56] desTestVectors2 = [
+ [0x95, 0xa8, 0xd7, 0x28, 0x13, 0xda, 0xa9, 0x4d],
+ [0x0e, 0xec, 0x14, 0x87, 0xdd, 0x8c, 0x26, 0xd5],
+ [0x7a, 0xd1, 0x6f, 0xfb, 0x79, 0xc4, 0x59, 0x26],
+ [0xd3, 0x74, 0x62, 0x94, 0xca, 0x6a, 0x6c, 0xf3],
+ [0x80, 0x9f, 0x5f, 0x87, 0x3c, 0x1f, 0xd7, 0x61],
+ [0xc0, 0x2f, 0xaf, 0xfe, 0xc9, 0x89, 0xd1, 0xfc],
+ [0x46, 0x15, 0xaa, 0x1d, 0x33, 0xe7, 0x2f, 0x10],
+ [0x20, 0x55, 0x12, 0x33, 0x50, 0xc0, 0x08, 0x58],
+ [0xdf, 0x3b, 0x99, 0xd6, 0x57, 0x73, 0x97, 0xc8],
+ [0x31, 0xfe, 0x17, 0x36, 0x9b, 0x52, 0x88, 0xc9],
+ [0xdf, 0xdd, 0x3c, 0xc6, 0x4d, 0xae, 0x16, 0x42],
+ [0x17, 0x8c, 0x83, 0xce, 0x2b, 0x39, 0x9d, 0x94],
+ [0x50, 0xf6, 0x36, 0x32, 0x4a, 0x9b, 0x7f, 0x80],
+ [0xa8, 0x46, 0x8e, 0xe3, 0xbc, 0x18, 0xf0, 0x6d],
+ [0xa2, 0xdc, 0x9e, 0x92, 0xfd, 0x3c, 0xde, 0x92],
+ [0xca, 0xc0, 0x9f, 0x79, 0x7d, 0x03, 0x12, 0x87],
+ [0x90, 0xba, 0x68, 0x0b, 0x22, 0xae, 0xb5, 0x25],
+ [0xce, 0x7a, 0x24, 0xf3, 0x50, 0xe2, 0x80, 0xb6],
+ [0x88, 0x2b, 0xff, 0x0a, 0xa0, 0x1a, 0x0b, 0x87],
+ [0x25, 0x61, 0x02, 0x88, 0x92, 0x45, 0x11, 0xc2],
+ [0xc7, 0x15, 0x16, 0xc2, 0x9c, 0x75, 0xd1, 0x70],
+ [0x51, 0x99, 0xc2, 0x9a, 0x52, 0xc9, 0xf0, 0x59],
+ [0xc2, 0x2f, 0x0a, 0x29, 0x4a, 0x71, 0xf2, 0x9f],
+ [0xee, 0x37, 0x14, 0x83, 0x71, 0x4c, 0x02, 0xea],
+ [0xa8, 0x1f, 0xbd, 0x44, 0x8f, 0x9e, 0x52, 0x2f],
+ [0x4f, 0x64, 0x4c, 0x92, 0xe1, 0x92, 0xdf, 0xed],
+ [0x1a, 0xfa, 0x9a, 0x66, 0xa6, 0xdf, 0x92, 0xae],
+ [0xb3, 0xc1, 0xcc, 0x71, 0x5c, 0xb8, 0x79, 0xd8],
+ [0x19, 0xd0, 0x32, 0xe6, 0x4a, 0xb0, 0xbd, 0x8b],
+ [0x3c, 0xfa, 0xa7, 0xa7, 0xdc, 0x87, 0x20, 0xdc],
+ [0xb7, 0x26, 0x5f, 0x7f, 0x44, 0x7a, 0xc6, 0xf3],
+ [0x9d, 0xb7, 0x3b, 0x3c, 0x0d, 0x16, 0x3f, 0x54],
+ [0x81, 0x81, 0xb6, 0x5b, 0xab, 0xf4, 0xa9, 0x75],
+ [0x93, 0xc9, 0xb6, 0x40, 0x42, 0xea, 0xa2, 0x40],
+ [0x55, 0x70, 0x53, 0x08, 0x29, 0x70, 0x55, 0x92],
+ [0x86, 0x38, 0x80, 0x9e, 0x87, 0x87, 0x87, 0xa0],
+ [0x41, 0xb9, 0xa7, 0x9a, 0xf7, 0x9a, 0xc2, 0x08],
+ [0x7a, 0x9b, 0xe4, 0x2f, 0x20, 0x09, 0xa8, 0x92],
+ [0x29, 0x03, 0x8d, 0x56, 0xba, 0x6d, 0x27, 0x45],
+ [0x54, 0x95, 0xc6, 0xab, 0xf1, 0xe5, 0xdf, 0x51],
+ [0xae, 0x13, 0xdb, 0xd5, 0x61, 0x48, 0x89, 0x33],
+ [0x02, 0x4d, 0x1f, 0xfa, 0x89, 0x04, 0xe3, 0x89],
+ [0xd1, 0x39, 0x97, 0x12, 0xf9, 0x9b, 0xf0, 0x2e],
+ [0x14, 0xc1, 0xd7, 0xc1, 0xcf, 0xfe, 0xc7, 0x9e],
+ [0x1d, 0xe5, 0x27, 0x9d, 0xae, 0x3b, 0xed, 0x6f],
+ [0xe9, 0x41, 0xa3, 0x3f, 0x85, 0x50, 0x13, 0x03],
+ [0xda, 0x99, 0xdb, 0xbc, 0x9a, 0x03, 0xf3, 0x79],
+ [0xb7, 0xfc, 0x92, 0xf9, 0x1d, 0x8e, 0x92, 0xe9],
+ [0xae, 0x8e, 0x5c, 0xaa, 0x3c, 0xa0, 0x4e, 0x85],
+ [0x9c, 0xc6, 0x2d, 0xf4, 0x3b, 0x6e, 0xed, 0x74],
+ [0xd8, 0x63, 0xdb, 0xb5, 0xc5, 0x9a, 0x91, 0xa0],
+ [0xa1, 0xab, 0x21, 0x90, 0x54, 0x5b, 0x91, 0xd7],
+ [0x08, 0x75, 0x04, 0x1e, 0x64, 0xc5, 0x70, 0xf7],
+ [0x5a, 0x59, 0x45, 0x28, 0xbe, 0xbe, 0xf1, 0xcc],
+ [0xfc, 0xdb, 0x32, 0x91, 0xde, 0x21, 0xf0, 0xc0],
+ [0x86, 0x9e, 0xfd, 0x7f, 0x9f, 0x26, 0x5a, 0x09],
+ ];
+
+ // Data permutation test. Encrypt.
+ ubyte[8][2][32] desTestVectors3 = [
+ [[0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31], [0x88, 0xd5, 0x5e, 0x54, 0xf5, 0x4c, 0x97, 0xb4]],
+ [[0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20], [0x0c, 0x0c, 0xc0, 0x0c, 0x83, 0xea, 0x48, 0xfd]],
+ [[0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20], [0x83, 0xbc, 0x8e, 0xf3, 0xa6, 0x57, 0x01, 0x83]],
+ [[0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20], [0xdf, 0x72, 0x5d, 0xca, 0xd9, 0x4e, 0xa2, 0xe9]],
+ [[0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01], [0xe6, 0x52, 0xb5, 0x3b, 0x55, 0x0b, 0xe8, 0xb0]],
+ [[0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01], [0xaf, 0x52, 0x71, 0x20, 0xc4, 0x85, 0xcb, 0xb0]],
+ [[0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01], [0x0f, 0x04, 0xce, 0x39, 0x3d, 0xb9, 0x26, 0xd5]],
+ [[0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01], [0xc9, 0xf0, 0x0f, 0xfc, 0x74, 0x07, 0x90, 0x67]],
+ [[0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01], [0x7c, 0xfd, 0x82, 0xa5, 0x93, 0x25, 0x2b, 0x4e]],
+ [[0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01], [0xcb, 0x49, 0xa2, 0xf9, 0xe9, 0x13, 0x63, 0xe3]],
+ [[0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40], [0x00, 0xb5, 0x88, 0xbe, 0x70, 0xd2, 0x3f, 0x56]],
+ [[0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40], [0x40, 0x6a, 0x9a, 0x6a, 0xb4, 0x33, 0x99, 0xae]],
+ [[0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01], [0x6c, 0xb7, 0x73, 0x61, 0x1d, 0xca, 0x9a, 0xda]],
+ [[0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01], [0x67, 0xfd, 0x21, 0xc1, 0x7d, 0xbb, 0x5d, 0x70]],
+ [[0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01], [0x95, 0x92, 0xcb, 0x41, 0x10, 0x43, 0x07, 0x87]],
+ [[0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20], [0xa6, 0xb7, 0xff, 0x68, 0xa3, 0x18, 0xdd, 0xd3]],
+ [[0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01], [0x4d, 0x10, 0x21, 0x96, 0xc9, 0x14, 0xca, 0x16]],
+ [[0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01], [0x2d, 0xfa, 0x9f, 0x45, 0x73, 0x59, 0x49, 0x65]],
+ [[0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01], [0xb4, 0x66, 0x04, 0x81, 0x6c, 0x0e, 0x07, 0x74]],
+ [[0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01], [0x6e, 0x7e, 0x62, 0x21, 0xa4, 0xf3, 0x4e, 0x87]],
+ [[0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01], [0xaa, 0x85, 0xe7, 0x46, 0x43, 0x23, 0x31, 0x99]],
+ [[0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01], [0x2e, 0x5a, 0x19, 0xdb, 0x4d, 0x19, 0x62, 0xd6]],
+ [[0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01], [0x23, 0xa8, 0x66, 0xa8, 0x09, 0xd3, 0x08, 0x94]],
+ [[0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01], [0xd8, 0x12, 0xd9, 0x61, 0xf0, 0x17, 0xd3, 0x20]],
+ [[0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b], [0x05, 0x56, 0x05, 0x81, 0x6e, 0x58, 0x60, 0x8f]],
+ [[0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01], [0xab, 0xd8, 0x8e, 0x8b, 0x1b, 0x77, 0x16, 0xf1]],
+ [[0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02], [0x53, 0x7a, 0xc9, 0x5b, 0xe6, 0x9d, 0xa1, 0xe1]],
+ [[0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08], [0xae, 0xd0, 0xf6, 0xae, 0x3c, 0x25, 0xcd, 0xd8]],
+ [[0x10, 0x02, 0x91, 0x14, 0x98, 0x10, 0x01, 0x04], [0xb3, 0xe3, 0x5a, 0x5e, 0xe5, 0x3e, 0x7b, 0x8d]],
+ [[0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04], [0x61, 0xc7, 0x9c, 0x71, 0x92, 0x1a, 0x2e, 0xf8]],
+ [[0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01], [0xe2, 0xf5, 0x72, 0x8f, 0x09, 0x95, 0x01, 0x3c]],
+ [[0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01], [0x1a, 0xea, 0xc3, 0x9a, 0x61, 0xf0, 0xa4, 0x64]],
+ ];
+
+ // S-Box test. Encrypt.
+ ubyte[8][3][19] desTestVectors4 = [
+ [[0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57], [0x01, 0xa1, 0xd6, 0xd0, 0x39, 0x77, 0x67, 0x42],
+ [0x69, 0x0f, 0x5b, 0x0d, 0x9a, 0x26, 0x93, 0x9b]],
+ [[0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e], [0x5c, 0xd5, 0x4c, 0xa8, 0x3d, 0xef, 0x57, 0xda],
+ [0x7a, 0x38, 0x9d, 0x10, 0x35, 0x4b, 0xd2, 0x71]],
+ [[0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86], [0x02, 0x48, 0xd4, 0x38, 0x06, 0xf6, 0x71, 0x72],
+ [0x86, 0x8e, 0xbb, 0x51, 0xca, 0xb4, 0x59, 0x9a]],
+ [[0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e], [0x51, 0x45, 0x4b, 0x58, 0x2d, 0xdf, 0x44, 0x0a],
+ [0x71, 0x78, 0x87, 0x6e, 0x01, 0xf1, 0x9b, 0x2a]],
+ [[0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6], [0x42, 0xfd, 0x44, 0x30, 0x59, 0x57, 0x7f, 0xa2],
+ [0xaf, 0x37, 0xfb, 0x42, 0x1f, 0x8c, 0x40, 0x95]],
+ [[0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce], [0x05, 0x9b, 0x5e, 0x08, 0x51, 0xcf, 0x14, 0x3a],
+ [0x86, 0xa5, 0x60, 0xf1, 0x0e, 0xc6, 0xd8, 0x5b]],
+ [[0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6], [0x07, 0x56, 0xd8, 0xe0, 0x77, 0x47, 0x61, 0xd2],
+ [0x0c, 0xd3, 0xda, 0x02, 0x00, 0x21, 0xdc, 0x09]],
+ [[0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe], [0x76, 0x25, 0x14, 0xb8, 0x29, 0xbf, 0x48, 0x6a],
+ [0xea, 0x67, 0x6b, 0x2c, 0xb7, 0xdb, 0x2b, 0x7a]],
+ [[0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16], [0x3b, 0xdd, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02],
+ [0xdf, 0xd6, 0x4a, 0x81, 0x5c, 0xaf, 0x1a, 0x0f]],
+ [[0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f], [0x26, 0x95, 0x5f, 0x68, 0x35, 0xaf, 0x60, 0x9a],
+ [0x5c, 0x51, 0x3c, 0x9c, 0x48, 0x86, 0xc0, 0x88]],
+ [[0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46], [0x16, 0x4d, 0x5e, 0x40, 0x4f, 0x27, 0x52, 0x32],
+ [0x0a, 0x2a, 0xee, 0xae, 0x3f, 0xf4, 0xab, 0x77]],
+ [[0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e], [0x6b, 0x05, 0x6e, 0x18, 0x75, 0x9f, 0x5c, 0xca],
+ [0xef, 0x1b, 0xf0, 0x3e, 0x5d, 0xfa, 0x57, 0x5a]],
+ [[0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76], [0x00, 0x4b, 0xd6, 0xef, 0x09, 0x17, 0x60, 0x62],
+ [0x88, 0xbf, 0x0d, 0xb6, 0xd7, 0x0d, 0xee, 0x56]],
+ [[0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07], [0x48, 0x0d, 0x39, 0x00, 0x6e, 0xe7, 0x62, 0xf2],
+ [0xa1, 0xf9, 0x91, 0x55, 0x41, 0x02, 0x0b, 0x56]],
+ [[0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f], [0x43, 0x75, 0x40, 0xc8, 0x69, 0x8f, 0x3c, 0xfa],
+ [0x6f, 0xbf, 0x1c, 0xaf, 0xcf, 0xfd, 0x05, 0x56]],
+ [[0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7], [0x07, 0x2d, 0x43, 0xa0, 0x77, 0x07, 0x52, 0x92],
+ [0x2f, 0x22, 0xe4, 0x9b, 0xab, 0x7c, 0xa1, 0xac]],
+ [[0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf], [0x02, 0xfe, 0x55, 0x77, 0x81, 0x17, 0xf1, 0x2a],
+ [0x5a, 0x6b, 0x61, 0x2c, 0xc2, 0x6c, 0xce, 0x4a]],
+ [[0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6], [0x1d, 0x9d, 0x5c, 0x50, 0x18, 0xf7, 0x28, 0xc2],
+ [0x5f, 0x4c, 0x03, 0x8e, 0xd1, 0x2b, 0x2e, 0x41]],
+ [[0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef], [0x30, 0x55, 0x32, 0x28, 0x6d, 0x6f, 0x29, 0x5a],
+ [0x63, 0xfa, 0xc0, 0xd0, 0x34, 0xd9, 0xf7, 0x93]],
+ ];
+}
+
+///
+unittest
+{
+ auto des = scoped!(DES!1);
+ ubyte[8] key = [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01];
+ ubyte[8] plain = [0x80, 0, 0, 0, 0, 0, 0, 0];
+ ubyte[8] cipher;
+
+ des.key = key;
+ foreach (ubyte i; 0..64)
+ {
+ if (i != 0)
+ {
+ plain[i / 8] = i % 8 ? plain[i / 8] >> 1 : 0x80;
+ if (i % 8 == 0)
+ {
+ plain[i / 8 - 1] = 0;
+ }
+ }
+ // Initial Permutation and Expansion test.
+ des.encrypt(plain, cipher);
+ assert(cipher == desTestVectors1[i]);
+
+ // Inverse Permutation and Expansion test.
+ des.encrypt(cipher, cipher);
+ assert(cipher == plain);
+ }
+
+ plain[0..$] = 0;
+ foreach (ubyte i; 0..56)
+ {
+ key[i / 7] = i % 7 ? key[i / 7] >> 1 : 0x80;
+ if (i % 7 == 0 && i != 0)
+ {
+ key[i / 7 - 1] = 0x01;
+ }
+ des.key = key;
+
+ // Initial Permutation and Expansion test.
+ des.encrypt(plain, cipher);
+ assert(cipher == desTestVectors2[i]);
+
+ // Test of right-shifts in Decryption.
+ des.decrypt(desTestVectors2[i], cipher);
+ assert(cipher == plain);
+ }
+
+ // Data permutation test.
+ plain[0..$] = 0;
+ foreach (i; desTestVectors3)
+ {
+ des.key = i[0];
+ des.encrypt(plain, cipher);
+ assert(cipher == i[1]);
+ }
+
+ // S-Box test.
+ foreach (i; desTestVectors4)
+ {
+ des.key = i[0];
+ des.encrypt(i[1], cipher);
+ assert(cipher == i[2]);
+ }
+}
diff --git a/source/tanya/crypto/mode.d b/source/tanya/crypto/mode.d
new file mode 100644
index 0000000..52c24e6
--- /dev/null
+++ b/source/tanya/crypto/mode.d
@@ -0,0 +1,279 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Block cipher modes of operation.
+ *
+ * Copyright: Eugene Wissner 2016.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
+ */
+module tanya.crypto.mode;
+
+import tanya.memory;
+import std.algorithm.iteration;
+import std.typecons;
+
+/**
+ * Supported padding mode.
+ *
+ * See_Also:
+ * $(D_PSYMBOL pad)
+ */
+enum PaddingMode
+{
+ zero,
+ pkcs7,
+ ansiX923,
+}
+
+/**
+ * Params:
+ * input = Sequence that should be padded.
+ * mode = Padding mode.
+ * blockSize = Block size.
+ * allocator = Allocator was used to allocate $(D_PARAM input).
+ *
+ * Returns: The function modifies the initial array and returns it.
+ *
+ * See_Also:
+ * $(D_PSYMBOL PaddingMode)
+ */
+ubyte[] pad(ref ubyte[] input,
+ in PaddingMode mode,
+ in ushort blockSize,
+ shared Allocator allocator = defaultAllocator)
+in
+{
+ assert(blockSize > 0 && blockSize <= 256);
+ assert(blockSize % 64 == 0);
+ assert(input.length > 0);
+}
+body
+{
+ immutable rest = cast(ubyte) input.length % blockSize;
+ immutable size_t lastBlock = input.length - (rest > 0 ? rest : blockSize);
+ immutable needed = cast(ubyte) (rest > 0 ? blockSize - rest : 0);
+
+ final switch (mode) with (PaddingMode)
+ {
+ case zero:
+ allocator.expandArray(input, needed);
+ break;
+ case pkcs7:
+ if (needed)
+ {
+ allocator.expandArray(input, needed);
+ input[input.length - needed ..$].each!((ref e) => e = needed);
+ }
+ else
+ {
+ allocator.expandArray(input, blockSize);
+ }
+ break;
+ case ansiX923:
+ allocator.expandArray(input, needed ? needed : blockSize);
+ input[$ - 1] = needed;
+ break;
+ }
+
+ return input;
+}
+
+///
+unittest
+{
+ { // Zeros
+ auto input = defaultAllocator.makeArray!ubyte(50);
+
+ pad(input, PaddingMode.zero, 64);
+ assert(input.length == 64);
+
+ pad(input, PaddingMode.zero, 64);
+ assert(input.length == 64);
+ assert(input[63] == 0);
+
+ defaultAllocator.dispose(input);
+ }
+ { // PKCS#7
+ auto input = defaultAllocator.makeArray!ubyte(50);
+ for (ubyte i; i < 40; ++i)
+ {
+ input[i] = i;
+ }
+
+ pad(input, PaddingMode.pkcs7, 64);
+ assert(input.length == 64);
+ for (ubyte i; i < 64; ++i)
+ {
+ if (i >= 40 && i < 50)
+ {
+ assert(input[i] == 0);
+ }
+ else if (i >= 50)
+ {
+ assert(input[i] == 14);
+ }
+ else
+ {
+ assert(input[i] == i);
+ }
+ }
+
+ pad(input, PaddingMode.pkcs7, 64);
+ assert(input.length == 128);
+ for (ubyte i; i < 128; ++i)
+ {
+ if (i >= 64 || (i >= 40 && i < 50))
+ {
+ assert(input[i] == 0);
+ }
+ else if (i >= 50 && i < 64)
+ {
+ assert(input[i] == 14);
+ }
+ else
+ {
+ assert(input[i] == i);
+ }
+ }
+
+ defaultAllocator.dispose(input);
+ }
+ { // ANSI X.923
+ auto input = defaultAllocator.makeArray!ubyte(50);
+ for (ubyte i; i < 40; ++i)
+ {
+ input[i] = i;
+ }
+
+ pad(input, PaddingMode.ansiX923, 64);
+ assert(input.length == 64);
+ for (ubyte i; i < 64; ++i)
+ {
+ if (i < 40)
+ {
+ assert(input[i] == i);
+ }
+ else if (i == 63)
+ {
+ assert(input[i] == 14);
+ }
+ else
+ {
+ assert(input[i] == 0);
+ }
+ }
+
+ pad(input, PaddingMode.pkcs7, 64);
+ assert(input.length == 128);
+ for (ubyte i = 0; i < 128; ++i)
+ {
+ if (i < 40)
+ {
+ assert(input[i] == i);
+ }
+ else if (i == 63)
+ {
+ assert(input[i] == 14);
+ }
+ else
+ {
+ assert(input[i] == 0);
+ }
+ }
+
+ defaultAllocator.dispose(input);
+ }
+}
+
+/**
+ * Params:
+ * input = Sequence that should be padded.
+ * mode = Padding mode.
+ * blockSize = Block size.
+ * allocator = Allocator was used to allocate $(D_PARAM input).
+ *
+ * Returns: The function modifies the initial array and returns it.
+ *
+ * See_Also:
+ * $(D_PSYMBOL pad)
+ */
+ref ubyte[] unpad(ref ubyte[] input,
+ in PaddingMode mode,
+ in ushort blockSize,
+ shared Allocator allocator = defaultAllocator)
+in
+{
+ assert(input.length != 0);
+ assert(input.length % 64 == 0);
+}
+body
+{
+ final switch (mode) with (PaddingMode)
+ {
+ case zero:
+ break;
+ case pkcs7:
+ case ansiX923:
+ immutable last = input[$ - 1];
+
+ allocator.shrinkArray(input, last ? last : blockSize);
+ break;
+ }
+
+ return input;
+}
+
+///
+unittest
+{
+ { // Zeros
+ auto input = defaultAllocator.makeArray!ubyte(50);
+ auto inputDup = defaultAllocator.makeArray!ubyte(50);
+
+ pad(input, PaddingMode.zero, 64);
+ pad(inputDup, PaddingMode.zero, 64);
+
+ unpad(input, PaddingMode.zero, 64);
+ assert(input == inputDup);
+
+ defaultAllocator.dispose(input);
+ defaultAllocator.dispose(inputDup);
+
+ }
+ { // PKCS#7
+ auto input = defaultAllocator.makeArray!ubyte(50);
+ auto inputDup = defaultAllocator.makeArray!ubyte(50);
+ for (ubyte i; i < 40; ++i)
+ {
+ input[i] = i;
+ inputDup[i] = i;
+ }
+
+ pad(input, PaddingMode.pkcs7, 64);
+ unpad(input, PaddingMode.pkcs7, 64);
+ assert(input == inputDup);
+
+ defaultAllocator.dispose(input);
+ defaultAllocator.dispose(inputDup);
+ }
+ { // ANSI X.923
+ auto input = defaultAllocator.makeArray!ubyte(50);
+ auto inputDup = defaultAllocator.makeArray!ubyte(50);
+ for (ubyte i; i < 40; ++i)
+ {
+ input[i] = i;
+ inputDup[i] = i;
+ }
+
+ pad(input, PaddingMode.pkcs7, 64);
+ unpad(input, PaddingMode.pkcs7, 64);
+ assert(input == inputDup);
+
+ defaultAllocator.dispose(input);
+ defaultAllocator.dispose(inputDup);
+ }
+}
diff --git a/source/tanya/crypto/package.d b/source/tanya/crypto/package.d
new file mode 100644
index 0000000..314d0f1
--- /dev/null
+++ b/source/tanya/crypto/package.d
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Copyright: Eugene Wissner 2016.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
+ */
+module tanya.crypto;
+
+public
+{
+ import tanya.crypto.des;
+ import tanya.crypto.mode;
+ import tanya.crypto.symmetric;
+}
diff --git a/source/tanya/crypto/symmetric.d b/source/tanya/crypto/symmetric.d
new file mode 100644
index 0000000..b6c268a
--- /dev/null
+++ b/source/tanya/crypto/symmetric.d
@@ -0,0 +1,177 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Interfaces for implementing secret key algorithms.
+ *
+ * Copyright: Eugene Wissner 2016.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
+ */
+module tanya.crypto.symmetric;
+
+/**
+ * Implemented by secret key algorithms.
+ */
+interface SymmetricCipher
+{
+ /**
+ * Returns: Key length.
+ */
+ @property inout(uint) keyLength() inout const pure nothrow @safe @nogc;
+
+ /**
+ * Returns: Minimum key length.
+ */
+ @property inout(uint) minKeyLength() inout const pure nothrow @safe @nogc;
+
+ /**
+ * Returns: Maximum key length.
+ */
+ @property inout(uint) maxKeyLength() inout const pure nothrow @safe @nogc;
+
+ /// Cipher direction.
+ protected enum Direction : ushort
+ {
+ encryption,
+ decryption,
+ }
+
+ /**
+ * Params:
+ * key = Key.
+ */
+ @property void key(ubyte[] key) pure nothrow @safe @nogc
+ in
+ {
+ assert(key.length >= minKeyLength);
+ assert(key.length <= maxKeyLength);
+ }
+}
+
+/**
+ * Implemented by block ciphers.
+ */
+interface BlockCipher : SymmetricCipher
+{
+ /**
+ * Returns: Block size.
+ */
+ @property inout(uint) blockSize() inout const pure nothrow @safe @nogc;
+
+ /**
+ * Encrypts a block.
+ *
+ * Params:
+ * plain = Plain text, input.
+ * cipher = Cipher text, output.
+ */
+ void encrypt(in ubyte[] plain, ubyte[] cipher)
+ in
+ {
+ assert(plain.length == blockSize);
+ assert(cipher.length == blockSize);
+ }
+
+ /**
+ * Decrypts a block.
+ *
+ * Params:
+ * cipher = Cipher text, input.
+ * plain = Plain text, output.
+ */
+ void decrypt(in ubyte[] cipher, ubyte[] plain)
+ in
+ {
+ assert(plain.length == blockSize);
+ assert(cipher.length == blockSize);
+ }
+}
+
+/**
+ * Mixed in by algorithms with fixed block size.
+ *
+ * Params:
+ * N = Block size.
+ */
+mixin template FixedBlockSize(uint N)
+ if (N != 0)
+{
+ private enum uint blockSize_ = N;
+
+ /**
+ * Returns: Fixed block size.
+ */
+ final @property inout(uint) blockSize() inout const pure nothrow @safe @nogc
+ {
+ return blockSize_;
+ }
+}
+
+/**
+ * Mixed in by symmetric algorithms.
+ * If $(D_PARAM Min) equals $(D_PARAM Max) fixed key length is assumed.
+ *
+ * Params:
+ * Min = Minimum key length.
+ * Max = Maximum key length.
+ */
+mixin template KeyLength(uint Min, uint Max = Min)
+ if (Min != 0 && Max != 0)
+{
+ static if (Min == Max)
+ {
+ private enum uint keyLength_ = Min;
+
+ /**
+ * Returns: Key length.
+ */
+ final @property inout(uint) keyLength() inout const pure nothrow @safe @nogc
+ {
+ return keyLength_;
+ }
+
+ /**
+ * Returns: Minimum key length.
+ */
+ final @property inout(uint) minKeyLength() inout const pure nothrow @safe @nogc
+ {
+ return keyLength_;
+ }
+
+ /**
+ * Returns: Maximum key length.
+ */
+ final @property inout(uint) maxKeyLength() inout const pure nothrow @safe @nogc
+ {
+ return keyLength_;
+ }
+ }
+ else static if (Min < Max)
+ {
+ private enum uint minKeyLength_ = Min;
+ private enum uint maxKeyLength_ = Max;
+
+ /**
+ * Returns: Minimum key length.
+ */
+ final @property inout(uint) minKeyLength() inout const pure nothrow @safe @nogc
+ {
+ return minKeyLength_;
+ }
+
+ /**
+ * Returns: Maximum key length.
+ */
+ final @property inout(uint) maxKeyLength() inout const pure nothrow @safe @nogc
+ {
+ return maxKeyLength_;
+ }
+ }
+ else
+ {
+ static assert(false, "Max should be larger or equal to Min");
+ }
+}