From e9a0a93d3c05051438bbf41f0fd5218cd4e3fac7 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 6 Oct 2016 06:32:01 +0200 Subject: [PATCH] Add DES block cipher --- source/tanya/crypto/cipher.d | 279 --------------- source/tanya/crypto/des.d | 607 ++++++++++++++++++++++++++++++++ source/tanya/crypto/mode.d | 279 +++++++++++++++ source/tanya/crypto/package.d | 18 + source/tanya/crypto/symmetric.d | 177 ++++++++++ 5 files changed, 1081 insertions(+), 279 deletions(-) delete mode 100644 source/tanya/crypto/cipher.d create mode 100644 source/tanya/crypto/des.d create mode 100644 source/tanya/crypto/mode.d create mode 100644 source/tanya/crypto/package.d create mode 100644 source/tanya/crypto/symmetric.d 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"); + } +}