From f6a718e33114ee302a4bc11f7adf89c3ba7d5f4f Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Wed, 6 Jul 2016 02:00:03 +0200 Subject: [PATCH] optimized chacha20, added benchmark functionality, added some 'immutables' --- source/actions.d | 15 ++-- source/app.d | 11 ++- source/chacha20.d | 185 ++++++++++++++++++++++++---------------------- source/cipher.d | 12 +-- source/salsa20.d | 37 +++++----- source/utility.d | 13 ++++ 6 files changed, 152 insertions(+), 121 deletions(-) diff --git a/source/actions.d b/source/actions.d index 53984ad..dbc4ebc 100644 --- a/source/actions.d +++ b/source/actions.d @@ -16,8 +16,8 @@ enum string randomDeviceName = "/dev/random"; void encrypt(string keyFileName, Cipher cipher, bool armor) { - auto key = loadKey(keyFileName, armor); - ubyte[8] nonce; + immutable key = loadKey(keyFileName, armor); + immutable ubyte[8] nonce; if (armor) { ubyte[base64ChunkSize] buf; @@ -61,9 +61,9 @@ void encrypt(string keyFileName, Cipher cipher, bool armor) void decrypt(string keyFileName, Cipher cipher, bool armor) { - import std.range; - auto key = loadKey(keyFileName, armor); - ubyte[8] nonce; + import std.range : inputRangeObject, InputRange; + immutable key = loadKey(keyFileName, armor); + immutable ubyte[8] nonce; ubyte[chunkSize] buf; int counter; InputRange!ubyte r; @@ -110,6 +110,11 @@ void generateKey(bool armor) } } +void bench() +{ + +} + private: ubyte[32] loadKey(string filename, bool armor) diff --git a/source/app.d b/source/app.d index bd00ea7..2e6659b 100644 --- a/source/app.d +++ b/source/app.d @@ -7,7 +7,7 @@ import actions; int main(string[] args) { - bool[string] actions = ["genKey" : false, "encrypt" : false, "decrypt" : false]; + bool[string] actions = ["genKey" : false, "encrypt" : false, "decrypt" : false, "benchmark" : false]; Cipher cipher = Cipher.chacha20; string keyFileName = "symkey.asc"; @@ -20,6 +20,7 @@ int main(string[] args) "Generate a new 256 bit key.", &actions["genKey"], "encrypt|e", "Encrypt a message.", &actions["encrypt"], "decrypt|d", "Decrypt a message.", &actions["decrypt"], + "benchmark|b", "Perform some benchmarks.", &actions["benchmark"], "cipher|c", "The cipher to use (default: %s).".format(cipher), &cipher, "key|k", "The file which contains the key (default: %s).".format(keyFileName), &keyFileName, "armor|a", "use ascii-armored I/O.", &armor); @@ -50,6 +51,10 @@ int main(string[] args) { decrypt(keyFileName, cipher, armor); } + else if (actions["benchmark"]) + { + bench(); + } } catch (Exception e) { @@ -72,6 +77,6 @@ void printHelp(Option[] options) { defaultGetoptPrinter("Usage: ./learncrypt [options]\n\nCommon options:", options[$ - 1 .. $]); defaultGetoptPrinter("\nGlobal options:", options[$ - 2 .. $ - 1]); - defaultGetoptPrinter("\nActions:", options[0 .. 3]); - defaultGetoptPrinter("\nAction options:", options[3 .. 5]); + defaultGetoptPrinter("\nActions:", options[0 .. 4]); + defaultGetoptPrinter("\nAction options:", options[4 .. 5]); } diff --git a/source/chacha20.d b/source/chacha20.d index 3baec5d..fdc56ad 100644 --- a/source/chacha20.d +++ b/source/chacha20.d @@ -1,15 +1,21 @@ module chacha20; -private import std.string : format; -private import std.range : isInputRange, isForwardRange, ElementType; -private import std.array; +private: + +import std.string : format; +import std.range : isInputRange, isForwardRange, ElementType; +import std.array; + +import bitmanip; +import utility; -private import bitmanip; public: // TODO: Check unittests (Use reliable software to check if the results are correct) +// TODO: Implement random access + auto chacha20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce) if (isInputRange!R && is(ElementType!R : ubyte)) { @@ -18,27 +24,26 @@ auto chacha20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce) private: ulong count; R range; - ubyte[] chachaSection; + ubyte[64] chachaSection; + uint chachaCounter; public: bool empty() @property { - return range.empty || (count == ulong.max && chachaSection.empty); + return range.empty || (count == ulong.max && chachaCounter == 64); } ubyte front() @property { assert(!empty); - return range.front ^ chachaSection.front; + return range.front ^ chachaSection[chachaCounter]; } void popFront() { assert(!empty); - chachaSection.popFront(); - if (chachaSection.empty) + if (++chachaCounter == 64) { - ++count; - chachaSection = chacha20Exp(key, nonce ~ littleEndianInv(count)); + chachaSection = chacha20Exp(key, concat!(ubyte[16])(nonce, littleEndianInv(++count))); } range.popFront(); } @@ -46,11 +51,25 @@ auto chacha20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce) { auto save() @property { - return rangeResult(count, range.save, chachaSection.dup); + return rangeResult(count, range.save, chachaSection); } } } - return rangeResult(0UL, range, chacha20Exp(key, nonce ~ littleEndianInv(0UL))); + return rangeResult(0UL, range, chacha20Exp(key, concat!(ubyte[16])(nonce, littleEndianInv(0UL)))); +} + +// TODO: Create more unittests!!! + +@safe unittest +{ + ubyte[] test = new ubyte[64]; + ubyte[32] key; + ubyte[8] nonce; + test = test.chacha20Cipher(key, nonce).array; + assert(test == [140,111, 5,175,106, 91,125,127, 60,125, 18, 49, 73, 47,186, 94, + 234,193,178,253,211,130, 18, 77,243,176, 91,203,126, 83, 82,194, + 77,251,230, 8,208,202, 74,201,254, 13,148,163, 20, 1,151,129, + 168,147,213,247, 92, 23,242, 73,135, 68,217,123, 87,123,234,199]); } private: @@ -63,7 +82,7 @@ enum string quarterRound(alias _x0, alias _x1, alias _x2, alias _x3) = q{ }.format(__traits(identifier, _x0), __traits(identifier, _x1), __traits(identifier, _x2), __traits(identifier, _x3)); -unittest +@safe unittest { uint a1 = 0x00000000, a2 = 0x00000000, a3 = 0x00000000, a4 = 0x00000000, b1 = 0x00000001, b2 = 0x00000000, b3 = 0x00000000, b4 = 0x00000000, @@ -107,7 +126,7 @@ enum string rowRound(alias _x00, alias _x01, alias _x02, alias _x03, __traits(identifier, _x12), __traits(identifier, _x13), __traits(identifier, _x14), __traits(identifier, _x15)); -unittest +@safe unittest { uint y00 = 0x00000001, y01 = 0x00000000, y02 = 0x00000000, y03 = 0x00000000, y04 = 0x00000001, y05 = 0x00000000, y06 = 0x00000000, y07 = 0x00000000, @@ -125,11 +144,11 @@ unittest mixin(rowRound!(x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, x12, x13, x14, x15)); - uint[] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, - y08, y09, y10, y11, y12, y13, y14, y15]; + immutable uint[16] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, + y08, y09, y10, y11, y12, y13, y14, y15]; - uint[] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, - x08, x09, x10, x11, x12, x13, x14, x15]; + immutable uint[16] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, + x08, x09, x10, x11, x12, x13, x14, x15]; assert(test0 == [0x10000001, 0x10000000, 0x00001000, 0x10001001, 0x88888808, 0x80808808, 0x80800808, 0x08080080, @@ -159,7 +178,7 @@ enum string colRound(alias _x00, alias _x01, alias _x02, alias _x03, __traits(identifier, _x12), __traits(identifier, _x13), __traits(identifier, _x14), __traits(identifier, _x15)); -unittest{ +@safe unittest{ uint y00 = 0x00000001, y01 = 0x00000000, y02 = 0x00000000, y03 = 0x00000000, y04 = 0x00000001, y05 = 0x00000000, y06 = 0x00000000, y07 = 0x00000000, y08 = 0x00000001, y09 = 0x00000000, y10 = 0x00000000, y11 = 0x00000000, @@ -177,11 +196,11 @@ unittest{ mixin(colRound!(x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, x12, x13, x14, x15)); - uint[] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, - y08, y09, y10, y11, y12, y13, y14, y15]; + immutable uint[16] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, + y08, y09, y10, y11, y12, y13, y14, y15]; - uint[] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, - x08, x09, x10, x11, x12, x13, x14, x15]; + immutable uint[16] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, + x08, x09, x10, x11, x12, x13, x14, x15]; assert(test0 == [0x30000002, 0x00000000, 0x00000000, 0x00000000, 0x81811899, 0x00000000, 0x00000000, 0x00000000, @@ -194,16 +213,7 @@ unittest{ 0xf5529d13, 0xfbee0de7, 0x7c5c8c13, 0x9c847b82]); } -ubyte[] chacha20(in ubyte[] input) @safe nothrow pure -in -{ - assert(input.length == 64); -} -out(result) -{ - assert(result.length == 64); -} -body +ubyte[64] chacha20(in ubyte[64] input) @safe nothrow pure @nogc { auto x00 = littleEndian(input[00..04]), x01 = littleEndian(input[04..08]), x02 = littleEndian(input[08..12]), x03 = littleEndian(input[12..16]), @@ -228,41 +238,49 @@ body x08, x09, x10, x11, x12, x13, x14, x15)); } - return littleEndianInv(x00 + y00) ~ littleEndianInv(x01 + y01) ~ - littleEndianInv(x02 + y02) ~ littleEndianInv(x03 + y03) ~ - littleEndianInv(x04 + y04) ~ littleEndianInv(x05 + y05) ~ - littleEndianInv(x06 + y06) ~ littleEndianInv(x07 + y07) ~ - littleEndianInv(x08 + y08) ~ littleEndianInv(x09 + y09) ~ - littleEndianInv(x10 + y10) ~ littleEndianInv(x11 + y11) ~ - littleEndianInv(x12 + y12) ~ littleEndianInv(x13 + y13) ~ - littleEndianInv(x14 + y14) ~ littleEndianInv(x15 + y15); + return concat!(ubyte[64])(littleEndianInv(x00 + y00), + littleEndianInv(x01 + y01), + littleEndianInv(x02 + y02), + littleEndianInv(x03 + y03), + littleEndianInv(x04 + y04), + littleEndianInv(x05 + y05), + littleEndianInv(x06 + y06), + littleEndianInv(x07 + y07), + littleEndianInv(x08 + y08), + littleEndianInv(x09 + y09), + littleEndianInv(x10 + y10), + littleEndianInv(x11 + y11), + littleEndianInv(x12 + y12), + littleEndianInv(x13 + y13), + littleEndianInv(x14 + y14), + littleEndianInv(x15 + y15)); } -unittest +@safe unittest { - ubyte[] test0 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + ubyte[64] test0 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; test0 = chacha20(test0); - ubyte[] test1 = [211,159, 13,115, 76, 55, 82,183, 3,117,222, 37,191,187,234,136, - 49,237,179, 48, 1,106,178,219,175,199,166, 48, 86, 16,179,207, - 31,240, 32, 63, 15, 83, 93,161,116,147, 48,113,238, 55,204, 36, - 79,201,235, 79, 3, 81,156, 47,203, 26,244,243, 88,118,104, 54]; + ubyte[64] test1 = [211,159, 13,115, 76, 55, 82,183, 3,117,222, 37,191,187,234,136, + 49,237,179, 48, 1,106,178,219,175,199,166, 48, 86, 16,179,207, + 31,240, 32, 63, 15, 83, 93,161,116,147, 48,113,238, 55,204, 36, + 79,201,235, 79, 3, 81,156, 47,203, 26,244,243, 88,118,104, 54]; test1 = chacha20(test1); - ubyte[] test2 = [ 88,118,104, 54, 79,201,235, 79, 3, 81,156, 47,203, 26,244,243, - 191,187,234,136,211,159, 13,115, 76, 55, 82,183, 3,117,222, 37, - 86, 16,179,207, 49,237,179, 48, 1,106,178,219,175,199,166, 48, - 238, 55,204, 36, 31,240, 32, 63, 15, 83, 93,161,116,147, 48,113]; + ubyte[64] test2 = [ 88,118,104, 54, 79,201,235, 79, 3, 81,156, 47,203, 26,244,243, + 191,187,234,136,211,159, 13,115, 76, 55, 82,183, 3,117,222, 37, + 86, 16,179,207, 49,237,179, 48, 1,106,178,219,175,199,166, 48, + 238, 55,204, 36, 31,240, 32, 63, 15, 83, 93,161,116,147, 48,113]; test2 = chacha20(test2); - ubyte[] test3 = [ 6,124, 83,146, 38,191, 9, 50, 4,161, 47,222,122,182,223,185, - 75, 27, 0,216, 16,122, 7, 89,162,104,101,147,213, 21, 54, 95, - 225,253,139,176,105,132, 23,116, 76, 41,176,207,221, 34,157,108, - 94, 94, 99, 52, 90,117, 91,220,146,190,239,143,196,176,130,186]; - foreach (i; 0..1000000) + ubyte[64] test3 = [ 6,124, 83,146, 38,191, 9, 50, 4,161, 47,222,122,182,223,185, + 75, 27, 0,216, 16,122, 7, 89,162,104,101,147,213, 21, 54, 95, + 225,253,139,176,105,132, 23,116, 76, 41,176,207,221, 34,157,108, + 94, 94, 99, 52, 90,117, 91,220,146,190,239,143,196,176,130,186]; + foreach (i; 0..1_000_000) test3 = chacha20(test3); assert(test0 == [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -286,39 +304,32 @@ unittest 29,214,235,224, 96, 42, 55, 53,188, 35, 81,171, 81,218,221, 44]); } -enum ubyte[4] σ0 = [101, 120, 112, 97]; -enum ubyte[4] σ1 = [110, 100, 32, 51]; -enum ubyte[4] σ2 = [ 50, 45, 98, 121]; -enum ubyte[4] σ3 = [116, 101, 32, 107]; +static immutable ubyte[4] sigma0 = [101, 120, 112, 97]; +static immutable ubyte[4] sigma1 = [110, 100, 32, 51]; +static immutable ubyte[4] sigma2 = [ 50, 45, 98, 121]; +static immutable ubyte[4] sigma3 = [116, 101, 32, 107]; + +ubyte[64] chacha20Exp(in ubyte[32] key, in ubyte[16] n) @safe pure nothrow @nogc -ubyte[] chacha20Exp(in ubyte[] key, in ubyte[] n) @safe nothrow pure -in { - assert(key.length == 32); - assert(n.length == 16); -} -out(result) -{ - assert(result.length == 64); -} -body -{ - return chacha20(σ0 ~ key[0..16] ~ σ1 ~ n ~ σ2 ~ key[16..$] ~ σ3); + return chacha20(concat!(ubyte[64])(sigma0, + key[0..16], + sigma1, + n, + sigma2, + key[16..$], + sigma3)); } -unittest +@safe unittest { - ubyte[] key; - ubyte[] n; - key.length = 32; - n.length = 16; - foreach (i; 0..16) - key[i] = cast(ubyte)(i + 1); - foreach (i; 16..32) - key[i] = cast(ubyte)(i + 200 - 15); + immutable ubyte[32] key = [ 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 201,202,203,204,205,206,207,208, + 209,210,211,212,213,214,215,216]; - foreach (i; 0..16) - n[i] = cast(ubyte)(i + 1+ 100); + immutable ubyte[16] n = [101,102,103,104,105,106,107,108, + 109,110,111,112,113,114,115,116]; assert(chacha20Exp(key, n) == [ 2, 7, 55,183,240,232, 0,145,207,208,120,131,146, 9,130, 31, 99,154, 60, 98,194,161,191, 80,167, 61,100,101,173,193, 48,203, diff --git a/source/cipher.d b/source/cipher.d index 78cad74..725dccc 100644 --- a/source/cipher.d +++ b/source/cipher.d @@ -1,13 +1,13 @@ module cipher; -private import std.range : isInputRange, isForwardRange, ElementType, +private: + +import std.range : isInputRange, isForwardRange, ElementType, InputRange, ForwardRange, inputRangeObject; +import std.string : format; -private import std.string : format; - -private import salsa20; - -private import chacha20; +import salsa20; +import chacha20; public: diff --git a/source/salsa20.d b/source/salsa20.d index cc8426a..a40e226 100644 --- a/source/salsa20.d +++ b/source/salsa20.d @@ -41,7 +41,6 @@ auto salsa20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce) assert(!empty); if (++salsaCounter == 64) { - import std.stdio; salsaSection = salsa20Exp(key, concat!(ubyte[16])(nonce, littleEndianInv(++count))); salsaCounter = 0; } @@ -58,7 +57,7 @@ auto salsa20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce) return rangeResult(0UL, range, salsa20Exp(key, concat!(ubyte[16])(nonce, littleEndianInv(0UL)))); } -// TODO: Create more unittest!!! +// TODO: Create more unittests!!! @safe unittest { @@ -144,11 +143,11 @@ enum string rowRound(alias _x00, alias _x01, alias _x02, alias _x03, mixin(rowRound!(x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, x12, x13, x14, x15)); - uint[] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, - y08, y09, y10, y11, y12, y13, y14, y15]; + immutable uint[16] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, + y08, y09, y10, y11, y12, y13, y14, y15]; - uint[] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, - x08, x09, x10, x11, x12, x13, x14, x15]; + immutable uint[16] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, + x08, x09, x10, x11, x12, x13, x14, x15]; assert(test0 == [0x08008145, 0x00000080, 0x00010200, 0x20500000, 0x20100001, 0x00048044, 0x00000080, 0x00010000, @@ -197,11 +196,11 @@ enum string colRound(alias _x00, alias _x01, alias _x02, alias _x03, mixin(colRound!(x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, x12, x13, x14, x15)); - immutable uint[] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, - y08, y09, y10, y11, y12, y13, y14, y15]; + immutable uint[16] test0 = [y00, y01, y02, y03, y04, y05, y06, y07, + y08, y09, y10, y11, y12, y13, y14, y15]; - immutable uint[] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, - x08, x09, x10, x11, x12, x13, x14, x15]; + immutable uint[16] test1 = [x00, x01, x02, x03, x04, x05, x06, x07, + x08, x09, x10, x11, x12, x13, x14, x15]; assert(test0 == [0x10090288, 0x00000000, 0x00000000, 0x00000000, 0x00000101, 0x00000000, 0x00000000, 0x00000000, @@ -281,7 +280,7 @@ ubyte[64] salsa20(in ubyte[64] input) pure nothrow @safe @nogc 75, 27, 0,216, 16,122, 7, 89,162,104,101,147,213, 21, 54, 95, 225,253,139,176,105,132, 23,116, 76, 41,176,207,221, 34,157,108, 94, 94, 99, 52, 90,117, 91,220,146,190,239,143,196,176,130,186]; - foreach (i; 0..1000000) + foreach (i; 0..1_000_000) test3 = salsa20(test3); assert(test0 == [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -310,7 +309,7 @@ static immutable ubyte[4] sigma1 = [110, 100, 32, 51]; static immutable ubyte[4] sigma2 = [ 50, 45, 98, 121]; static immutable ubyte[4] sigma3 = [116, 101, 32, 107]; -ubyte[64] salsa20Exp(in ubyte[32] key, in ubyte[16] n) @safe pure nothrow @nogc +ubyte[64] salsa20Exp(in ref ubyte[32] key, in ubyte[16] n) @safe pure nothrow @nogc { return salsa20(concat!(ubyte[64])(sigma0, key[0..16], @@ -323,15 +322,13 @@ ubyte[64] salsa20Exp(in ubyte[32] key, in ubyte[16] n) @safe pure nothrow @nogc @safe unittest { - ubyte[32] key; - ubyte[16] n; - foreach (i; 0..16) - key[i] = cast(ubyte)(i + 1); - foreach (i; 16..32) - key[i] = cast(ubyte)(i + 200 - 15); + immutable ubyte[32] key = [ 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 201,202,203,204,205,206,207,208, + 209,210,211,212,213,214,215,216]; - foreach (i; 0..16) - n[i] = cast(ubyte)(i + 1 + 100); + immutable ubyte[16] n = [101,102,103,104,105,106,107,108, + 109,110,111,112,113,114,115,116]; assert(salsa20Exp(key, n) == [ 69, 37, 68, 39, 41, 15,107,193,255,139,122, 6,170,233,217, 98, 89,144,182,106, 21, 51,200, 65,239, 49,222, 34,215,114, 40,126, diff --git a/source/utility.d b/source/utility.d index 207c937..248d6b4 100644 --- a/source/utility.d +++ b/source/utility.d @@ -12,3 +12,16 @@ auto concat(T : E[n], E, size_t n)(in E[][] args...) @nogc return result; } +@safe unittest +{ + assert(concat!(int[0])() == []); + assert(concat!(int[0])([]) == []); + assert(concat!(int[0])([], []) == []); + assert(concat!(int[1])([1]) == [1]); + assert(concat!(int[1])([1], []) == [1]); + assert(concat!(int[1])([], [1]) == [1]); + assert(concat!(int[2])([1, 2]) == [1, 2]); + assert(concat!(int[2])([1], [2]) == [1, 2]); + assert(concat!(int[6])([1], [2, 3], [4, 5, 6]) == [1, 2, 3, 4, 5, 6]); + assert(concat!(char[12])("Hello", " ", "World!") == "Hello World!"); +}