optimized chacha20, added benchmark functionality, added some 'immutables'

This commit is contained in:
Johannes Loher 2016-07-06 02:00:03 +02:00
parent 0a9fef2cae
commit f6a718e331
6 changed files with 152 additions and 121 deletions

View file

@ -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)

View file

@ -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]);
}

View file

@ -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,10 +144,10 @@ 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,
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,
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,
@ -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,10 +196,10 @@ 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,
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,
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,
@ -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,
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,
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,
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,
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..1000000)
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,

View file

@ -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:

View file

@ -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,10 +143,10 @@ 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,
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,
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,
@ -197,10 +196,10 @@ 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,
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,
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,
@ -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,

View file

@ -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!");
}