Initiliazation
This commit is contained in:
416
vv1/agent/angel/utils/cryptography/bitmanip.d
Normal file
416
vv1/agent/angel/utils/cryptography/bitmanip.d
Normal file
@@ -0,0 +1,416 @@
|
||||
module angel.utils.cryptography.bitmanip;
|
||||
|
||||
import std.traits;
|
||||
///
|
||||
/// This module contains several methods to convert integer types into byte arrays
|
||||
/// and vice versa.
|
||||
///
|
||||
///
|
||||
|
||||
alias rotateLeft rol;
|
||||
alias rotateRight ror;
|
||||
|
||||
/// rot shift to the left
|
||||
/// Params:
|
||||
/// x = integer to shift
|
||||
/// shiftAmount = number of bits to shift
|
||||
@safe
|
||||
@nogc
|
||||
T rotateLeft(T)(T x, uint shiftAmount) pure nothrow
|
||||
{
|
||||
enum nbits = T.sizeof*8;
|
||||
//shiftAmount %= nbits;
|
||||
return cast(T)(x << shiftAmount) | (x >>> (nbits-shiftAmount));
|
||||
}
|
||||
|
||||
/// test rotateLeft
|
||||
unittest {
|
||||
ubyte b0 = 0b10000001;
|
||||
ubyte b1 = 0b00000011;
|
||||
ubyte b2 = 0b00000110;
|
||||
ubyte b7 = 0b11000000;
|
||||
|
||||
assert(rotateLeft(b0,0) == b0);
|
||||
assert(rotateLeft(b0,1) == b1);
|
||||
assert(rotateLeft(b0,2) == b2);
|
||||
assert(rotateLeft(b0,7) == b7);
|
||||
assert(rotateLeft(b0,8) == b0);
|
||||
}
|
||||
|
||||
/// rot shift to the right
|
||||
/// Params:
|
||||
/// x = integer to shift
|
||||
/// shiftAmount = number of bits to shift
|
||||
@safe
|
||||
@nogc
|
||||
T rotateRight(T)(T x, uint shiftAmount) pure nothrow
|
||||
{
|
||||
enum nbits = T.sizeof*8;
|
||||
//shiftAmount %= nbits;
|
||||
return cast(T)((x >>> shiftAmount) | (x << (nbits-shiftAmount)));
|
||||
}
|
||||
|
||||
/// test rotateRight
|
||||
unittest {
|
||||
ubyte b0 = 0b00000101;
|
||||
ubyte b1 = 0b10000010;
|
||||
ubyte b2 = 0b01000001;
|
||||
ubyte b7 = 0b00001010;
|
||||
|
||||
assert(rotateRight(b0,0) == b0);
|
||||
assert(rotateRight(b0,1) == b1);
|
||||
assert(rotateRight(b0,2) == b2);
|
||||
assert(rotateRight(b0,7) == b7);
|
||||
assert(rotateRight(b0,8) == b0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Converts big endian bytes to integral of type T
|
||||
Params: bs = the big endian bytes
|
||||
Returns: integral of type T
|
||||
*/
|
||||
@safe @nogc
|
||||
T fromBigEndian(T)(in ubyte[] bs) if (isIntegral!T)
|
||||
in {
|
||||
assert(bs.length >= T.sizeof, "input buffer too short");
|
||||
}
|
||||
body {
|
||||
version(BigEndian) {
|
||||
// data is already in memory as we want
|
||||
return (cast(const T[])bs)[0];
|
||||
}else {
|
||||
Unqual!T n = 0;
|
||||
static if (T.sizeof >= short.sizeof) {
|
||||
n |= bs[0];
|
||||
n <<= 8;
|
||||
n |= bs[1];
|
||||
}
|
||||
static if (T.sizeof >= int.sizeof) {
|
||||
n <<= 8;
|
||||
n |= bs[2];
|
||||
n <<= 8;
|
||||
n |= bs[3];
|
||||
}
|
||||
static if (T.sizeof == long.sizeof) {
|
||||
n <<= 8;
|
||||
n |= bs[4];
|
||||
n <<= 8;
|
||||
n |= bs[5];
|
||||
n <<= 8;
|
||||
n |= bs[6];
|
||||
n <<= 8;
|
||||
n |= bs[7];
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Converts little endian bytes to integral of type T
|
||||
Params: bs = the little endian bytes
|
||||
Returns: integral of type T
|
||||
*/
|
||||
@safe @nogc
|
||||
T fromLittleEndian(T)(in ubyte[] bs) if (isIntegral!T)
|
||||
in {
|
||||
assert(bs.length >= T.sizeof, "input buffer too short");
|
||||
}
|
||||
body {
|
||||
version(LittleEndian) {
|
||||
// data is already in memory as we want
|
||||
return (cast(const T[])bs)[0];
|
||||
}else {
|
||||
Unqual!T n = 0;
|
||||
static if (T.sizeof >= short.sizeof) {
|
||||
n |= bs[0];
|
||||
n |= cast(T)bs[1] << 8;
|
||||
}
|
||||
static if (T.sizeof >= int.sizeof) {
|
||||
n |= cast(T)bs[2] << 16;
|
||||
n |= cast(T)bs[3] << 24;
|
||||
}
|
||||
static if (T.sizeof == long.sizeof) {
|
||||
n |= cast(T)bs[4] << 32;
|
||||
n |= cast(T)bs[5] << 40;
|
||||
n |= cast(T)bs[6] << 48;
|
||||
n |= cast(T)bs[7] << 56;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Converts big endian bytes to integrals of type T
|
||||
size of bs has to match the size in bytes of output
|
||||
Params:
|
||||
bs = the big endian bytes
|
||||
output = where the T's get written to
|
||||
*/
|
||||
@safe @nogc
|
||||
void fromBigEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
|
||||
in {
|
||||
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
|
||||
}
|
||||
body {
|
||||
version(BigEndian) {
|
||||
// short cut on big endian systems
|
||||
const T[] casted = cast(const T[]) bs;
|
||||
output[] = casted[];
|
||||
} else {
|
||||
// for little endian systems
|
||||
enum s = T.sizeof;
|
||||
foreach (i; 0 .. output.length)
|
||||
{
|
||||
output[i] = fromBigEndian!T(bs[s*i .. s*i+s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Converts little endian bytes to integrals of type T
|
||||
size of bs has to match the size in bytes of output
|
||||
Params:
|
||||
bs = the little endian bytes
|
||||
output = where the T's get written to
|
||||
*/
|
||||
@safe @nogc
|
||||
void fromLittleEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
|
||||
in {
|
||||
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
|
||||
}
|
||||
body {
|
||||
version(LittleEndian) {
|
||||
// short cut on little endian systems
|
||||
const T[] casted = cast(const T[]) bs;
|
||||
output[] = casted[];
|
||||
} else {
|
||||
// for big endian systems
|
||||
enum s = T.sizeof;
|
||||
foreach (i; 0 .. output.length)
|
||||
{
|
||||
output[i] = fromLittleEndian!T(bs[s*i .. s*i+s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
convert a integral type T into an array of bytes.
|
||||
Params:
|
||||
n = the number
|
||||
output = the buffer to write the bytes to
|
||||
*/
|
||||
@safe @nogc
|
||||
void toBigEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
|
||||
in {
|
||||
assert(output.length >= T.sizeof, "output buffer too small");
|
||||
}
|
||||
body {
|
||||
Unqual!T n = val;
|
||||
uint off = 0;
|
||||
|
||||
static if(T.sizeof == long.sizeof) {
|
||||
output[off] = cast (ubyte) (n >>> 56);
|
||||
++off;
|
||||
output[off] = cast (ubyte) (n >>> 48);
|
||||
++off;
|
||||
output[off] = cast (ubyte) (n >>> 40);
|
||||
++off;
|
||||
output[off] = cast (ubyte) (n >>> 32);
|
||||
++off;
|
||||
}
|
||||
static if(T.sizeof >= int.sizeof) {
|
||||
output[off] = cast (ubyte) (n >>> 24);
|
||||
++off;
|
||||
output[off] = cast (ubyte) (n >>> 16);
|
||||
++off;
|
||||
}
|
||||
static if(T.sizeof >= short.sizeof) {
|
||||
output[off] = cast (ubyte) (n >>> 8);
|
||||
++off;
|
||||
}
|
||||
output[off] = cast (ubyte) (n);
|
||||
}
|
||||
|
||||
/**
|
||||
convert a integral type T into an array of bytes.
|
||||
Params:
|
||||
n = the number
|
||||
output = the buffer to write the bytes to
|
||||
*/
|
||||
@safe @nogc
|
||||
void toLittleEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
|
||||
in {
|
||||
assert(output.length >= T.sizeof, "output buffer too small");
|
||||
}
|
||||
body {
|
||||
Unqual!T n = val;
|
||||
output[0] = cast (ubyte) (n);
|
||||
n >>>= 8;
|
||||
static if(T.sizeof >= short.sizeof) {
|
||||
output[1] = cast (ubyte) (n);
|
||||
n >>>= 8;
|
||||
}
|
||||
static if(T.sizeof >= int.sizeof) {
|
||||
output[2] = cast (ubyte) (n);
|
||||
n >>>= 8;
|
||||
output[3] = cast (ubyte) (n);
|
||||
n >>>= 8;
|
||||
}
|
||||
static if(T.sizeof == long.sizeof) {
|
||||
output[4] = cast (ubyte) (n);
|
||||
n >>>= 8;
|
||||
output[5] = cast (ubyte) (n);
|
||||
n >>>= 8;
|
||||
output[6] = cast (ubyte) (n);
|
||||
n >>>= 8;
|
||||
output[7] = cast (ubyte) (n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
convert a integral type T[] into an array of bytes.
|
||||
Params:
|
||||
ns = the numbers
|
||||
output = the buffer to write the bytes to
|
||||
*/
|
||||
@safe @nogc
|
||||
void toBigEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
|
||||
in {
|
||||
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
|
||||
}
|
||||
body {
|
||||
version(BigEndian) {
|
||||
// shortcut on BigEndian systems
|
||||
const ubyte[] casted = cast(const ubyte []) ns;
|
||||
output[] = casted[];
|
||||
}else{
|
||||
foreach(i, const T n; ns) {
|
||||
toBigEndian!T(n, output[T.sizeof * i .. $]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
convert a integral type T[] into an array of bytes.
|
||||
Params:
|
||||
ns the numbers
|
||||
output the buffer to write the bytes to
|
||||
*/
|
||||
@safe @nogc
|
||||
void toLittleEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
|
||||
in {
|
||||
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
|
||||
}
|
||||
body {
|
||||
version(LittleEndian) {
|
||||
// shortcut on LittleEndian systems
|
||||
const ubyte[] casted = cast(const ubyte []) ns;
|
||||
output[] = casted[];
|
||||
}else{
|
||||
foreach(i, const T n; ns) {
|
||||
toLittleEndian!T(n, output[T.sizeof * i .. $]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ubyte[T.sizeof] toBigEndian(T)(in T n) pure nothrow @nogc
|
||||
if(isIntegral!T)
|
||||
{
|
||||
ubyte[T.sizeof] bs;
|
||||
toBigEndian!T(n, bs);
|
||||
return bs;
|
||||
}
|
||||
|
||||
ubyte[] toBigEndian(T)(in T[] ns) if(isIntegral!T)
|
||||
{
|
||||
ubyte[] bs = new ubyte[T.sizeof * ns.length];
|
||||
toBigEndian!T(ns, bs);
|
||||
return bs;
|
||||
}
|
||||
|
||||
|
||||
ubyte[T.sizeof] toLittleEndian(T)(in T n) pure nothrow @nogc
|
||||
if(isIntegral!T)
|
||||
{
|
||||
ubyte[T.sizeof] bs;
|
||||
toLittleEndian!T(n, bs);
|
||||
return bs;
|
||||
}
|
||||
|
||||
|
||||
ubyte[] toLittleEndian(T)(in T[] ns) if(isIntegral!T)
|
||||
{
|
||||
ubyte[] bs = new ubyte[T.sizeof * ns.length];
|
||||
toLittleEndian!T(ns, bs);
|
||||
return bs;
|
||||
}
|
||||
|
||||
unittest {
|
||||
|
||||
// int
|
||||
assert(toBigEndian(0x01020304) == [0x01,0x02,0x03,0x04], "intToBigEndian failed");
|
||||
assert(toLittleEndian(0x01020304) == [0x04,0x03,0x02,0x01], "intToLittleEndian failed");
|
||||
|
||||
|
||||
// long
|
||||
assert(toBigEndian(0x0102030405060708L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToBigEndian failed");
|
||||
assert(toLittleEndian(0x0807060504030201L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToLittleEndian failed");
|
||||
|
||||
// bigEndian to short, int, long
|
||||
assert(fromBigEndian!ushort([0x01,0x02]) == 0x0102u);
|
||||
assert(fromBigEndian!uint([0x01,0x02,0x03,0x04]) == 0x01020304u);
|
||||
assert(fromBigEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0102030405060708UL);
|
||||
|
||||
// littleEndian to short, int, long
|
||||
assert(fromLittleEndian!ushort([0x02,0x01]) == 0x0102u);
|
||||
assert(fromLittleEndian!uint([0x04,0x03,0x02,0x01]) == 0x01020304u);
|
||||
assert(fromLittleEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0807060504030201UL);
|
||||
|
||||
// bigEndian: convert multiple ints
|
||||
uint[] output = new uint[2];
|
||||
immutable ubyte[] input = [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08];
|
||||
fromBigEndian(input, output);
|
||||
assert(output == [0x01020304u, 0x05060708u], "fromBigEndian(ubyte[] input, int[] output) failed");
|
||||
|
||||
// littleEndian: convert multiple ints
|
||||
output = new uint[2];
|
||||
fromLittleEndian(input, output);
|
||||
assert(output == [0x04030201u, 0x08070605u], "fromLittleEndian(ubyte[] input, int[] output) failed");
|
||||
|
||||
|
||||
immutable int i = 0xf1f2f3f4;
|
||||
int iResult;
|
||||
ubyte[] buf;
|
||||
|
||||
// int to bigEndian
|
||||
buf = new ubyte[4];
|
||||
toBigEndian!int(i, buf);
|
||||
iResult = fromBigEndian!int(buf);
|
||||
assert(i == iResult);
|
||||
|
||||
// int to littleEndian
|
||||
buf = new ubyte[4];
|
||||
toLittleEndian!int(i, buf);
|
||||
iResult = fromLittleEndian!int(buf);
|
||||
assert(i == iResult);
|
||||
|
||||
|
||||
|
||||
immutable long l = 0xf1f2f3f4f5f6f7f8;
|
||||
long lResult;
|
||||
|
||||
// long to bigEndian
|
||||
buf = new ubyte[8];
|
||||
toBigEndian!long(l, buf);
|
||||
lResult = fromBigEndian!long(buf);
|
||||
assert(l == lResult);
|
||||
|
||||
// int to littleEndian
|
||||
buf = new ubyte[8];
|
||||
toLittleEndian!long(l, buf);
|
||||
lResult = fromLittleEndian!long(buf);
|
||||
assert(l == lResult);
|
||||
}
|
||||
191
vv1/agent/angel/utils/cryptography/blockcipher.d
Normal file
191
vv1/agent/angel/utils/cryptography/blockcipher.d
Normal file
@@ -0,0 +1,191 @@
|
||||
module angel.utils.cryptography.blockcipher;
|
||||
|
||||
/// Use this to check if type is a block cipher.
|
||||
@safe
|
||||
template isBlockCipher(T)
|
||||
{
|
||||
enum bool isBlockCipher =
|
||||
is(T == struct) &&
|
||||
is(typeof(
|
||||
{
|
||||
ubyte[0] block;
|
||||
T bc = T.init; // Can define
|
||||
string name = T.name;
|
||||
uint blockSize = T.blockSize;
|
||||
bc.start(cast(const ubyte[]) block, cast(const ubyte[]) block); // init with secret key and iv
|
||||
uint len = bc.encrypt(cast (const ubyte[]) block, block);
|
||||
bc.reset();
|
||||
}));
|
||||
}
|
||||
|
||||
/// OOP API for block ciphers
|
||||
@safe
|
||||
public interface IBlockCipher {
|
||||
|
||||
|
||||
@safe public:
|
||||
|
||||
/**
|
||||
* Initialize the cipher.
|
||||
*
|
||||
* Params:
|
||||
* forEncryption = if true the cipher is initialised for
|
||||
* encryption, if false for decryption.
|
||||
* userKey = A secret key.
|
||||
* iv = A nonce.
|
||||
*/
|
||||
void start(in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc;
|
||||
|
||||
/**
|
||||
* Return the name of the algorithm the cipher implements.
|
||||
*
|
||||
* Returns: the name of the algorithm the cipher implements.
|
||||
*/
|
||||
@property
|
||||
string name() pure nothrow;
|
||||
|
||||
/**
|
||||
* Return the block size for this cipher (in bytes).
|
||||
*
|
||||
* Returns: the block size for this cipher in bytes.
|
||||
*/
|
||||
@property
|
||||
uint blockSize() pure nothrow @nogc;
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* Params:
|
||||
* input = the slice containing the input data.
|
||||
* output = the slice the output data will be copied into.
|
||||
* Throws: IllegalStateException if the cipher isn't initialised.
|
||||
* Returns: the number of bytes processed and produced.
|
||||
*/
|
||||
@nogc
|
||||
uint encrypt(in ubyte[] input, ubyte[] output) nothrow;
|
||||
|
||||
@nogc
|
||||
uint decrypt(in ubyte[] input, ubyte[] output) nothrow;
|
||||
|
||||
/**
|
||||
* Reset the cipher. After resetting the cipher is in the same state
|
||||
* as it was after the last init (if there was one).
|
||||
*/
|
||||
@nogc
|
||||
void reset() nothrow;
|
||||
}
|
||||
|
||||
/// Wraps block ciphers into the OOP API
|
||||
@safe
|
||||
public class BlockCipherWrapper(T) if(isBlockCipher!T): IBlockCipher {
|
||||
|
||||
private T cipher;
|
||||
|
||||
@safe public:
|
||||
|
||||
/**
|
||||
* Initialize the cipher.
|
||||
*
|
||||
* Params:
|
||||
* forEncryption = if true the cipher is initialised for
|
||||
* encryption, if false for decryption.
|
||||
* params = the key and other data required by the cipher.
|
||||
*
|
||||
* Throws: IllegalArgumentException if the params argument is
|
||||
* inappropriate.
|
||||
*/
|
||||
void start(in ubyte[] key, in ubyte[] iv = null) nothrow {
|
||||
cipher.start(key, iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the algorithm the cipher implements.
|
||||
*
|
||||
* Returns: the name of the algorithm the cipher implements.
|
||||
*/
|
||||
@property
|
||||
string name() pure nothrow {
|
||||
return cipher.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the block size for this cipher (in bytes).
|
||||
*
|
||||
* Returns: the block size for this cipher in bytes.
|
||||
*/
|
||||
@property
|
||||
uint blockSize() pure nothrow @nogc {
|
||||
return T.blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* Params:
|
||||
* input = the slice containing the input data.
|
||||
* output = the slice the output data will be copied into.
|
||||
* Throws: IllegalStateException if the cipher isn't initialised.
|
||||
* Returns: the number of bytes processed and produced.
|
||||
*/
|
||||
uint encrypt(in ubyte[] input, ubyte[] output) nothrow @nogc {
|
||||
return cipher.encrypt(input, output);
|
||||
}
|
||||
|
||||
uint decrypt(in ubyte[] input, ubyte[] output) nothrow @nogc {
|
||||
return cipher.decrypt(input, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cipher. After resetting the cipher is in the same state
|
||||
* as it was after the last init (if there was one).
|
||||
*/
|
||||
void reset() nothrow @nogc {
|
||||
cipher.reset();
|
||||
}
|
||||
}
|
||||
|
||||
version(unittest) {
|
||||
|
||||
// unittest helper functions
|
||||
|
||||
import std.format: format;
|
||||
|
||||
/// Runs decryption and encryption using BlockCipher bc with given keys, plaintexts, and ciphertexts
|
||||
///
|
||||
/// Params:
|
||||
/// keys = The encryption/decryption keys.
|
||||
/// plaintexts = Plaintexts.
|
||||
/// cipherTexts = Corresponding ciphertexts.
|
||||
/// ivs = Initialization vectors.
|
||||
///
|
||||
@safe
|
||||
public void blockCipherTest(IBlockCipher bc, string[] keys, string[] plaintexts, string[] cipherTexts, string[] ivs = null) {
|
||||
|
||||
foreach (uint i, string test_key; keys)
|
||||
{
|
||||
ubyte[] buffer = new ubyte[bc.blockSize];
|
||||
|
||||
|
||||
const ubyte[] key = cast(const ubyte[]) test_key;
|
||||
const (ubyte)[] iv = null;
|
||||
if(ivs !is null) {
|
||||
iv = cast(const (ubyte)[]) ivs[i];
|
||||
}
|
||||
|
||||
// Encryption
|
||||
bc.start(key, iv);
|
||||
bc.encrypt(cast(const ubyte[]) plaintexts[i], buffer);
|
||||
|
||||
assert(buffer == cipherTexts[i],
|
||||
format("%s failed to encrypt.", bc.name));
|
||||
|
||||
// Decryption
|
||||
bc.start(key, iv);
|
||||
bc.decrypt(cast(const ubyte[]) cipherTexts[i], buffer);
|
||||
assert(buffer == plaintexts[i],
|
||||
format("%s failed to decrypt.", bc.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
148
vv1/agent/angel/utils/cryptography/cryptography.d
Normal file
148
vv1/agent/angel/utils/cryptography/cryptography.d
Normal file
@@ -0,0 +1,148 @@
|
||||
module angel.utils.cryptography.cryptography;
|
||||
|
||||
// Internal imports
|
||||
import angel.utils.logging;
|
||||
import angel.utils.cryptography.serpent;
|
||||
import angel.utils.cryptography.threefish;
|
||||
import angel.utils.cryptography.curve25519;
|
||||
// External imports
|
||||
import std.stdio;
|
||||
import std.random;
|
||||
import std.format;
|
||||
|
||||
class Cryptography {
|
||||
public {
|
||||
struct KeyPair {
|
||||
ubyte[32] clientSecretKey;
|
||||
ubyte[32] sharedSecret;
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyPair derive_25519(ubyte[] pk) {
|
||||
ubyte[32] sk; // generate client secret key
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
sk[i] = cast(ubyte)(uniform(0, 256));
|
||||
}
|
||||
|
||||
Logger.log(LogLevel.Debug, "Generated client sk");
|
||||
|
||||
ubyte[32] ss = curve25519_scalarmult(sk, pk); // derive shared secret out of pk and sk
|
||||
|
||||
Logger.log(LogLevel.Debug, format("Derived shared secret: %s", ss));
|
||||
|
||||
return KeyPair(sk, ss);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Freefishy {
|
||||
private Threefish512 cipher;
|
||||
|
||||
this(ulong[8] key, ulong[2] tweak) {
|
||||
cipher = new Threefish512();
|
||||
cipher.setup(key, tweak);
|
||||
}
|
||||
|
||||
public ubyte[] encrypt(ubyte[] payload) {
|
||||
ulong[8] plain = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
// Pack the ubyte[] into ulong[] by 8 bytes per ulong
|
||||
foreach (i, c; payload) {
|
||||
plain[i / 8] |= cast(ulong) c << ((i % 8) * 8);
|
||||
}
|
||||
|
||||
// Encrypt the ulong array
|
||||
ulong[] encrypted_three = cipher.crypt(plain);
|
||||
|
||||
// Convert the encrypted ulong[] back to ubyte[]
|
||||
ubyte[] encrypted_bytes;
|
||||
foreach (enc_val; encrypted_three) {
|
||||
foreach (i; 0..8) {
|
||||
encrypted_bytes ~= cast(ubyte)((enc_val >> (i * 8)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.log(LogLevel.Debug, format("Threefish Encrypted: %s", encrypted_bytes));
|
||||
return encrypted_bytes;
|
||||
}
|
||||
|
||||
public ubyte[] decrypt(ubyte[] payload) {
|
||||
ulong[8] decrypted_buffer;
|
||||
|
||||
// Convert the ubyte[] payload back into ulong[] (8 bytes per ulong)
|
||||
ulong[8] cipher_payload;
|
||||
foreach (i, val; payload) {
|
||||
cipher_payload[i / 8] |= cast(ulong) val << ((i % 8) * 8);
|
||||
}
|
||||
|
||||
// Decrypt the ulong[] array
|
||||
cipher.decrypt(cipher_payload.ptr, decrypted_buffer.ptr);
|
||||
|
||||
// Convert the decrypted ulong[] back to ubyte[]
|
||||
ubyte[] decrypted_bytes;
|
||||
foreach (dec_val; decrypted_buffer) {
|
||||
foreach (i; 0..8) {
|
||||
decrypted_bytes ~= cast(ubyte)((dec_val >> (i * 8)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.log(LogLevel.Debug, format("Threefish Decrypted: %s", decrypted_bytes));
|
||||
return decrypted_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Serpi {
|
||||
private Serpent serp;
|
||||
|
||||
this(Cryptography.KeyPair keypair) {
|
||||
auto key = cast(ubyte[]) keypair.sharedSecret.dup;
|
||||
serp.start(key);
|
||||
}
|
||||
|
||||
public ubyte[] encrypt(ubyte[] payload) {
|
||||
ubyte padding = cast(ubyte)(16 - (payload.length % 16));
|
||||
ubyte[] paddingBytes = new ubyte[padding];
|
||||
foreach (i; 0 .. padding) {
|
||||
paddingBytes[i] = padding;
|
||||
}
|
||||
ubyte[] input = payload ~ paddingBytes;
|
||||
ubyte[] output = new ubyte[input.length];
|
||||
|
||||
serp.encrypt(input, output);
|
||||
Logger.log(LogLevel.Debug, format("Serpent Encrypted data: %s", output));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public ubyte[] decrypt(ubyte[] payload) {
|
||||
ubyte[] output = new ubyte[payload.length];
|
||||
serp.decrypt(payload, output);
|
||||
|
||||
if (output.length == 0) {
|
||||
throw new Exception("Decryption failed: Output length is zero.");
|
||||
}
|
||||
|
||||
ubyte padding = output[$ - 1];
|
||||
|
||||
if (padding > 16 || padding == 0) {
|
||||
throw new Exception("Invalid padding detected");
|
||||
}
|
||||
|
||||
foreach (i; output.length - padding .. output.length) {
|
||||
if (output[i] != padding) {
|
||||
throw new Exception("Padding validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
ubyte[] unpadded = output[0 .. output.length - padding];
|
||||
Logger.log(LogLevel.Debug, format("Serpent Decrypted data: %s", unpadded));
|
||||
|
||||
return unpadded;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
serp.reset();
|
||||
}
|
||||
}
|
||||
136
vv1/agent/angel/utils/cryptography/curve25519.d
Normal file
136
vv1/agent/angel/utils/cryptography/curve25519.d
Normal file
@@ -0,0 +1,136 @@
|
||||
module angel.utils.cryptography.curve25519;
|
||||
|
||||
import angel.utils.cryptography.fieldelem;
|
||||
import angel.utils.cryptography.utils;
|
||||
|
||||
public enum ubyte[32] publicBasePoint = cast(immutable (ubyte[32]) ) x"0900000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
@safe nothrow @nogc:
|
||||
|
||||
///
|
||||
///
|
||||
/// Params:
|
||||
/// secret = Your secret key, the 'exponent'.
|
||||
/// p = Receivers public key. Default base point = 9.
|
||||
///
|
||||
/// Returns: p^secret.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// ubyte[32] publicKey = curve25519_scalarmult(secretKey);
|
||||
///
|
||||
/// ubyte[32] sharedKey = curve25519_scalarmult(mySecretKey, herPublicKey);
|
||||
///
|
||||
ubyte[32] curve25519_scalarmult(
|
||||
in ubyte[] secret,
|
||||
in ubyte[] p = cast(const ubyte[32]) publicBasePoint) @safe nothrow @nogc
|
||||
in {
|
||||
assert(secret.length == 32, "Secret key must be 32 bytes long.");
|
||||
assert(p.length == 32, "Public key must be 32 bytes long.");
|
||||
} body {
|
||||
ubyte[32] sec = secret;
|
||||
scope(exit) {
|
||||
wipe(sec);
|
||||
}
|
||||
|
||||
ubyte[32] pub = p;
|
||||
|
||||
return curve25519_scalarmult(sec, pub);
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
/// Params:
|
||||
/// secret = Your secret key, the 'exponent'.
|
||||
/// p = Receivers public key. Default base point = 9.
|
||||
///
|
||||
/// Returns: p^secret.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// ubyte[32] publicKey = curve25519_scalarmult(secretKey);
|
||||
///
|
||||
/// ubyte[32] sharedKey = curve25519_scalarmult(mySecretKey, herPublicKey);
|
||||
///
|
||||
ubyte[32] curve25519_scalarmult(in ref ubyte[32] secret, in ref ubyte[32] p = publicBasePoint) @safe nothrow @nogc
|
||||
{
|
||||
ubyte[32] e = secret;
|
||||
scope(exit) {
|
||||
wipe(e);
|
||||
}
|
||||
clamp(e);
|
||||
|
||||
fe x1, x2, x3, z2, z3, tmp0, tmp1;
|
||||
scope(exit) {
|
||||
wipe(x1);
|
||||
wipe(x2);
|
||||
wipe(x3);
|
||||
wipe(z2);
|
||||
wipe(z3);
|
||||
wipe(tmp0);
|
||||
wipe(tmp1);
|
||||
}
|
||||
|
||||
x1 = fe.fromBytes(p);
|
||||
x2 = fe.one;
|
||||
z2 = fe.zero;
|
||||
x3 = x1;
|
||||
z3 = fe.one;
|
||||
|
||||
uint swap = 0, b;
|
||||
for (int pos = 254; pos >= 0;--pos) {
|
||||
b = e[pos / 8] >> (pos & 7);
|
||||
b &= 1;
|
||||
swap ^= b;
|
||||
fe_cswap(x2,x3,swap);
|
||||
fe_cswap(z2,z3,swap);
|
||||
swap = b;
|
||||
|
||||
tmp0 = x3 - z3;
|
||||
|
||||
tmp1 = x2 - z2;
|
||||
x2 += z2;
|
||||
z2 = x3 + z3;
|
||||
|
||||
z3 = tmp0 * x2;
|
||||
|
||||
z2 *= tmp1;
|
||||
tmp0 = tmp1.sq;
|
||||
tmp1 = x2.sq;
|
||||
x3 = z2 + z3;
|
||||
|
||||
z2 = z3 - z2;
|
||||
x2 = tmp0 * tmp1;
|
||||
|
||||
tmp1 -= tmp0;
|
||||
|
||||
z2 = z2.sq;
|
||||
|
||||
z3 = fe_mul121666(tmp1);
|
||||
|
||||
x3 = x3.sq;
|
||||
|
||||
tmp0 += z3;
|
||||
z3 = x1 * z2;
|
||||
|
||||
z2 = tmp0 * tmp1;
|
||||
}
|
||||
fe_cswap(x2,x3,swap);
|
||||
fe_cswap(z2,z3,swap);
|
||||
|
||||
x2 *= z2.inverse;
|
||||
return x2.toBytes;
|
||||
}
|
||||
|
||||
/// Transforms 32 random bytes into a valid secret key.
|
||||
///
|
||||
/// Params:
|
||||
/// sk = 32 byte secret key.
|
||||
package void clamp(ubyte[] sk) pure
|
||||
in {
|
||||
assert(sk.length == 32);
|
||||
} body {
|
||||
sk[0] &= 248;
|
||||
sk[31] &= 63;
|
||||
sk[31] |= 64;
|
||||
}
|
||||
1239
vv1/agent/angel/utils/cryptography/fieldelem.d
Normal file
1239
vv1/agent/angel/utils/cryptography/fieldelem.d
Normal file
File diff suppressed because it is too large
Load Diff
1030
vv1/agent/angel/utils/cryptography/serpent.d
Normal file
1030
vv1/agent/angel/utils/cryptography/serpent.d
Normal file
File diff suppressed because it is too large
Load Diff
261
vv1/agent/angel/utils/cryptography/threefish.d
Normal file
261
vv1/agent/angel/utils/cryptography/threefish.d
Normal file
@@ -0,0 +1,261 @@
|
||||
module angel.utils.cryptography.threefish;
|
||||
|
||||
import std.random : Random, unpredictableSeed, uniform;
|
||||
|
||||
// memcpy
|
||||
extern(C) nothrow @nogc void* memcpy(void* dst, const void* src, size_t n);
|
||||
|
||||
class Threefish512
|
||||
{
|
||||
private {
|
||||
// Размер блока шифра
|
||||
enum blockSize = 64;
|
||||
// Количество 64-битных слов в ключе (и в блоке)
|
||||
enum Nw = 8;
|
||||
// Количество раундов
|
||||
enum Nr = 72;
|
||||
// Количество раундов (за вычетом последнего)
|
||||
enum Ns = Nr / 4;
|
||||
// Функция перестановки
|
||||
uint[8] p = [2, 1, 4, 7, 6, 5, 0, 3];
|
||||
uint[8] p_1 = [6, 1, 0, 7, 2, 5, 4, 3];
|
||||
|
||||
// Функция смешивания и перестановки
|
||||
uint[4][8] r = [
|
||||
[46, 36, 19, 37],
|
||||
[33, 27, 14, 42],
|
||||
[17, 49, 36, 39],
|
||||
[44, 9 , 54, 56],
|
||||
[39, 30, 34, 24],
|
||||
[13, 50, 10, 17],
|
||||
[25, 29, 39, 43],
|
||||
[8 , 35, 56, 22]
|
||||
];
|
||||
|
||||
// Твик-значение (свободный параметр алгоритма)
|
||||
ulong[3] t;
|
||||
// Раундовые ключи
|
||||
ulong[8][Ns + 1] subKeys;
|
||||
|
||||
auto _mix(ref ulong[2] x, ulong r, ref ulong[2] y)
|
||||
{
|
||||
y[0] = x[0] + x[1];
|
||||
y[1] = (x[1] << r) | (x[1] >> (64 - r));
|
||||
y[1] ^= y[0];
|
||||
}
|
||||
|
||||
auto _demix(ref ulong[2] y, ulong r, ref ulong[2] x)
|
||||
{
|
||||
y[1] ^= y[0];
|
||||
x[1] = (y[1] << (64 - r)) | (y[1] >> r);
|
||||
x[0] = y[0] - x[1];
|
||||
}
|
||||
|
||||
alias _mod8 = (ulong a) => a & 7UL;
|
||||
}
|
||||
|
||||
/// Шифрование блока
|
||||
/// plain - указатель на блок для шифрования, c - массив-приемник результата
|
||||
void crypt(ulong* plainData, ulong* c) @system
|
||||
{
|
||||
ulong[8] f;
|
||||
ulong[8] e;
|
||||
ulong[2] y;
|
||||
ulong[2] x;
|
||||
ulong[8] v;
|
||||
uint i;
|
||||
|
||||
memcpy (&v[0], plainData, 64);
|
||||
|
||||
for (uint round = 0; round < Nr; round++)
|
||||
{
|
||||
if (round % 4 == 0)
|
||||
{
|
||||
uint s = round >> 2;
|
||||
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
e[i] = v[i] + subKeys[s][i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
e[i] = v[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < Nw / 2; i++)
|
||||
{
|
||||
x[0] = e[i * 2];
|
||||
x[1] = e[i * 2 + 1];
|
||||
|
||||
_mix(x, r[cast(uint) _mod8(round)][i], y);
|
||||
|
||||
f[i * 2] = y[0];
|
||||
f[i * 2 + 1] = y[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
v[i] = f[p[i]];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
c[i] = v[i] + subKeys[Ns][i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Шифрование блока (безопасная версия)
|
||||
/// plain - массив с данными блока
|
||||
auto crypt(ulong[8] plainData)
|
||||
{
|
||||
ulong[8] c = 0;
|
||||
crypt(plainData.ptr, c.ptr);
|
||||
return c;
|
||||
}
|
||||
|
||||
/// Дешифрование блока
|
||||
/// plain - указатель на блок для дешифрования, c - массив-приемник результата
|
||||
void decrypt(ulong* plainData, ulong* c) @system
|
||||
{
|
||||
ulong[8] f;
|
||||
ulong[8] e;
|
||||
ulong[2] y;
|
||||
ulong[2] x;
|
||||
ulong[8] v;
|
||||
uint i;
|
||||
|
||||
memcpy(&v[0], plainData, 64);
|
||||
|
||||
for (uint round = Nr; round > 0; round--)
|
||||
{
|
||||
if (round % 4 == 0)
|
||||
{
|
||||
uint s = round >> 2;
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
f[i] = v[i] - subKeys[s][i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
f[i] = v[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
e[i] = f[p_1[i]];
|
||||
}
|
||||
|
||||
for (i = 0; i < Nw / 2; i++)
|
||||
{
|
||||
y[0] = e[i * 2];
|
||||
y[1] = e[i * 2 + 1];
|
||||
|
||||
_demix(y, r[cast(uint) _mod8(round - 1)][i], x);
|
||||
|
||||
v[i * 2] = x[0];
|
||||
v[i * 2 + 1] = x[1];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
c[i] = v[i] - subKeys[0][i];
|
||||
}
|
||||
}
|
||||
|
||||
/// Дешифрование блока (безопасная версия)
|
||||
/// plain - массив с данными блока
|
||||
auto decrypt(ulong[8] plain)
|
||||
{
|
||||
ulong[8] c = 0;
|
||||
decrypt(plain.ptr, c.ptr);
|
||||
return c;
|
||||
}
|
||||
|
||||
/// Подготовка раундовых ключей
|
||||
/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
|
||||
void setup(ulong* keyData, ulong* tweakData) @system
|
||||
{
|
||||
uint i;
|
||||
ulong[8] K;
|
||||
ulong[2] T;
|
||||
ulong[9] key;
|
||||
|
||||
// C240 constant
|
||||
ulong kNw = 0x1BD11BDAA9FC1A22;
|
||||
|
||||
memcpy(&K[0], &keyData[0], 64);
|
||||
memcpy(&T[0], &tweakData[0], 16);
|
||||
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
kNw ^= K[i];
|
||||
key[i] = K[i];
|
||||
}
|
||||
|
||||
key[8] = kNw;
|
||||
|
||||
t[0] = T[0];
|
||||
t[1] = T[1];
|
||||
t[2] = T[0] ^ T[1];
|
||||
|
||||
for (uint round = 0; round <= Ns; round++)
|
||||
{
|
||||
for (i = 0; i < Nw; i++)
|
||||
{
|
||||
subKeys[round][i] = key[(round + i) % (Nw + 1)];
|
||||
|
||||
if (i == Nw - 3)
|
||||
{
|
||||
subKeys[round][i] += t[round % 3];
|
||||
}
|
||||
else if (i == Nw - 2)
|
||||
{
|
||||
subKeys[round][i] += t[(round + 1) % 3];
|
||||
}
|
||||
else if (i == Nw - 1)
|
||||
{
|
||||
subKeys[round][i] += round;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Подготовка раундовых ключей (безопасная версия)
|
||||
/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
|
||||
void setup(ulong[8] keyData, ulong[2] tweakData)
|
||||
{
|
||||
setup(keyData.ptr, tweakData.ptr);
|
||||
}
|
||||
|
||||
public static ulong[8] generateKey()
|
||||
{
|
||||
ulong[8] key;
|
||||
auto rng = Random(unpredictableSeed);
|
||||
foreach (i; 0 .. 8)
|
||||
{
|
||||
key[i] = uniform!ulong(rng);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public static ulong[2] generateTweak()
|
||||
{
|
||||
ulong[2] tweak;
|
||||
auto rng = Random(unpredictableSeed);
|
||||
foreach (i; 0 .. 2)
|
||||
{
|
||||
tweak[i] = uniform!ulong(rng);
|
||||
}
|
||||
return tweak;
|
||||
}
|
||||
}
|
||||
109
vv1/agent/angel/utils/cryptography/utils.d
Normal file
109
vv1/agent/angel/utils/cryptography/utils.d
Normal file
@@ -0,0 +1,109 @@
|
||||
module angel.utils.cryptography.utils;
|
||||
|
||||
import core.vararg;
|
||||
import std.traits;
|
||||
import std.algorithm;
|
||||
|
||||
/// TODO: neat variadic implementation of `wipe()`
|
||||
|
||||
/// Clears data in memory.
|
||||
@safe @nogc nothrow
|
||||
void wipe(T)(ref T t) {
|
||||
static if(is(typeof(cast (ubyte[]) t))) {
|
||||
ubyte[] bytes = cast(ubyte[]) t;
|
||||
|
||||
bytes[] = 0;
|
||||
|
||||
if(!all!"a == 0"(bytes[])) {
|
||||
// This should not get optimized away.
|
||||
assert(false, "Wiping failed.");
|
||||
}
|
||||
} else static if ( is(typeof( {T a = T.init;} ))) {
|
||||
t = T.init;
|
||||
|
||||
if(t != T.init) {
|
||||
// This should not get optimized away.
|
||||
assert(false, "Wiping failed.");
|
||||
}
|
||||
} else {
|
||||
static assert(false, "Type not supported for wiping: " ~ T.stringof);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@safe @nogc nothrow
|
||||
void wipe(T...)(ref T ts) {
|
||||
foreach(ref t; ts) {
|
||||
wipe(t);
|
||||
}
|
||||
}
|
||||
|
||||
// test static arrays
|
||||
unittest {
|
||||
ubyte[4] buf1 = [1,2,3,4];
|
||||
uint[4] buf2 = [1,2,3,4];
|
||||
size_t[4] buf3 = [1,2,3,4];
|
||||
|
||||
wipe(buf1);
|
||||
wipe(buf2);
|
||||
wipe(buf3);
|
||||
|
||||
assert(all!"a == 0"(buf1[]), "Failed to wipe ubyte[].");
|
||||
assert(all!"a == 0"(buf2[]), "Failed to wipe ubyte[].");
|
||||
assert(all!"a == 0"(buf3[]), "Failed to wipe ubyte[].");
|
||||
}
|
||||
|
||||
// test dynamic arrays
|
||||
unittest {
|
||||
ubyte[] buf1 = [1,2,3,4];
|
||||
uint[] buf2 = [1,2,3,4];
|
||||
size_t[] buf3 = [1,2,3,4];
|
||||
|
||||
wipe(buf1, buf2, buf3);
|
||||
|
||||
assert(all!"a == 0"(buf1), "Failed to wipe ubyte[].");
|
||||
assert(all!"a == 0"(buf2), "Failed to wipe ubyte[].");
|
||||
assert(all!"a == 0"(buf3), "Failed to wipe ubyte[].");
|
||||
}
|
||||
|
||||
unittest {
|
||||
int a = 42;
|
||||
int b = 84;
|
||||
ubyte c = 1;
|
||||
|
||||
wipe(a, b, c);
|
||||
|
||||
assert(a == 0 && b == 0 && c == 0, "Wiping integer failed!");
|
||||
}
|
||||
|
||||
/// Compares a and b in constant time.
|
||||
///
|
||||
/// Returns: 0 if a == b, some other value if a != b.
|
||||
bool crypto_equals(T)(in T[] a, in T[] b) pure nothrow @safe @nogc
|
||||
in {
|
||||
assert(a.length == b.length, "Unequal length.");
|
||||
} body {
|
||||
T result = 0;
|
||||
size_t i = 0;
|
||||
|
||||
while(i < a.length) {
|
||||
result |= a[i] ^ b[i];
|
||||
++i;
|
||||
}
|
||||
|
||||
if(i != a.length) {
|
||||
// Just to be sure that the compiler optimization does not destroy const time.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
// test crypto_equals
|
||||
unittest {
|
||||
ubyte[32] f = 0;
|
||||
immutable ubyte[32] zero = 0;
|
||||
assert(crypto_equals(f[], zero[]));
|
||||
f[8] = 1;
|
||||
assert(!crypto_equals(f[], zero[]));
|
||||
}
|
||||
Reference in New Issue
Block a user