optimized speed and memory usage of salsa20.d and actions.d. Some more small fixes
This commit is contained in:
parent
7e3f2cc611
commit
20dc2ff534
4 changed files with 152 additions and 76 deletions
|
@ -10,6 +10,7 @@ import cipher;
|
|||
public:
|
||||
|
||||
enum chunkSize = 4096;
|
||||
enum base64ChunkSize = 57;
|
||||
|
||||
enum string randomDeviceName = "/dev/random";
|
||||
|
||||
|
@ -19,43 +20,70 @@ void encrypt(string keyFileName, Cipher cipher, bool armor)
|
|||
ubyte[8] nonce;
|
||||
if (armor)
|
||||
{
|
||||
ubyte[] buf;
|
||||
ubyte[base64ChunkSize] buf;
|
||||
char[Base64.encodeLength(base64ChunkSize)] buf2;
|
||||
int counter;
|
||||
foreach (b; stdin.byChunk(chunkSize).joiner.cipherFunction(key, nonce, cipher))
|
||||
{
|
||||
buf ~= [b];
|
||||
if (buf.length == 57)
|
||||
buf[counter] = b;
|
||||
++counter;
|
||||
if (counter == 57)
|
||||
{
|
||||
stdout.writeln(Base64.encode(buf));
|
||||
buf = [];
|
||||
Base64.encode(buf, buf2[]);
|
||||
stdout.writeln(buf2);
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
if (buf !is null)
|
||||
stdout.writeln(Base64.encode(buf));
|
||||
if (counter > 0)
|
||||
{
|
||||
Base64.encode(buf[0..counter], buf2[]);
|
||||
stdout.writeln(buf2[0..Base64.encodeLength(counter)]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ubyte[chunkSize] buf;
|
||||
int counter;
|
||||
foreach (b; stdin.byChunk(chunkSize).joiner.cipherFunction(key, nonce, cipher))
|
||||
stdout.rawWrite([b]);
|
||||
{
|
||||
buf[counter] = b;
|
||||
++counter;
|
||||
if (counter == chunkSize)
|
||||
{
|
||||
stdout.rawWrite(buf);
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
if (counter > 0)
|
||||
stdout.rawWrite(buf[0..counter]);
|
||||
}
|
||||
}
|
||||
|
||||
void decrypt(string keyFileName, Cipher cipher, bool armor)
|
||||
{
|
||||
import std.range;
|
||||
auto key = loadKey(keyFileName, armor);
|
||||
ubyte[8] nonce;
|
||||
ubyte[chunkSize] buf;
|
||||
int counter;
|
||||
InputRange!ubyte r;
|
||||
if (armor)
|
||||
r = Base64.decoder(stdin.byLine).joiner.cipherFunction(key, nonce, cipher).inputRangeObject;
|
||||
else
|
||||
r = stdin.byChunk(chunkSize).joiner.cipherFunction(key, nonce, cipher).inputRangeObject;
|
||||
|
||||
foreach (b; r)
|
||||
{
|
||||
ubyte[] buf;
|
||||
foreach (b; Base64.decoder(stdin.byLine).joiner.cipherFunction(key, nonce, cipher))
|
||||
buf[counter] = b;
|
||||
++counter;
|
||||
if (counter == 4096)
|
||||
{
|
||||
stdout.rawWrite([b]);
|
||||
stdout.rawWrite(buf);
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (b; stdin.byChunk(chunkSize).joiner.cipherFunction(key, nonce, cipher))
|
||||
stdout.rawWrite([b]);
|
||||
}
|
||||
if(counter > 0)
|
||||
stdout.rawWrite(buf[0..counter]);
|
||||
}
|
||||
|
||||
void generateKey(bool armor)
|
||||
|
@ -90,12 +118,11 @@ ubyte[32] loadKey(string filename, bool armor)
|
|||
scope (exit)
|
||||
keyFile.close();
|
||||
ubyte[32] key;
|
||||
ubyte[33] temp;
|
||||
if (armor)
|
||||
{
|
||||
ubyte[] tempKey;
|
||||
foreach (line; keyFile.byLine)
|
||||
tempKey ~= Base64.decode(line);
|
||||
key = tempKey;
|
||||
import std.string : chomp;
|
||||
key = Base64.decode(keyFile.readln.chomp, temp[]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
29
source/app.d
29
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];
|
||||
|
||||
Cipher cipher = Cipher.chacha20;
|
||||
string keyFileName = "symkey.asc";
|
||||
|
@ -16,14 +16,13 @@ int main(string[] args)
|
|||
GetoptResult result;
|
||||
try
|
||||
{
|
||||
result = getopt(args,
|
||||
std.getopt.config.bundling,
|
||||
"gen-key|g", "Generate a new 256 bit key.", &actions["genKey"],
|
||||
"encrypt|e", "Encrypt a message.", &actions["encrypt"],
|
||||
"decrypt|d", "Decrypt a message.", &actions["decrypt"],
|
||||
"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);
|
||||
result = getopt(args, std.getopt.config.bundling, "gen-key|g",
|
||||
"Generate a new 256 bit key.", &actions["genKey"],
|
||||
"encrypt|e", "Encrypt a message.", &actions["encrypt"],
|
||||
"decrypt|d", "Decrypt a message.", &actions["decrypt"],
|
||||
"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);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -71,12 +70,8 @@ int main(string[] args)
|
|||
|
||||
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("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]);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
module cipher;
|
||||
|
||||
private import std.range : isInputRange, isForwardRange, ElementType, InputRange, ForwardRange, inputRangeObject;
|
||||
private import std.range : isInputRange, isForwardRange, ElementType,
|
||||
InputRange, ForwardRange, inputRangeObject;
|
||||
|
||||
private import std.string : format;
|
||||
|
||||
private import salsa20;
|
||||
|
||||
private import chacha20;
|
||||
|
||||
public:
|
||||
|
@ -15,9 +18,9 @@ enum Cipher
|
|||
}
|
||||
|
||||
template cipherFunction(R)
|
||||
if(isInputRange!R && is(ElementType!R : ubyte))
|
||||
if (isInputRange!R && is(ElementType!R : ubyte))
|
||||
{
|
||||
static if(isForwardRange!R)
|
||||
static if (isForwardRange!R)
|
||||
alias ReturnType = ForwardRange;
|
||||
else
|
||||
alias ReturnType = InputRange;
|
||||
|
@ -26,10 +29,10 @@ if(isInputRange!R && is(ElementType!R : ubyte))
|
|||
{
|
||||
final switch (cipher)
|
||||
{
|
||||
case Cipher.salsa20:
|
||||
return range.salsa20Cipher(key, nonce).inputRangeObject;
|
||||
case Cipher.chacha20:
|
||||
return range.chacha20Cipher(key, nonce).inputRangeObject;
|
||||
case Cipher.salsa20:
|
||||
return range.salsa20Cipher(key, nonce).inputRangeObject;
|
||||
case Cipher.chacha20:
|
||||
return range.chacha20Cipher(key, nonce).inputRangeObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,8 +41,8 @@ unittest
|
|||
{
|
||||
import std.array;
|
||||
|
||||
ubyte[32] key;
|
||||
ubyte[8] nonce;
|
||||
immutable ubyte[32] key;
|
||||
immutable ubyte[8] nonce;
|
||||
ubyte[] a = [1, 2, 3];
|
||||
auto b = a.cipherFunction(key, nonce, Cipher.salsa20);
|
||||
InputRange!ubyte c = a.inputRangeObject;
|
||||
|
|
109
source/salsa20.d
109
source/salsa20.d
|
@ -35,8 +35,10 @@ auto salsa20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce)
|
|||
salsaSection.popFront();
|
||||
if (salsaSection.empty)
|
||||
{
|
||||
++count;
|
||||
salsaSection = salsa20Exp(key, nonce ~ littleEndianInv(count));
|
||||
ubyte[16] n;
|
||||
n[0..8] = nonce;
|
||||
n[8..16] = littleEndianInv(0UL);
|
||||
salsaSection = salsa20Exp(key, n).dup;
|
||||
}
|
||||
range.popFront();
|
||||
}
|
||||
|
@ -48,7 +50,10 @@ auto salsa20Cipher(R)(R range, ubyte[32] key, ubyte[8] nonce)
|
|||
}
|
||||
}
|
||||
}
|
||||
return rangeResult(0UL, range, salsa20Exp(key, nonce ~ littleEndianInv(0UL)));
|
||||
ubyte[16] n;
|
||||
n[0..8] = nonce;
|
||||
n[8..16] = littleEndianInv(0UL);
|
||||
return rangeResult(0UL, range, salsa20Exp(key, n).dup);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -176,11 +181,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[] 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[] 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,
|
||||
|
@ -193,8 +198,8 @@ unittest
|
|||
0x481c2027, 0x53a8e4b5, 0x4c1f89c5, 0x3f78c9c8]);
|
||||
}
|
||||
|
||||
ubyte[] salsa20(in ubyte[] input) pure nothrow @safe
|
||||
in
|
||||
ubyte[64] salsa20(in ubyte[64] input) pure nothrow @safe
|
||||
/*in
|
||||
{
|
||||
assert(input.length == 64);
|
||||
}
|
||||
|
@ -202,7 +207,7 @@ out(result)
|
|||
{
|
||||
assert(result.length == 64);
|
||||
}
|
||||
body
|
||||
body*/
|
||||
{
|
||||
auto x00 = littleEndian(input[00..04]), x01 = littleEndian(input[04..08]),
|
||||
x02 = littleEndian(input[08..12]), x03 = littleEndian(input[12..16]),
|
||||
|
@ -227,37 +232,55 @@ body
|
|||
x08, x09, x10, x11, x12, x13, x14, x15));
|
||||
}
|
||||
|
||||
return littleEndianInv(x00 + y00) ~ littleEndianInv(x01 + y01) ~
|
||||
ubyte[64] buf;
|
||||
buf[00..04] = littleEndianInv(x00 + y00);
|
||||
buf[04..08] = littleEndianInv(x01 + y01);
|
||||
buf[08..12] = littleEndianInv(x02 + y02);
|
||||
buf[12..16] = littleEndianInv(x03 + y03);
|
||||
buf[16..20] = littleEndianInv(x04 + y04);
|
||||
buf[20..24] = littleEndianInv(x05 + y05);
|
||||
buf[24..28] = littleEndianInv(x06 + y06);
|
||||
buf[28..32] = littleEndianInv(x07 + y07);
|
||||
buf[32..36] = littleEndianInv(x08 + y08);
|
||||
buf[36..40] = littleEndianInv(x09 + y09);
|
||||
buf[40..44] = littleEndianInv(x10 + y10);
|
||||
buf[44..48] = littleEndianInv(x11 + y11);
|
||||
buf[48..52] = littleEndianInv(x12 + y12);
|
||||
buf[52..56] = littleEndianInv(x13 + y13);
|
||||
buf[56..60] = littleEndianInv(x14 + y14);
|
||||
buf[60..64] = littleEndianInv(x15 + y15);
|
||||
return buf;
|
||||
/* 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);
|
||||
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,
|
||||
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[] 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 = salsa20(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 = salsa20(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];
|
||||
|
@ -285,13 +308,24 @@ unittest
|
|||
122,127,195,185,185,204,188, 90,245, 9,183,248,226, 85,245,104]);
|
||||
}
|
||||
|
||||
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[] salsa20Exp(in ubyte[] key, in ubyte[] n) @safe pure nothrow
|
||||
in
|
||||
import std.range;
|
||||
import std.algorithm;
|
||||
import std.typetuple;
|
||||
|
||||
template expand(alias A, alias C="A[I]") {
|
||||
auto ref M(alias I)() @property { return mixin(C); }
|
||||
mixin(q{alias expand = TypeTuple!(}
|
||||
~ iota(A.length).map!q{"M!"~text(a)}().join(",")
|
||||
~ q{);});
|
||||
}
|
||||
|
||||
ubyte[64] salsa20Exp(in ubyte[32] key, in ubyte[16] n) @safe pure nothrow
|
||||
/*in
|
||||
{
|
||||
assert(key.length == 32);
|
||||
assert(n.length == 16);
|
||||
|
@ -300,24 +334,41 @@ out(result)
|
|||
{
|
||||
assert(result.length == 64);
|
||||
}
|
||||
body
|
||||
body*/
|
||||
{
|
||||
return salsa20(σ0 ~ key[0..16] ~ σ1 ~ n ~ σ2 ~ key[16..$] ~ σ3);
|
||||
ubyte[64] buf = [
|
||||
expand!sigma0,
|
||||
expand!key[0..16],
|
||||
expand!sigma1,
|
||||
expand!n,
|
||||
expand!sigma2,
|
||||
expand!key[16..$],
|
||||
expand!sigma3
|
||||
];
|
||||
/* buf[0..4] = sigma0;
|
||||
buf[4..20] = key[0..16];
|
||||
buf[20..24] = sigma1;
|
||||
buf[24..40] = n;
|
||||
buf[40..44] = sigma2;
|
||||
buf[44..60] = key[16..$];
|
||||
buf[60..64] = sigma3;*/
|
||||
return salsa20(buf);
|
||||
//return salsa20(σ0 ~ key[0..16] ~ σ1 ~ n ~ σ2 ~ key[16..$] ~ σ3);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
ubyte[] key;
|
||||
ubyte[] n;
|
||||
key.length = 32;
|
||||
n.length = 16;
|
||||
ubyte[32] key;
|
||||
ubyte[16] 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);
|
||||
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,
|
||||
|
|
Loading…
Reference in a new issue