module chacha20; private: import std.string : format; import std.range : isInputRange, isForwardRange, ElementType; import std.array; import bitmanip; import utility; public: // TODO: Implement random access auto chacha20Cipher(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] chachaSection; uint chachaCounter; public: bool empty() @property { return range.empty || (count == ulong.max && chachaCounter == 64); } ubyte front() @property { assert(!empty); return range.front ^ chachaSection[chachaCounter]; } void popFront() { assert(!empty); if (++chachaCounter == 64) { chachaSection = chacha20Exp(key, concat!(ubyte[16])(littleEndianInv(++count), nonce)); chachaCounter = 0; } range.popFront(); } static if (isForwardRange!R) { auto save() @property { return rangeResult(count, range.save, chachaSection, chachaCounter); } } } return rangeResult(0UL, range, chacha20Exp(key, concat!(ubyte[16])([0, 0, 0, 0, 0, 0, 0, 0], nonce))); } // TODO: Create more unittests!!! @safe unittest { ubyte[] test1 = new ubyte[64]; ubyte[32] key1; ubyte[8] nonce1; test1 = test1.chacha20Cipher(key1, nonce1).array; assert(test1 == [118, 184, 224, 173, 160, 241, 61, 144, 64, 93, 106, 229, 83, 134, 189, 40, 189, 210, 25, 184, 160, 141, 237, 26, 168, 54, 239, 204, 139, 119, 13, 199, 218, 65, 89, 124, 81, 87, 72, 141, 119, 36, 224, 63, 184, 216, 74, 55, 106, 67, 184, 244, 21, 24, 161, 28, 195, 135, 182, 105, 178, 238, 101, 134]); } 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)); @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] == [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)); @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)); immutable uint[16] test0 = [ y00, y01, y02, y03, y04, y05, y06, y07, y08, y09, y10, y11, y12, y13, y14, y15 ]; 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, 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)); @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[16] test0 = [ y00, y01, y02, y03, y04, y05, y06, y07, y08, y09, y10, y11, y12, y13, y14, y15 ]; 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, 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[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]), 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 = chacha20(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 = chacha20(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 = chacha20(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 .. 1_000_000) 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]); } 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 { return chacha20(concat!(ubyte[64])(sigma0, sigma1, sigma2, sigma3, key, n)); } @safe unittest { 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]; immutable ubyte[16] n = [ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116 ]; assert(chacha20Exp(key, n) == [91, 78, 219, 119, 210, 190, 8, 62, 37, 143, 86, 137, 217, 173, 192, 90, 200, 33, 74, 29, 135, 175, 94, 6, 23, 221, 182, 9, 17, 163, 46, 191, 222, 214, 172, 130, 247, 120, 162, 47, 190, 213, 83, 21, 242, 210, 49, 174, 198, 64, 8, 65, 98, 73, 10, 144, 56, 48, 201, 68, 1, 174, 183, 172]); // Test vectors consistent with http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 immutable ubyte[32] key1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; immutable ubyte[16] n1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; immutable ubyte[32] key2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; immutable ubyte[16] n2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; immutable ubyte[32] key3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; immutable ubyte[16] n3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; immutable ubyte[32] key4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; immutable ubyte[16] n4 = [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]; assert(chacha20Exp(key1, n1) == [118, 184, 224, 173, 160, 241, 61, 144, 64, 93, 106, 229, 83, 134, 189, 40, 189, 210, 25, 184, 160, 141, 237, 26, 168, 54, 239, 204, 139, 119, 13, 199, 218, 65, 89, 124, 81, 87, 72, 141, 119, 36, 224, 63, 184, 216, 74, 55, 106, 67, 184, 244, 21, 24, 161, 28, 195, 135, 182, 105, 178, 238, 101, 134]); assert(chacha20Exp(key2, n2) == [69, 64, 240, 90, 159, 31, 178, 150, 215, 115, 110, 123, 32, 142, 60, 150, 235, 79, 225, 131, 70, 136, 210, 96, 79, 69, 9, 82, 237, 67, 45, 65, 187, 226, 160, 182, 234, 117, 102, 210, 165, 209, 231, 226, 13, 66, 175, 44, 83, 215, 146, 177, 196, 63, 234, 129, 126, 154, 210, 117, 174, 84, 105, 99]); assert(chacha20Exp(key3, n3) == [222, 156, 186, 123, 243, 214, 158, 245, 231, 134, 220, 99, 151, 63, 101, 58, 11, 73, 224, 21, 173, 191, 247, 19, 79, 203, 125, 241, 55, 130, 16, 49, 232, 90, 5, 2, 120, 167, 8, 69, 39, 33, 79, 115, 239, 199, 250, 91, 82, 119, 6, 46, 183, 160, 67, 62, 68, 95, 65, 227, 26, 250, 183, 87]); assert(chacha20Exp(key4, n4) == [239, 63, 223, 214, 198, 21, 120, 251, 245, 207, 53, 189, 61, 211, 59, 128, 9, 99, 22, 52, 210, 30, 66, 172, 51, 150, 11, 209, 56, 229, 13, 50, 17, 30, 76, 175, 35, 126, 229, 60, 168, 173, 100, 38, 25, 74, 136, 84, 93, 220, 73, 122, 11, 70, 110, 125, 107, 189, 176, 4, 27, 47, 88, 107]); }