340 lines
15 KiB
D
340 lines
15 KiB
D
module salsa20;
|
|
|
|
private:
|
|
|
|
import std.string : format;
|
|
import std.range : isInputRange, isForwardRange, ElementType;
|
|
import std.array;
|
|
|
|
import bitmanip;
|
|
import utility;
|
|
|
|
|
|
public:
|
|
|
|
// TODO: Implement random access
|
|
|
|
auto salsa20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce)
|
|
if (isInputRange!R && is(ElementType!R : ubyte))
|
|
{
|
|
struct rangeResult
|
|
{
|
|
private:
|
|
ulong count;
|
|
R range;
|
|
ubyte[64] salsaSection;
|
|
uint salsaCounter;
|
|
public:
|
|
bool empty() @property
|
|
{
|
|
return range.empty || (count == ulong.max && salsaCounter == 64);
|
|
}
|
|
|
|
ubyte front() @property
|
|
{
|
|
assert(!empty);
|
|
return range.front ^ salsaSection[salsaCounter];
|
|
}
|
|
|
|
void popFront()
|
|
{
|
|
assert(!empty);
|
|
if (++salsaCounter == 64)
|
|
{
|
|
import std.stdio;
|
|
salsaSection = salsa20Exp(key, concat!(ubyte[16])(nonce, littleEndianInv(++count)));
|
|
salsaCounter = 0;
|
|
}
|
|
range.popFront();
|
|
}
|
|
static if (isForwardRange!R)
|
|
{
|
|
auto save() @property
|
|
{
|
|
return rangeResult(count, range.save, salsaSection);
|
|
}
|
|
}
|
|
}
|
|
return rangeResult(0UL, range, salsa20Exp(key, concat!(ubyte[16])(nonce, littleEndianInv(0UL))));
|
|
}
|
|
|
|
// TODO: Create more unittest!!!
|
|
|
|
@safe unittest
|
|
{
|
|
ubyte[] test = new ubyte[64];
|
|
ubyte[32] key;
|
|
ubyte[8] nonce;
|
|
test = test.salsa20Cipher(key, nonce).array;
|
|
assert(test == [154,151,246, 91,155, 76,114, 27,150, 10,103, 33, 69,252,168,212,
|
|
227, 46,103,249, 17, 30,169,121,206,156, 72, 38,128,106,238,230,
|
|
61,233,192,218, 43,215,249, 30,188,178, 99,155,249,137,198, 37,
|
|
27, 41,191, 56,211,154,155,220,231,197, 95, 75, 42,193, 42, 57]);
|
|
}
|
|
|
|
private:
|
|
|
|
enum string quarterRound(alias _x0, alias _x1, alias _x2, alias _x3) = q{
|
|
%2$s ^= (%1$s + %4$s).rotateLeft(7);
|
|
%3$s ^= (%2$s + %1$s).rotateLeft(9);
|
|
%4$s ^= (%3$s + %2$s).rotateLeft(13);
|
|
%1$s ^= (%4$s + %3$s).rotateLeft(18);
|
|
}.format(__traits(identifier, _x0), __traits(identifier, _x1),
|
|
__traits(identifier, _x2), __traits(identifier, _x3));
|
|
|
|
@safe 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] == [0x08008145, 0x00000080, 0x00010200, 0x20500000]);
|
|
assert([c1, c2, c3, c4] == [0x88000100, 0x00000001, 0x00000200, 0x00402000]);
|
|
assert([d1, d2, d3, d4] == [0x80040000, 0x00000000, 0x00000001, 0x00002000]);
|
|
assert([e1, e2, e3, e4] == [0x00048044, 0x00000080, 0x00010000, 0x20100001]);
|
|
assert([f1, f2, f3, f4] == [0xe876d72b, 0x9361dfd5, 0xf1460244, 0x948541a3]);
|
|
assert([g1, g2, g3, g4] == [0x3e2f308c, 0xd90a8f36, 0x6ab2a923, 0x2883524c]);
|
|
}
|
|
|
|
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, %2$s, %3$s, %4$s));
|
|
mixin(quarterRound!(%6$s, %7$s, %8$s, %5$s));
|
|
mixin(quarterRound!(%11$s, %12$s, %9$s, %10$s));
|
|
mixin(quarterRound!(%16$s, %13$s, %14$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));
|
|
|
|
@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,
|
|
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 == [0x08008145, 0x00000080, 0x00010200, 0x20500000,
|
|
0x20100001, 0x00048044, 0x00000080, 0x00010000,
|
|
0x00000001, 0x00002000, 0x80040000, 0x00000000,
|
|
0x00000001, 0x00000200, 0x00402000, 0x88000100]);
|
|
|
|
assert(test1 == [0xa890d39d, 0x65d71596, 0xe9487daa, 0xc8ca6a86,
|
|
0x949d2192, 0x764b7754, 0xe408d9b9, 0x7a41b4d1,
|
|
0x3402e183, 0x3c3af432, 0x50669f96, 0xd89ef0a8,
|
|
0x0040ede5, 0xb545fbce, 0xd257ed4f, 0x1818882d]);
|
|
}
|
|
|
|
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!(%6$s, %10$s, %14$s, %2$s));
|
|
mixin(quarterRound!(%11$s, %15$s, %3$s, %7$s));
|
|
mixin(quarterRound!(%16$s, %4$s, %8$s, %12$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));
|
|
|
|
@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,
|
|
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));
|
|
|
|
immutable uint[] 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];
|
|
|
|
assert(test0 == [0x10090288, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000101, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00020401, 0x00000000, 0x00000000, 0x00000000,
|
|
0x40a04001, 0x00000000, 0x00000000, 0x00000000]);
|
|
|
|
assert(test1 == [0x8c9d190a, 0xce8e4c90, 0x1ef8e9d3, 0x1326a71a,
|
|
0x90a20123, 0xead3c4f3, 0x63a091a0, 0xf0708d69,
|
|
0x789b010c, 0xd195a681, 0xeb7d5504, 0xa774135c,
|
|
0x481c2027, 0x53a8e4b5, 0x4c1f89c5, 0x3f78c9c8]);
|
|
}
|
|
|
|
ubyte[64] salsa20(in ubyte[64] input) pure nothrow @safe @nogc
|
|
{
|
|
auto x00 = littleEndian(input[00..04]), x01 = littleEndian(input[04..08]),
|
|
x02 = littleEndian(input[08..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 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));
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
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 = salsa20(test0);
|
|
|
|
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 = salsa20(test1);
|
|
|
|
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 = salsa20(test2);
|
|
|
|
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)
|
|
test3 = salsa20(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 == [109, 42,178,168,156,240,248,238,168,196,190,203, 26,110,170,154,
|
|
29, 29,150, 26,150, 30,235,249,190,163,251, 48, 69,144, 51, 57,
|
|
118, 40,152,157,180, 57, 27, 94,107, 42,236, 35, 27,111,114,114,
|
|
219,236,232,135,111,155,110, 18, 24,232, 95,158,179, 19, 48,202]);
|
|
|
|
assert(test2 == [179, 19, 48,202,219,236,232,135,111,155,110, 18, 24,232, 95,158,
|
|
26,110,170,154,109, 42,178,168,156,240,248,238,168,196,190,203,
|
|
69,144, 51, 57, 29, 29,150, 26,150, 30,235,249,190,163,251, 48,
|
|
27,111,114,114,118, 40,152,157,180, 57, 27, 94,107, 42,236, 35]);
|
|
|
|
assert(test3 == [ 8, 18, 38,199,119, 76,215, 67,173,127,144,162,103,212,176,217,
|
|
192, 19,233, 33,159,197,154,160,128,243,219, 65,171,136,135,225,
|
|
123, 11, 68, 86,237, 82, 20,155,133,189, 9, 83,167,116,194, 78,
|
|
122,127,195,185,185,204,188, 90,245, 9,183,248,226, 85,245,104]);
|
|
}
|
|
|
|
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] salsa20Exp(in ubyte[32] key, in ubyte[16] n) @safe pure nothrow @nogc
|
|
{
|
|
return salsa20(concat!(ubyte[64])(sigma0,
|
|
key[0..16],
|
|
sigma1,
|
|
n,
|
|
sigma2,
|
|
key[16..$],
|
|
sigma3));
|
|
}
|
|
|
|
@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);
|
|
|
|
foreach (i; 0..16)
|
|
n[i] = cast(ubyte)(i + 1 + 100);
|
|
|
|
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,
|
|
104,197, 7,225,197,153, 31, 2,102, 78, 76,176, 84,245,246,184,
|
|
177,160,133,130, 6, 72,149,119,192,195,132,236,234,103,246, 74]);
|
|
}
|