learncrypto/source/chacha20.d

345 lines
15 KiB
D
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module chacha20;
private import std.string : format;
private import std.range : isInputRange, isForwardRange, ElementType;
private import std.array;
private import std.traits : hasElaborateCopyConstructor;
private import bitmanip;
public:
// TODO: Check unittests (Use reliable software to check if the results are correct)
auto chacha20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce)
if(isInputRange!R && is(ElementType!R : ubyte))
{
static struct rangeResult
{
private:
ubyte[32] key;
ubyte[8] nonce;
ulong count;
R range;
ubyte[] chachaSection;
public:
this(R range, in ubyte[32] key, in ubyte[8] nonce)
{
this.range = range;
this.key = key;
this.nonce = nonce;
this.count = 0;
chachaSection = chacha20Exp(key, nonce ~ littleEndianInv(count));
}
bool empty() @property
{
return range.empty || (count == ulong.max && chachaSection.empty);
}
ubyte front() @property
{
assert(!empty);
return range.front ^ chachaSection.front;
}
void popFront()
{
assert(!empty);
chachaSection.popFront();
if(chachaSection.empty)
{
++count;
chachaSection = chacha20Exp(key, nonce ~ littleEndianInv(count));
}
range.popFront();
}
static if (isForwardRange!R)
{
auto save() @property
{
rangeResult copy;
copy.range = range.save;
copy.key = key.dup;
copy.nonce = nonce.dup;
copy.count = count;
copy.chachaSection = chachaSection.dup;
return copy;
}
}
}
return rangeResult(range, key, nonce);
}
private:
enum string quarterRound(alias _x0, alias _x1, alias _x2, alias _x3) = q{
%1$s += %2$s; %4$s ^= %1$s; %4$s = rotateLeft(%4$s, 16);
%3$s += %4$s; %2$s ^= %3$s; %2$s = rotateLeft(%2$s, 12);
%1$s += %2$s; %4$s ^= %1$s; %4$s = rotateLeft(%4$s, 8);
%3$s += %4$s; %2$s ^= %3$s; %2$s = rotateLeft(%2$s, 7);
}.format(__traits(identifier, _x0), __traits(identifier, _x1),
__traits(identifier, _x2), __traits(identifier, _x3));
unittest
{
uint a1 = 0x00000000, a2 = 0x00000000, a3 = 0x00000000, a4 = 0x00000000,
b1 = 0x00000001, b2 = 0x00000000, b3 = 0x00000000, b4 = 0x00000000,
c1 = 0x00000000, c2 = 0x00000001, c3 = 0x00000000, c4 = 0x00000000,
d1 = 0x00000000, d2 = 0x00000000, d3 = 0x00000001, d4 = 0x00000000,
e1 = 0x00000000, e2 = 0x00000000, e3 = 0x00000000, e4 = 0x00000001,
f1 = 0xe7e8c006, f2 = 0xc4f9417d, f3 = 0x6479b4b2, f4 = 0x68c67137,
g1 = 0xd3917c5b, g2 = 0x55f1c407, g3 = 0x52a58a7a, g4 = 0x8f887a3b;
mixin(quarterRound!(a1, a2, a3, a4));
mixin(quarterRound!(b1, b2, b3, b4));
mixin(quarterRound!(c1, c2, c3, c4));
mixin(quarterRound!(d1, d2, d3, d4));
mixin(quarterRound!(e1, e2, e3, e4));
mixin(quarterRound!(f1, f2, f3, f4));
mixin(quarterRound!(g1, g2, g3, g4));
assert([a1, a2, a3, a4] == [0x00000000, 0x00000000, 0x00000000, 0x00000000]);
assert([b1, b2, b3, b4] == [0x10000001, 0x80808808, 0x01010110, 0x01000110]);
assert([c1, c2, c3, c4] == [0x10001001, 0x88888808, 0x01110110, 0x01100110]);
assert([d1, d2, d3, d4] == [0x00001000, 0x08080080, 0x00100001, 0x00100000]);
assert([e1, e2, e3, e4] == [0x10000000, 0x80800808, 0x01010010, 0x01000010]);
assert([f1, f2, f3, f4] == [0x207cb2a0, 0x1f261df7, 0x9da4fd26, 0xc8768450]);
assert([g1, g2, g3, g4] == [0x18cb6df2, 0x41821bf8, 0x1fcb29a7, 0x92cbf922]);
}
enum string rowRound(alias _x00, alias _x01, alias _x02, alias _x03,
alias _x04, alias _x05, alias _x06, alias _x07,
alias _x08, alias _x09, alias _x10, alias _x11,
alias _x12, alias _x13, alias _x14, alias _x15,) = q{
mixin(quarterRound!(%1$s, %6$s, %11$s, %16$s));
mixin(quarterRound!(%2$s, %7$s, %12$s, %13$s));
mixin(quarterRound!(%3$s, %8$s, %9$s, %14$s));
mixin(quarterRound!(%4$s, %5$s, %10$s, %15$s));
}.format(__traits(identifier, _x00), __traits(identifier, _x01),
__traits(identifier, _x02), __traits(identifier, _x03),
__traits(identifier, _x04), __traits(identifier, _x05),
__traits(identifier, _x06), __traits(identifier, _x07),
__traits(identifier, _x08), __traits(identifier, _x09),
__traits(identifier, _x10), __traits(identifier, _x11),
__traits(identifier, _x12), __traits(identifier, _x13),
__traits(identifier, _x14), __traits(identifier, _x15));
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,
y12 = 0x00000001, y13 = 0x00000000, y14 = 0x00000000, y15 = 0x00000000;
uint x00 = 0x08521bd6, x01 = 0x1fe88837, x02 = 0xbb2aa576, x03 = 0x3aa26365,
x04 = 0xc54c6a5b, x05 = 0x2fc74c2f, x06 = 0x6dd39cc3, x07 = 0xda0a64f6,
x08 = 0x90a2f23d, x09 = 0x067f95a6, x10 = 0x06b35f61, x11 = 0x41e4732e,
x12 = 0xe859c100, x13 = 0xea4d84b7, x14 = 0x0f619bff, x15 = 0xbc6e965a;
mixin(rowRound!(y00, y01, y02, y03, y04, y05, y06, y07,
y08, y09, y10, y11, y12, y13, y14, y15));
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];
uint[] 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,
0x00100001, 0x01110110, 0x01010110, 0x01010010,
0x01000010, 0x00100000, 0x01100110, 0x01000110]);
assert(test1 == [0x9318dab2, 0x6219299a, 0xd68946c3, 0x3eb5b75f,
0xe0df816c, 0x476df40b, 0xbb2e1d6f, 0x7608ad18,
0x71b82d0d, 0xe707569d, 0x4c71a945, 0x0b2b589a,
0xe34c7f87, 0x5239bb58, 0x8a47d068, 0x475ec56d]);
}
enum string colRound(alias _x00, alias _x01, alias _x02, alias _x03,
alias _x04, alias _x05, alias _x06, alias _x07,
alias _x08, alias _x09, alias _x10, alias _x11,
alias _x12, alias _x13, alias _x14, alias _x15,) = q{
mixin(quarterRound!(%1$s, %5$s, %9$s, %13$s));
mixin(quarterRound!(%2$s, %6$s, %10$s, %14$s));
mixin(quarterRound!(%3$s, %7$s, %11$s, %15$s));
mixin(quarterRound!(%4$s, %8$s, %12$s, %16$s));
}.format(__traits(identifier, _x00), __traits(identifier, _x01),
__traits(identifier, _x02), __traits(identifier, _x03),
__traits(identifier, _x04), __traits(identifier, _x05),
__traits(identifier, _x06), __traits(identifier, _x07),
__traits(identifier, _x08), __traits(identifier, _x09),
__traits(identifier, _x10), __traits(identifier, _x11),
__traits(identifier, _x12), __traits(identifier, _x13),
__traits(identifier, _x14), __traits(identifier, _x15));
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,
y12 = 0x00000001, y13 = 0x00000000, y14 = 0x00000000, y15 = 0x00000000;
uint x00 = 0x08521bd6, x01 = 0x1fe88837, x02 = 0xbb2aa576, x03 = 0x3aa26365,
x04 = 0xc54c6a5b, x05 = 0x2fc74c2f, x06 = 0x6dd39cc3, x07 = 0xda0a64f6,
x08 = 0x90a2f23d, x09 = 0x067f95a6, x10 = 0x06b35f61, x11 = 0x41e4732e,
x12 = 0xe859c100, x13 = 0xea4d84b7, x14 = 0x0f619bff, x15 = 0xbc6e965a;
mixin(colRound!(y00, y01, y02, y03, y04, y05, y06, y07,
y08, y09, y10, y11, y12, y13, y14, y15));
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];
uint[] 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,
0x03030231, 0x00000000, 0x00000000, 0x00000000,
0x03000230, 0x00000000, 0x00000000, 0x00000000]);
assert(test1 == [0x54c4775a, 0xb72a4bef, 0xcaba7b13, 0xdc9d2cb9,
0x01a21f25, 0x229f731a, 0xb515e4fe, 0xcd79967d,
0xcd26b517, 0x533f496f, 0x5cd61313, 0x3c6a9772,
0xf5529d13, 0xfbee0de7, 0x7c5c8c13, 0x9c847b82]);
}
ubyte[] chacha20(in ubyte[] input) @safe nothrow pure
in
{
assert(input.length == 64);
}
out(result)
{
assert(result.length == 64);
}
body
{
auto x00 = littleEndian(input[0..4]), x01 = littleEndian(input[4..8]),
x02 = littleEndian(input[8..12]), x03 = littleEndian(input[12..16]),
x04 = littleEndian(input[16..20]), x05 = littleEndian(input[20..24]),
x06 = littleEndian(input[24..28]), x07 = littleEndian(input[28..32]),
x08 = littleEndian(input[32..36]), x09 = littleEndian(input[36..40]),
x10 = littleEndian(input[40..44]), x11 = littleEndian(input[44..48]),
x12 = littleEndian(input[48..52]), x13 = littleEndian(input[52..56]),
x14 = littleEndian(input[56..60]), x15 = littleEndian(input[60..64]);
auto y00 = x00, y01 = x01, y02 = x02, y03 = x03,
y04 = x04, y05 = x05, y06 = x06, y07 = x07,
y08 = x08, y09 = x09, y10 = x10, y11 = x11,
y12 = x12, y13 = x13, y14 = x14, y15 = x15;
foreach(i; 0..10)
{
mixin(colRound!(x00, x01, x02, x03, x04, x05, x06, x07,
x08, x09, x10, x11, x12, x13, x14, x15));
mixin(rowRound!(x00, x01, x02, x03, x04, x05, x06, x07,
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);
}
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];
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];
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];
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)
test3 = chacha20(test3);
assert(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]);
assert(test1 == [ 19,135,149, 14, 40,180,217,109,187,122,124,114, 23, 14,107,241,
231, 18, 85,210, 56,159, 57,224, 96, 71, 85,167,202,180, 98, 96,
185, 76, 56,161,168,230,132,189, 84, 59, 55,254, 39, 21, 41,153,
144,151, 5,208,189, 9,140, 64,226,151,219, 26, 80,221,237,194]);
assert(test2 == [ 76, 1, 30, 79,130,234,169,252,194, 25,110,243,141, 64, 68,140,
106, 32,248,152,219, 73,115,133, 10,143, 99,162, 72,155,138, 55,
58,214, 79, 49, 97, 63,201,155,178,203, 15, 94, 66,224,216,184,
64, 85,186,170, 65, 79, 11, 91, 22,247,191, 72, 68,250,229,108]);
assert(test3 == [ 88, 97,168, 54,215,192,249,115,185,160,231, 86,203,147, 27, 34,
237,252,228, 40, 1,135,107, 88,160,239, 5,182,250, 16,217,224,
101, 27,113, 5, 50,192, 52, 70, 7,110,217, 10, 3,226, 85,129,
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];
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);
}
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);
foreach(i; 0..16)
n[i] = cast(ubyte)(i + 1+ 100);
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,
248, 45, 55, 12, 69, 21,147,216,142,141,137,131, 14, 7,181, 1,
63,126,214,246, 74,167, 55,124,119,140,129,165,170,250,173, 94]);
}