Initiliazation
This commit is contained in:
331
full/Angel-payload/angel/utils/cryptography/gcm/galoisfield.d
Normal file
331
full/Angel-payload/angel/utils/cryptography/gcm/galoisfield.d
Normal file
@@ -0,0 +1,331 @@
|
||||
module angel.utils.cryptography.gcm.galoisfield;
|
||||
|
||||
import std.traits: isIntegral;
|
||||
import std.array;
|
||||
|
||||
package:
|
||||
@safe
|
||||
struct GF128
|
||||
{
|
||||
|
||||
alias ubyte T;
|
||||
alias T[BLOCKLEN/(T.sizeof*8)] block;
|
||||
|
||||
enum BLOCKLEN = 128;
|
||||
|
||||
enum T R = 0xE1<<(T.sizeof*8-8);
|
||||
|
||||
enum T ONE = 0x80<<(T.sizeof*8-8);
|
||||
|
||||
/**
|
||||
* raises x to the power 'pow' by squaring
|
||||
* x <- x^pow
|
||||
*
|
||||
* Params:
|
||||
* x = this value gets raised to the power pow
|
||||
* pow = the power
|
||||
*/
|
||||
static void power(T)(T[] x, ulong pow) nothrow @nogc
|
||||
if(isIntegral!T)
|
||||
in {
|
||||
assert(x.length*T.sizeof*8 == BLOCKLEN, "invalid length. expected 16 bytes.");
|
||||
}
|
||||
body {
|
||||
block squared;
|
||||
squared[] = x[];
|
||||
|
||||
block exp;
|
||||
exp[0] = ONE; // little endian 1
|
||||
|
||||
block one;
|
||||
one[0] = ONE; // little endian 1
|
||||
|
||||
while(pow > 0) {
|
||||
|
||||
if(pow & 0x1) {
|
||||
multiply(exp, squared);
|
||||
} else {
|
||||
multiply(exp, one); // dummy multiplication to avoid timing attacks
|
||||
}
|
||||
|
||||
multiply(squared, squared);
|
||||
|
||||
pow = pow >> 1;
|
||||
}
|
||||
|
||||
x[] = exp[];
|
||||
}
|
||||
|
||||
// test power
|
||||
unittest {
|
||||
immutable ubyte[] x = cast(immutable ubyte[]) x"66e94bd4ef8a2c3b884cfa59ca342b2e";
|
||||
|
||||
ubyte[16] naivePow;
|
||||
naivePow[0] = 0x80; // little endian 1
|
||||
|
||||
immutable uint pow = 13;
|
||||
|
||||
for(uint i = 0; i < pow; ++i) {
|
||||
multiply(naivePow, x);
|
||||
}
|
||||
|
||||
ubyte[16] powBySquare;
|
||||
powBySquare[] = x[];
|
||||
|
||||
power(powBySquare, pow);
|
||||
|
||||
assert(naivePow == powBySquare, "power() failed");
|
||||
}
|
||||
|
||||
/// Multiplies x by y using schoolbook multiplication. Result stored in x.
|
||||
static void multiply(T[] x, in T[] y) nothrow @nogc
|
||||
in {
|
||||
assert(x.length*T.sizeof*8 == BLOCKLEN, "x: invalid length.");
|
||||
}
|
||||
body {
|
||||
|
||||
block v = x;
|
||||
block z;
|
||||
|
||||
for(uint i = 0; i < y.length; ++i) {
|
||||
T currWord = y[i];
|
||||
|
||||
for(int j = T.sizeof*8-1; j >= 0; --j) {
|
||||
|
||||
// if((currWord >> j) & 0x01) {
|
||||
// z[] ^= v[];
|
||||
// }
|
||||
// avoid branching:
|
||||
//z[] ^= v[]&(-(cast(T)((currWord >> j) & 1)));
|
||||
z[] ^= v[] & cast(T)(-cast(int)((currWord >> j) & 1)); // less prone to timing attacks than if statement
|
||||
|
||||
T lsb = v[$-1] & 1;
|
||||
shiftRight(v);
|
||||
|
||||
// if(lsb) {
|
||||
// v[0] ^= R;
|
||||
// }
|
||||
// Avoid branching by using conditional XOR:
|
||||
v[0] ^= R&(-lsb); // -lsb is either 0x00, or 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
x[] = z[];
|
||||
}
|
||||
|
||||
/// test multiplication by one
|
||||
unittest {
|
||||
immutable block x0 = cast(immutable block) x"66e94bd4ef8a2c3b884cfa59ca342b2e";
|
||||
block x1 = x0;
|
||||
|
||||
block one;
|
||||
one[0] = ONE;
|
||||
|
||||
multiply(x1, one);
|
||||
|
||||
assert(x1 == x0, "GCM multiplication by ONE failed!");
|
||||
}
|
||||
|
||||
/// test multiplication
|
||||
unittest {
|
||||
|
||||
immutable block H = cast(immutable block)x"66e94bd4ef8a2c3b884cfa59ca342b2e";
|
||||
|
||||
block x1 = cast(immutable block) x"0388dace60b6a392f328c2b971b2fe78";
|
||||
|
||||
multiply(x1, H);
|
||||
|
||||
assert(x1 == x"5e2ec746917062882c85b0685353deb7", "GCM multiplication failed!");
|
||||
}
|
||||
|
||||
// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
unittest {
|
||||
|
||||
immutable block H = cast(immutable block) x"73A23D80121DE2D5A850253FCF43120E";
|
||||
block X1 = cast(immutable block) x"D609B1F056637A0D46DF998D88E5222A";
|
||||
|
||||
multiply(X1, H);
|
||||
|
||||
assert(X1 == x"6B0BE68D67C6EE03EF7998E399C01CA4", "GCM multiplication failed!");
|
||||
}
|
||||
|
||||
// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
unittest {
|
||||
|
||||
immutable block H = cast(immutable block) x"286D73994EA0BA3CFD1F52BF06A8ACF2";
|
||||
block X1 = cast(immutable block) x"D609B1F056637A0D46DF998D88E5222A";
|
||||
|
||||
multiply(X1, H);
|
||||
|
||||
assert(X1 == x"BA7C26F578254853CF321281A48317CA", "GCM multiplication failed!");
|
||||
}
|
||||
|
||||
// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
unittest {
|
||||
|
||||
immutable block H = cast(immutable block) x"E4E01725D724C1215C7309AD34539257";
|
||||
block X1 = cast(immutable block) x"E20106D7CD0DF0761E8DCD3D88E54000";
|
||||
|
||||
multiply(X1, H);
|
||||
|
||||
assert(X1 == x"8DAD4981E33493018BB8482F69E4478C", "GCM multiplication failed!");
|
||||
}
|
||||
|
||||
/**
|
||||
* multiplication by P (only bit 1 = 1)
|
||||
*/
|
||||
static void multiplyP(T[] x) nothrow @nogc
|
||||
in {
|
||||
assert(x.length == 16, "x: invalid length. must be 16.");
|
||||
}
|
||||
body {
|
||||
T lsb = x[$-1] & 0x01;
|
||||
shiftRight(x);
|
||||
x[0] ^= R * lsb;
|
||||
}
|
||||
|
||||
// test multiplyP()
|
||||
unittest {
|
||||
|
||||
block X = cast(immutable block) x"E20106D7CD0DF0761E8DCD3D88E54000";
|
||||
immutable block P = cast(immutable block) x"40000000000000000000000000000000";
|
||||
|
||||
block XmultP;
|
||||
XmultP = X;
|
||||
|
||||
multiplyP(XmultP);
|
||||
|
||||
multiply(X, P);
|
||||
|
||||
assert(X == XmultP, "multiplyP() failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* multiplication by P^8
|
||||
*/
|
||||
static void multiplyP8(T)(T[] x)
|
||||
{
|
||||
T lsw = x[$-1];
|
||||
shiftRight8(x);
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
// if (lsw & (1 << i))
|
||||
// {
|
||||
// x[0] ^= ((R<<(T.sizeof*8-8)) >> (7 - i));
|
||||
// }
|
||||
// avoid branching:
|
||||
x[0] ^= (((R<<(T.sizeof*8-8)) >> (7 - i))) * (lsw & (1 << i));
|
||||
}
|
||||
}
|
||||
|
||||
// test multiplyP8()
|
||||
unittest {
|
||||
|
||||
block X = cast(immutable block) x"E20106D7CD0DF0761E8DCD3D88E54000";
|
||||
immutable block P = cast(immutable block) x"40000000000000000000000000000000";
|
||||
|
||||
block XmultP8;
|
||||
XmultP8 = X;
|
||||
|
||||
multiplyP8(XmultP8);
|
||||
|
||||
foreach(i;0..8){
|
||||
multiply(X, P);
|
||||
}
|
||||
|
||||
assert(X == XmultP8, "multiplyP8() failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift big endian number a 1 bit to the right.
|
||||
*/
|
||||
static void shiftRight(T)(T[] a) nothrow @nogc
|
||||
if(isIntegral!T)
|
||||
{
|
||||
T carryBit = 0;
|
||||
for(size_t i = 0; i < a.length; ++i) {
|
||||
T b = a[i];
|
||||
a[i] >>= 1;
|
||||
a[i] |= carryBit;
|
||||
carryBit = cast(T)(b << (T.sizeof * 8 - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// test right shift with bytes
|
||||
unittest {
|
||||
ubyte[] a = [0xf1,0x83,0x01];
|
||||
shiftRight(a);
|
||||
assert(a == [0x78,0xc1,0x80], "right shift failed");
|
||||
}
|
||||
|
||||
// test shiftRight
|
||||
unittest {
|
||||
|
||||
ubyte[16] a = cast(immutable ubyte[16]) x"59ed3f2bb1a0aaa07c9f56c6a504647b";
|
||||
foreach(i;0..8) {
|
||||
shiftRight(a);
|
||||
}
|
||||
|
||||
assert(a == x"0059ed3f2bb1a0aaa07c9f56c6a50464", "right shift failed");
|
||||
}
|
||||
|
||||
// with ints
|
||||
unittest {
|
||||
uint[] a = [0xfedcba98,0x76543210];
|
||||
foreach(i;0..8) {
|
||||
shiftRight(a);
|
||||
}
|
||||
assert(a == [0x00fedcba,0x98765432], "right shift failed");
|
||||
}
|
||||
|
||||
// with longs
|
||||
unittest {
|
||||
ulong[] a = [0x59ed3f2bb1a0aaa0,0x7c9f56c6a504647b];
|
||||
foreach(i;0..8) {
|
||||
shiftRight(a);
|
||||
}
|
||||
assert(a == [0x0059ed3f2bb1a0aa,0xa07c9f56c6a50464], "right shift failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift big endian number a 8 bits to the right.
|
||||
*/
|
||||
static void shiftRight8(T)(T[] a) nothrow @nogc {
|
||||
T carryBit = 0;
|
||||
for(size_t i = 0; i < a.length; ++i) {
|
||||
T b = a[i];
|
||||
a[i] >>= 8;
|
||||
a[i] |= carryBit;
|
||||
carryBit = cast(T)(b << (T.sizeof * 8 - 8));
|
||||
}
|
||||
}
|
||||
|
||||
// static void shiftRight(T)(T[] a, ubyte n) nothrow @nogc {
|
||||
// T carryBit = 0;
|
||||
// for(size_t i = 0; i < a.length; ++i) {
|
||||
// T b = a[i];
|
||||
// a[i] >>= n;
|
||||
// a[i] |= carryBit;
|
||||
// carryBit = cast(T)(b << (T.sizeof * 8 - n));
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// static void shiftRight(ubyte[] a, ubyte n) nothrow @nogc {
|
||||
// shiftRight(cast(uint[])a, n);
|
||||
// }
|
||||
|
||||
// test shiftRight8()
|
||||
unittest {
|
||||
ubyte[16] a = cast(immutable ubyte[16])x"59ed3f2bb1a0aaa07c9f56c6a504647b";
|
||||
ubyte[16] b = a;
|
||||
foreach(i;0..8) {
|
||||
shiftRight(a);
|
||||
}
|
||||
|
||||
shiftRight8(b);
|
||||
|
||||
assert(a == b, "right shift by 8 bits failed");
|
||||
}
|
||||
|
||||
}
|
||||
840
full/Angel-payload/angel/utils/cryptography/gcm/gcm.d
Normal file
840
full/Angel-payload/angel/utils/cryptography/gcm/gcm.d
Normal file
@@ -0,0 +1,840 @@
|
||||
module angel.utils.cryptography.gcm.gcm;
|
||||
|
||||
public import angel.utils.cryptography.aead;
|
||||
import angel.utils.cryptography.gcm.ghash;
|
||||
import angel.utils.cryptography.gcm.multiplier;
|
||||
|
||||
public import angel.utils.cryptography.exceptions: InvalidCipherTextException, IllegalArgumentException;
|
||||
|
||||
|
||||
/// Implementation of the Galois/Counter mode (GCM)
|
||||
/// as described in NIST Special Publication 800-38D
|
||||
///
|
||||
/// Standards: NIST Special Publication 800-38D
|
||||
|
||||
|
||||
// TODO Shoup tables
|
||||
// TODO support for uneven macSize
|
||||
|
||||
//alias GCMEngine(T) = AEADCipherWrapper!(GCM!T); // would be nice but does not yet work
|
||||
|
||||
import angel.utils.cryptography.aes;
|
||||
//static assert(isAEADCipher!(GCM!AES), "GCM ist not a AEADCipher.");
|
||||
|
||||
///
|
||||
bool state; /// 1 encrypt, 0 decrypt
|
||||
/// usage of OOP API:
|
||||
/// auto aes_gcm = new AEADCipherWrapper!(GCM!AES)();
|
||||
///
|
||||
@safe
|
||||
public struct GCM(T) if(is(T == void) || (isBlockCipher!T && T.blockSize == 16))
|
||||
{
|
||||
|
||||
private enum OOP = is(T == void); // use OOP API
|
||||
|
||||
public enum blockSize = 16;
|
||||
public enum macSize = 16;
|
||||
|
||||
// if T == void: use OOP API for underlying block cipher
|
||||
static if(OOP) {
|
||||
/**
|
||||
* Params:
|
||||
* c = underlying BlockCipher
|
||||
*/
|
||||
public this(IBlockCipher c)
|
||||
in {
|
||||
assert(c.blockSize() == blockSize, "GCM: block size of underlying cipher must be 128 bits!");
|
||||
}
|
||||
body {
|
||||
blockCipher = c;
|
||||
}
|
||||
} else {
|
||||
static assert(T.blockSize == blockSize, "GCM: block size of underlying cipher must be 128 bits!");
|
||||
}
|
||||
|
||||
private {
|
||||
|
||||
static if(OOP) {
|
||||
IBlockCipher blockCipher;
|
||||
} else {
|
||||
T blockCipher; /// underlying BlockCipher
|
||||
}
|
||||
|
||||
GHash gHash; /// provides the multiplication in GF(2^128) by H
|
||||
CircularBlockBuffer!blockSize buf; /// stores input data before processing
|
||||
|
||||
ubyte[blockSize] Y; /// counter
|
||||
ubyte[blockSize] E0; /// E(key, Y0), needed to derive AuthTag from GHASH
|
||||
ubyte[blockSize] mac; /// used to store the encrypted ghash TODO: use other buffer, e.g. E0 itself
|
||||
|
||||
ubyte[blockSize] initialY; /// used to reset Y
|
||||
|
||||
ubyte[] userKey;
|
||||
ubyte[] iv;
|
||||
bool initialized = false; /// True if and only if GCM has been initialized
|
||||
}
|
||||
|
||||
public {
|
||||
|
||||
/// Initialize the underlying cipher.
|
||||
/// Params:
|
||||
/// forEncryption = true if we are setting up for encryption, false otherwise.
|
||||
/// key = Secret key.
|
||||
/// nonce = Number used only once.
|
||||
void start(in ubyte[] key, in ubyte[] iv) nothrow @nogc
|
||||
in {
|
||||
assert(iv !is null, "Must provide an IV.");
|
||||
}
|
||||
body {
|
||||
|
||||
//this.forEncryption = forEncryption;
|
||||
|
||||
// init underyling cipher
|
||||
blockCipher.start(key);
|
||||
|
||||
// init gHash
|
||||
ubyte[blockSize] H;
|
||||
H[] = 0;
|
||||
blockCipher.decrypt(H,H); // calculate H=E(K,0^128);
|
||||
|
||||
gHash.init(H);
|
||||
|
||||
// init IV
|
||||
if(iv.length == 12) { // 96 bit IV is optimal
|
||||
Y[0..iv.length] = iv[];
|
||||
Y[$-1] = 1;
|
||||
}else {
|
||||
gHash.updateCipherData(iv);
|
||||
gHash.doFinal(Y);
|
||||
}
|
||||
|
||||
// generate key stream used later to encrypt ghash
|
||||
genNextKeyStreamBlock(E0);
|
||||
|
||||
initialY = Y; // remember this to reset the cipher
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static if(OOP) {
|
||||
/**
|
||||
* Returns: the algorithm name.
|
||||
*/
|
||||
string name() pure nothrow {
|
||||
return blockCipher.name ~ "/GCM";
|
||||
}
|
||||
} else {
|
||||
public enum name = T.name~"/GCM";
|
||||
}
|
||||
|
||||
static if(OOP) {
|
||||
/**
|
||||
* Returns: the cipher this object wraps.
|
||||
*/
|
||||
IBlockCipher getUnderlyingCipher() pure nothrow @nogc {
|
||||
return blockCipher;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Returns: the cipher this object wraps.
|
||||
*/
|
||||
ref T getUnderlyingCipher() pure nothrow @nogc {
|
||||
return blockCipher;
|
||||
}
|
||||
}
|
||||
|
||||
/// Process additional authenticated data.
|
||||
void processAADBytes(in ubyte[] aad...) nothrow @nogc
|
||||
in {
|
||||
assert(initialized, "not initialized");
|
||||
}
|
||||
body {
|
||||
gHash.updateAAD(aad);
|
||||
}
|
||||
|
||||
/// Process a block of bytes from in putting the result into out.
|
||||
///
|
||||
/// Params:
|
||||
/// input = The input byte array.
|
||||
/// output = The output buffer the processed bytes go into.
|
||||
///
|
||||
/// Returns:
|
||||
/// Returns a slice pointing to the output data.
|
||||
ubyte[] encrypt(in ubyte[] input, ubyte[] output) nothrow {
|
||||
state = 1;
|
||||
return processBytes(input, output);
|
||||
}
|
||||
ubyte[] decrypt(in ubyte[] input, ubyte[] output) nothrow {
|
||||
state = 0;
|
||||
return processBytes(input, output);
|
||||
}
|
||||
|
||||
ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow
|
||||
in {
|
||||
assert(initialized, "not initialized");
|
||||
assert(output.length >= getUpdateOutputSize(input.length), "output buffer too short");
|
||||
}
|
||||
body {
|
||||
|
||||
import std.algorithm: min;
|
||||
|
||||
size_t outputBytes = 0;
|
||||
|
||||
const(ubyte)[] iBuf = input;
|
||||
ubyte[] outPtr = output;
|
||||
|
||||
while(iBuf.length > 0) {
|
||||
if(buf.isFull()) {
|
||||
// encrypt one block
|
||||
outputBlock(outPtr);
|
||||
outPtr = outPtr[blockSize..$];
|
||||
outputBytes += blockSize;
|
||||
}
|
||||
|
||||
// copy max one block to the buffer
|
||||
size_t procLen = buf.put(iBuf);
|
||||
iBuf = iBuf[procLen..$];
|
||||
}
|
||||
|
||||
return output[0..outputBytes];
|
||||
}
|
||||
|
||||
|
||||
/// Finish the operation. Does not append mac tag to the cipher text.
|
||||
/// Mac tag does NOT get verified in decryption mode.
|
||||
///
|
||||
/// Params: out = space for any resulting output data.
|
||||
/// Returns: number of bytes written into out.
|
||||
size_t finish(ubyte[] macBuf, ubyte[] output) nothrow
|
||||
in {
|
||||
assert(initialized, "not initialized");
|
||||
|
||||
assert(output.length >= buf.length, "output buffer too small");
|
||||
assert(macBuf.length == 16, "MAC buffer must be 16 bytes.");
|
||||
}
|
||||
body{
|
||||
|
||||
size_t outputBytes = 0;
|
||||
|
||||
// if(!forEncryption) {
|
||||
// if(buf.length < macLen) {
|
||||
// throw new InvalidCipherTextException("ciphertext so short that it can't even contain the MAC");
|
||||
// }
|
||||
// }
|
||||
|
||||
size_t partialBlockLen = buf.length;
|
||||
|
||||
ubyte[2*blockSize] lastBlocks; // last two blocks. probably not full. last few bytes are the token.
|
||||
|
||||
|
||||
// copy partial cipher data block
|
||||
buf.drainAll(lastBlocks);
|
||||
|
||||
assert(output.length >= partialBlockLen, "output buffer too short");
|
||||
// encrypt last partial block
|
||||
ubyte[2*blockSize] keyStream;
|
||||
|
||||
// generate two blocks of key stream
|
||||
genNextKeyStreamBlock(keyStream[0..blockSize]);
|
||||
genNextKeyStreamBlock(keyStream[blockSize..2*blockSize]);
|
||||
|
||||
output[0..partialBlockLen] = lastBlocks[0..partialBlockLen] ^ keyStream[0..partialBlockLen];
|
||||
|
||||
gHash.updateCipherData(state ? output[0..partialBlockLen] : lastBlocks[0..partialBlockLen]);
|
||||
|
||||
output = output[partialBlockLen..$];
|
||||
outputBytes += partialBlockLen;
|
||||
|
||||
// calculate the hash
|
||||
ubyte[16] mac;
|
||||
gHash.doFinal(mac);
|
||||
|
||||
mac[] ^= E0[]; // calculate the token
|
||||
|
||||
macBuf[0..16] = mac[];
|
||||
|
||||
return outputBytes;
|
||||
}
|
||||
|
||||
/// Returns: Return the size of the output buffer required for a processBytes an input of len bytes.
|
||||
size_t getUpdateOutputSize(size_t len) nothrow @nogc pure const {
|
||||
size_t total = len + buf.length;
|
||||
//return (total + blockSize - 1) && (~blockSize+1);
|
||||
return total - (total % blockSize);
|
||||
}
|
||||
|
||||
|
||||
/// Returns: Return the size of the output buffer required for a processBytes plus a finish with an input of len bytes.
|
||||
size_t getOutputSize(size_t len) nothrow @nogc pure const {
|
||||
return len;
|
||||
}
|
||||
|
||||
/// 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
|
||||
{
|
||||
gHash.reset();
|
||||
buf.reset();
|
||||
|
||||
Y = initialY;
|
||||
blockCipher.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private nothrow @safe @nogc {
|
||||
|
||||
/**
|
||||
* generates the next key stream block by incrementing the counter
|
||||
* and encrypting it.
|
||||
*
|
||||
* bufOff is set to 0
|
||||
*/
|
||||
void genNextKeyStreamBlock(ubyte[] buf)
|
||||
in {
|
||||
assert(buf.length == blockSize);
|
||||
//assert(keyStreamBufOff == BLOCKSIZE, "not yet ready to generate next block");
|
||||
}
|
||||
body {
|
||||
blockCipher.encrypt(Y,buf);
|
||||
incrCounter();
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypt or decrypt a block and write it to output
|
||||
* update GHash
|
||||
*/
|
||||
void outputBlock(ubyte[] output)
|
||||
in {
|
||||
assert(output.length >= blockSize, "output buffer too short");
|
||||
assert(buf.length >= blockSize, "not enough data in buffer");
|
||||
}
|
||||
body {
|
||||
ubyte[blockSize] keyStream;
|
||||
ubyte[blockSize] inputBuf;
|
||||
genNextKeyStreamBlock(keyStream);
|
||||
|
||||
buf.drainBlock(inputBuf);
|
||||
|
||||
// encrypt the buffer
|
||||
output[0..blockSize] = keyStream[0..blockSize] ^ inputBuf[0..blockSize];
|
||||
|
||||
// update gHash
|
||||
gHash.updateCipherData(state ? output[0..blockSize] : inputBuf[0..blockSize]);
|
||||
}
|
||||
|
||||
/**
|
||||
* increment Y by 1
|
||||
* treats rightmost 32 bits as uint, lsb on the right
|
||||
*/
|
||||
void incrCounter() {
|
||||
for(uint i = blockSize -1; i >= blockSize-4; --i) {
|
||||
if(++Y[i] != 0) {
|
||||
break;
|
||||
}
|
||||
// increment next element on overflow of the previous
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Test with test vectors from
|
||||
/// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
/// section 2.2.1
|
||||
unittest {
|
||||
import dcrypt.blockcipher.aes;
|
||||
|
||||
alias const(ubyte)[] octets;
|
||||
|
||||
octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
|
||||
octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
|
||||
|
||||
GCM!AES gcm;
|
||||
gcm.start(key, iv);
|
||||
|
||||
ubyte[48] output;
|
||||
ubyte[] oBuf = output;
|
||||
size_t outLen;
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
|
||||
|
||||
outLen = gcm.processBytes(cast(octets)x"08000F101112131415161718191A1B1C", oBuf).length;
|
||||
oBuf = oBuf[outLen..$];
|
||||
outLen = gcm.processBytes(cast(octets)x"1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A", oBuf).length;
|
||||
oBuf = oBuf[outLen..$];
|
||||
|
||||
outLen = gcm.processBytes(cast(octets)x"0002", oBuf).length;
|
||||
oBuf = oBuf[outLen..$];
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"B2C2846512153524C0895E81");
|
||||
ubyte[16] mac;
|
||||
outLen = gcm.finish(mac, oBuf);
|
||||
// import std.stdio;
|
||||
// writefln("%(%x%)", output);
|
||||
assert(output == cast(octets)x"701AFA1CC039C0D765128A665DAB69243899BF7318CCDC81C9931DA17FBE8EDD7D17CB8B4C26FC81E3284F2B7FBA713D");
|
||||
assert(mac == cast(octets)x"4F8D55E7D3F06FD5A13C0C29B9D5B880");
|
||||
}
|
||||
|
||||
/// test decryption
|
||||
/// test vectors from
|
||||
/// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
/// section 2.2.1
|
||||
unittest {
|
||||
import dcrypt.blockcipher.aes;
|
||||
|
||||
alias const(ubyte)[] octets;
|
||||
|
||||
octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
|
||||
octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
|
||||
|
||||
GCM!AES gcm;
|
||||
gcm.start(key, iv);
|
||||
|
||||
ubyte[48] output;
|
||||
ubyte[] oBuf = output;
|
||||
size_t outLen;
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
|
||||
|
||||
// add ciphertext
|
||||
outLen = gcm.processBytes(cast(octets)
|
||||
x"701AFA1CC039C0D765128A665DAB6924
|
||||
3899BF7318CCDC81C9931DA17FBE8EDD
|
||||
7D17CB8B4C26FC81E3284F2B7FBA713D", oBuf).length;
|
||||
oBuf = oBuf[outLen..$];
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"B2C2846512153524C0895E81");
|
||||
ubyte[16] mac;
|
||||
outLen = gcm.finish(mac, oBuf);
|
||||
// import std.stdio;
|
||||
// writefln("%(%.2x%)", output);
|
||||
|
||||
assert(output ==
|
||||
x"08000F101112131415161718191A1B1
|
||||
C1D1E1F202122232425262728292A2B
|
||||
2C2D2E2F303132333435363738393A0002");
|
||||
|
||||
assert(mac == x"4F8D55E7D3F06FD5A13C0C29B9D5B880");
|
||||
}
|
||||
|
||||
/// Test decryption with modified cipher data. An exception should be thrown beacause of wrong token.
|
||||
///
|
||||
/// test vectors from
|
||||
/// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
/// section 2.2.1
|
||||
unittest {
|
||||
import dcrypt.blockcipher.aes;
|
||||
|
||||
alias const(ubyte)[] octets;
|
||||
|
||||
octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
|
||||
octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
|
||||
|
||||
GCM!AES gcm;
|
||||
gcm.start(key, iv);
|
||||
|
||||
ubyte[48] output;
|
||||
ubyte[] oBuf = output[];
|
||||
size_t outLen;
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
|
||||
|
||||
// add ciphertext
|
||||
outLen = gcm.processBytes(cast(octets)
|
||||
x"701AFA1CC039C0D765128A665DAB6924
|
||||
3899BF7318CCDC81C9931DA17FBE8EDD
|
||||
7D17CB8B4C26FC81E3284F2B7FBA713D", oBuf).length; // 880 has been changed do EEF
|
||||
oBuf = oBuf[outLen..$];
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"B2C2846512153524C0895E81");
|
||||
ubyte[16] mac;
|
||||
outLen = gcm.finish(mac, oBuf);
|
||||
assert(mac != x"4F8D55E7D3F06FD5A13C0C29B9D5BEEF");
|
||||
}
|
||||
|
||||
/// Test decryption with altered AAD. An exception should be thrown beacause of wrong token.
|
||||
///
|
||||
/// test vectors from
|
||||
/// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
/// section 2.2.1
|
||||
unittest {
|
||||
import dcrypt.blockcipher.aes;
|
||||
|
||||
alias const(ubyte)[] octets;
|
||||
|
||||
octets key = cast(octets)x"AD7A2BD03EAC835A6F620FDCB506B345";
|
||||
octets iv = cast(octets)x"12153524C0895E81B2C28465"; // 96 bits
|
||||
|
||||
GCM!AES gcm;
|
||||
gcm.start(key, iv);
|
||||
|
||||
ubyte[48] output;
|
||||
ubyte[] oBuf = output;
|
||||
size_t outLen;
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"D609B1F056637A0D46DF998D88E52E00");
|
||||
|
||||
// add ciphertext
|
||||
outLen = gcm.processBytes(cast(octets)
|
||||
x"701AFA1CC039C0D765128A665DAB6924
|
||||
3899BF7318CCDC81C9931DA17FBE8EDD
|
||||
7D17CB8B4C26FC81E3284F2B7FBA713D", oBuf).length;
|
||||
oBuf = oBuf[outLen..$];
|
||||
|
||||
gcm.processAADBytes(cast(octets)x"B2C2846512153524C089beef"); // changed 5E81 to beef
|
||||
ubyte[16] mac;
|
||||
gcm.finish(mac, oBuf);
|
||||
assert(mac != x"4F8D55E7D3F06FD5A13C0C29B9D5B880");
|
||||
// verify that an InvalidCipherTextException is thrown
|
||||
// bool exception = false;
|
||||
// try {
|
||||
// outLen = gcm.finish(oBuf);
|
||||
// } catch (InvalidCipherTextException e) {
|
||||
// exception = true;
|
||||
// }
|
||||
// assert(exception, "AAD has been altered but no exception has been thrown!");
|
||||
}
|
||||
|
||||
// test vectors from
|
||||
// gcm-spec: Test Case 6
|
||||
unittest {
|
||||
|
||||
import utils.cryptography.aes;
|
||||
|
||||
alias const(ubyte)[] octets;
|
||||
|
||||
octets key = cast(octets)x"feffe9928665731c6d6a8f9467308308";
|
||||
octets iv = cast(octets)
|
||||
x"9313225df88406e555909c5aff5269aa
|
||||
6a7a9538534f7da1e4c303d2a318a728
|
||||
c3c0c95156809539fcf0e2429a6b5254
|
||||
16aedbf5a0de6a57a637b39b"; // more than 96 bits
|
||||
|
||||
GCM!AES gcm;
|
||||
gcm.start(key, iv);
|
||||
|
||||
octets aad = cast(octets)(
|
||||
x"feedfacedeadbeeffeedfacedeadbeef
|
||||
abaddad2"
|
||||
);
|
||||
|
||||
octets plaintext = cast(octets)(
|
||||
x"d9313225f88406e5a55909c5aff5269a
|
||||
86a7a9531534f7da2e4c303d8a318a72
|
||||
1c3c0c95956809532fcf0e2449a6b525
|
||||
b16aedf5aa0de657ba637b39"
|
||||
);
|
||||
|
||||
ubyte[] output = new ubyte[gcm.getOutputSize(plaintext.length)];
|
||||
ubyte[] oBuf = output;
|
||||
size_t outLen;
|
||||
|
||||
outLen = gcm.processBytes(plaintext, oBuf).length;
|
||||
oBuf = oBuf[outLen..$];
|
||||
|
||||
gcm.processAADBytes(aad);
|
||||
ubyte[16] mac;
|
||||
outLen = gcm.finish(mac, oBuf);
|
||||
oBuf = oBuf[outLen..$];
|
||||
|
||||
octets expectedCiphertext = cast(octets) (
|
||||
x"8ce24998625615b603a033aca13fb894
|
||||
be9112a5c3a211a8ba262a3cca7e2ca7
|
||||
01e4a9a4fba43c90ccdcb281d48c7c6f
|
||||
d62875d2aca417034c34aee5"
|
||||
);
|
||||
|
||||
octets expectedMac = cast(octets) x"619cc5aefffe0bfa462af43c1699d050";
|
||||
|
||||
assert(output == expectedCiphertext);
|
||||
assert(mac == expectedMac);
|
||||
}
|
||||
|
||||
/// test GCM with different MAC sizes
|
||||
unittest {
|
||||
|
||||
import dcrypt.blockcipher.aes;
|
||||
|
||||
string[] keys = [
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
];
|
||||
string[] ivs = [
|
||||
x"00",
|
||||
x"00000000",
|
||||
x"00000000000000",
|
||||
x"00000000000000000000",
|
||||
x"00000000000000000000000000",
|
||||
x"00000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
];
|
||||
string[] aads = [
|
||||
x"",
|
||||
x"00000000000000",
|
||||
x"0000000000000000000000000000",
|
||||
x"000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000",
|
||||
x"0000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
];
|
||||
string[] plains = [
|
||||
x"",
|
||||
x"0000000000",
|
||||
x"00000000000000000000",
|
||||
x"000000000000000000000000000000",
|
||||
x"0000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000",
|
||||
x"000000000000000000000000000000000000000000000000000000000000",
|
||||
x"0000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
];
|
||||
string[] ciphers = [
|
||||
x"3c2fa7a9",
|
||||
x"078bb038e6b2353f0e05",
|
||||
x"d6a480d4dec719bd36a60efde3aaf1f8",
|
||||
x"e37dd3785cc7017f206df18d831e37cfe63f9e057a23",
|
||||
x"3fe95bef64662ddcf19a96cc584d2146499320eef8d518bb5e7e49a7",
|
||||
x"a3b22b8449afafbcd6c09f2cfa9de2be938f8bbf235863d0cefb4075046c9a4d351e",
|
||||
x"a0912f3bde077afa3f21725fbcae1c9c2e00b28b6eb462745e9b65a026cc4ba84d13b408b7061fe1",
|
||||
x"535b0d13cbb1012df5402f748cea5304d52db1e4b997317a54c2296b95e0300c6692f911625bfe617d16b63a237b",
|
||||
x"547096f9d7a83ba8d128467baac4a9d861ebd51cc2dfff111915cd0b4260b7dc49c8d8723eb15429024ac21eed99ca1338844092",
|
||||
x"95e67a9eade034290efa90e33f51710f02f3aba4c32873545891924aa52dcc092695e983b529b60e7b13aee5f7d6de278c77410e216d0fdbd7e1",
|
||||
x"0957e69831df479e8cf7b214e1cef4d3e7a2716e8179deaf8061383f35eeabd017080c3d7972b98009a38b5842a2a08a9123412338e16de05a72b76849629b48",
|
||||
x"07052b0f8b95c9491ae43bac6693802384688e9dd19d9ce295b4ab550163a2bb4b0dd905012a56094e895ea7a5857f8100af40b4adb6452d0b8e78e709c5c9f1d432b5f59317",
|
||||
x"e0902e27a95867acaa788920ac71b2f2a61863bdc40ee869bea53470edf02fc71800465c550a58ba69220c67243899d756cf0a5ac4fda582fc6e9d2f8498a0e73e0e809bfb8d86ab5fdf066c",
|
||||
];
|
||||
uint[] macSizes = [
|
||||
32,
|
||||
40,
|
||||
48,
|
||||
56,
|
||||
64,
|
||||
72,
|
||||
80,
|
||||
88,
|
||||
96,
|
||||
104,
|
||||
112,
|
||||
120,
|
||||
128,
|
||||
];
|
||||
|
||||
AEADCipherTest(
|
||||
new GCMEngine(new AESEngine),
|
||||
keys,
|
||||
ivs,
|
||||
plains,
|
||||
aads,
|
||||
ciphers,
|
||||
macSizes);
|
||||
|
||||
}
|
||||
|
||||
/// OOP Wrapper for GCM
|
||||
@safe
|
||||
public class GCMEngine: IAEADEngine {
|
||||
|
||||
private GCM!void cipher = void;
|
||||
|
||||
public {
|
||||
|
||||
/// Params: c = underlying block cipher
|
||||
this(IBlockCipher c) {
|
||||
cipher = GCM!void(c);
|
||||
}
|
||||
|
||||
void start(in ubyte[] key, in ubyte[] iv) nothrow @nogc {
|
||||
cipher.start(key, iv);
|
||||
}
|
||||
|
||||
@property
|
||||
string name() pure nothrow {
|
||||
return cipher.name;
|
||||
}
|
||||
|
||||
IBlockCipher getUnderlyingCipher() pure nothrow {
|
||||
return cipher.getUnderlyingCipher();
|
||||
}
|
||||
|
||||
void processAADBytes(in ubyte[] aad) nothrow {
|
||||
cipher.processAADBytes(aad);
|
||||
}
|
||||
|
||||
ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow {
|
||||
return cipher.processBytes(input, output);
|
||||
}
|
||||
ubyte[] encrypt(in ubyte[] input, ubyte[] output) nothrow {
|
||||
state = 1;
|
||||
return cipher.processBytes(input, output);
|
||||
}
|
||||
ubyte[] decrypt(in ubyte[] input, ubyte[] output) nothrow {
|
||||
state = 0;
|
||||
return cipher.processBytes(input, output);
|
||||
}
|
||||
|
||||
size_t finish(ubyte[] macBuf, ubyte[] output) {
|
||||
return cipher.finish(macBuf, output);
|
||||
}
|
||||
|
||||
size_t getUpdateOutputSize(size_t len) nothrow const {
|
||||
return cipher.getUpdateOutputSize(len);
|
||||
}
|
||||
|
||||
size_t getOutputSize(size_t len) nothrow const {
|
||||
return cipher.getOutputSize(len);
|
||||
}
|
||||
|
||||
void reset() nothrow {
|
||||
cipher.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Circular buffer holding 2*BLOCKSIZE bytes of data.
|
||||
@safe
|
||||
private struct CircularBlockBuffer(size_t BLOCKSIZE) {
|
||||
|
||||
import std.algorithm: min;
|
||||
|
||||
private {
|
||||
ubyte[2*BLOCKSIZE] buf;
|
||||
size_t offset = 0;
|
||||
size_t contentLen = 0;
|
||||
ubyte nextOutputBlock = 0;
|
||||
}
|
||||
|
||||
invariant {
|
||||
assert(offset <= 2*BLOCKSIZE, "offset out of bounds");
|
||||
assert(contentLen <= 2*BLOCKSIZE, "contentLen out of bounds");
|
||||
assert(nextOutputBlock <= 2, "nextOutputBlock out of bounds");
|
||||
}
|
||||
|
||||
|
||||
public nothrow @nogc {
|
||||
|
||||
/**
|
||||
* try to fill the buffer
|
||||
*
|
||||
* Returns: number of bytes written to buffer
|
||||
*/
|
||||
size_t put(in ubyte[] input)
|
||||
out (result){
|
||||
assert(result <= input.length);
|
||||
}
|
||||
body {
|
||||
|
||||
size_t procLen = min(input.length, 2*BLOCKSIZE - contentLen);
|
||||
|
||||
const(ubyte)[] iBuf = input;
|
||||
|
||||
// copy input into buffer
|
||||
foreach(i;0..procLen) {
|
||||
buf[offset] = input[i];
|
||||
offset = (offset + 1) % (2*BLOCKSIZE);
|
||||
}
|
||||
|
||||
contentLen += procLen;
|
||||
|
||||
return procLen;
|
||||
}
|
||||
|
||||
bool isFull() {
|
||||
return contentLen == buf.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* write max one block to output if buffer is full
|
||||
*
|
||||
* Returns: number of bytes written to output
|
||||
*/
|
||||
size_t drainBlock(ubyte[] output)
|
||||
in {
|
||||
assert(output.length >= BLOCKSIZE, "output buffer too short");
|
||||
}
|
||||
body {
|
||||
if(isFull()) {
|
||||
|
||||
size_t blockOff = nextOutputBlock * BLOCKSIZE;
|
||||
|
||||
// copy one block to output
|
||||
output[0..BLOCKSIZE] = buf[blockOff..blockOff+BLOCKSIZE];
|
||||
|
||||
nextOutputBlock ^= 0x01; // 0,1,0,1,...
|
||||
contentLen -= BLOCKSIZE;
|
||||
return BLOCKSIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* write whole buffer content to output
|
||||
*
|
||||
* Returns: number of bytes written to output
|
||||
*/
|
||||
size_t drainAll(ubyte[] output)
|
||||
in {
|
||||
assert(output.length >= contentLen, "output buffer too short");
|
||||
}
|
||||
body {
|
||||
|
||||
size_t startOff = nextOutputBlock * BLOCKSIZE;
|
||||
|
||||
// copy data to output
|
||||
foreach(i;0..contentLen) {
|
||||
output[i] = buf[(startOff + i) % (2*BLOCKSIZE)];
|
||||
}
|
||||
|
||||
size_t outLen = contentLen;
|
||||
contentLen = 0;
|
||||
nextOutputBlock = 0;
|
||||
offset = 0;
|
||||
return outLen;
|
||||
}
|
||||
|
||||
@property
|
||||
size_t length() const {
|
||||
return contentLen;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
buf[] = 0;
|
||||
offset = 0;
|
||||
contentLen = 0;
|
||||
nextOutputBlock = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
373
full/Angel-payload/angel/utils/cryptography/gcm/ghash.d
Normal file
373
full/Angel-payload/angel/utils/cryptography/gcm/ghash.d
Normal file
@@ -0,0 +1,373 @@
|
||||
module angel.utils.cryptography.gcm.ghash;
|
||||
|
||||
import angel.utils.cryptography.gcm.galoisfield;
|
||||
import angel.utils.cryptography.gcm.multiplier;
|
||||
|
||||
// BUG: llvm crashes when using GCMMultiplier64kTable
|
||||
alias GHashGeneral!GCMMultiplier8kTable GHash;
|
||||
|
||||
/// Params:
|
||||
/// Multiplier = a GCM Multiplier like GCMMultiplier8kTable
|
||||
@safe
|
||||
public struct GHashGeneral(Multiplier) if(isGCMMultiplier!Multiplier)
|
||||
{
|
||||
enum BLOCKSIZE = 16; /// block size in bytes
|
||||
|
||||
alias ubyte T;
|
||||
alias T[BLOCKSIZE/(T.sizeof)] block;
|
||||
|
||||
private {
|
||||
|
||||
block stateCipher; /// state for cipher data hashing
|
||||
block H;
|
||||
block stateAAD; /// state for AAD hashing
|
||||
block stateAADPre; /// stateAAD before first cipher byte is processed
|
||||
|
||||
ubyte stateAADOff = 0; /// offset in stateAAD buffer
|
||||
ubyte stateCipherOff = 0; /// offset in stateCipher buffer
|
||||
|
||||
ulong lenAAD = 0; /// length of additional authenticated data (AAD) in bits
|
||||
ulong lenAADPre = 0; /// length of AAD before first cipher byte is processed
|
||||
ulong lenCipher = 0; /// length of authenticated cipher data in bits
|
||||
|
||||
bool aadInput = true; /// AAD or cipher input
|
||||
|
||||
Multiplier gcmMult;
|
||||
}
|
||||
|
||||
invariant {
|
||||
// offsets should never exceed their boundaries
|
||||
assert(stateAADOff <= BLOCKSIZE);
|
||||
assert(stateCipherOff <= BLOCKSIZE);
|
||||
}
|
||||
|
||||
/// Params:
|
||||
/// H = element of GF(2^128)
|
||||
this(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == BLOCKSIZE, "H must be 16 bytes");
|
||||
}
|
||||
body {
|
||||
init(H);
|
||||
}
|
||||
|
||||
/// initialize the hash
|
||||
/// Params:
|
||||
/// H = the factor used for multiplication.
|
||||
public void init(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == BLOCKSIZE, "H must be 16 bytes");
|
||||
}
|
||||
body {
|
||||
this.H[] = H[];
|
||||
|
||||
// init the multiplier
|
||||
gcmMult.init(H);
|
||||
}
|
||||
|
||||
|
||||
/// add data to the AAD stream
|
||||
/// Params:
|
||||
/// aad = data to be authenticated only
|
||||
public void updateAAD(in ubyte[] aad...) nothrow @nogc
|
||||
{
|
||||
update(stateAAD, stateAADOff, aad);
|
||||
lenAAD += aad.length*8;
|
||||
}
|
||||
|
||||
/// Call this before processing any cipher data for better performance.
|
||||
private void finalizeAAD() nothrow @nogc {
|
||||
|
||||
stateCipher[] = stateAAD[];
|
||||
|
||||
if(lenAAD > 0) {
|
||||
stateAADPre[] = stateAAD[];
|
||||
lenAADPre = lenAAD;
|
||||
}
|
||||
|
||||
if(stateAADOff > 0) {
|
||||
// process partial block
|
||||
multiplyH(stateCipher);
|
||||
stateAADOff = 0;
|
||||
}
|
||||
aadInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* input = encrypted data
|
||||
*/
|
||||
public void updateCipherData(in ubyte[] input...) nothrow @nogc
|
||||
{
|
||||
if(aadInput) {
|
||||
finalizeAAD(); // sets aadInput = false
|
||||
}
|
||||
update(stateCipher, stateCipherOff, input);
|
||||
lenCipher += input.length*8;
|
||||
}
|
||||
|
||||
/// do final hash round and copy hash to buf
|
||||
/// resets GHASH
|
||||
/// Params: buf = output buffer for hash value
|
||||
public void doFinal(ubyte[] buf) nothrow @nogc
|
||||
in {
|
||||
assert(buf.length >= BLOCKSIZE, "output buffer too short");
|
||||
}
|
||||
body {
|
||||
|
||||
if(stateAADOff > 0) {
|
||||
// process last partial AAD block
|
||||
multiplyH(stateAAD);
|
||||
stateAADOff = 0;
|
||||
}
|
||||
|
||||
// process last incomplete block
|
||||
if(stateCipherOff > 0) {
|
||||
multiplyH(stateCipher);
|
||||
stateCipherOff = 0;
|
||||
}
|
||||
|
||||
if(lenAAD > lenAADPre) {
|
||||
// some AAD has been processed after first cipher bytes arrived
|
||||
// need to adjust the MAC state
|
||||
|
||||
// caluculate the difference
|
||||
stateAADPre[] ^= stateAAD[];
|
||||
|
||||
ulong blockDiff = (lenCipher + 127) / 128; // number of cipher data blocks.
|
||||
// + 127 added for rounding up.
|
||||
|
||||
// calculate H^blockDiff
|
||||
ubyte[BLOCKSIZE] expH;
|
||||
expH[] = H[];
|
||||
GF128.power(expH, blockDiff);
|
||||
|
||||
// propagate the difference to the current block
|
||||
GF128.multiply(stateAADPre, expH);
|
||||
|
||||
// add the difference to the current block
|
||||
stateCipher[] ^= stateAADPre[];
|
||||
}
|
||||
|
||||
// Add a block containing the length of both streams: X ^ (len(A)||len(C)) * H
|
||||
foreach(i;0..8) {
|
||||
stateCipher[i] ^= lenAAD >> (56-8*i);
|
||||
stateCipher[i+8] ^= lenCipher >> (56-8*i);
|
||||
}
|
||||
|
||||
multiplyH(stateCipher);
|
||||
|
||||
buf[0..BLOCKSIZE] = stateCipher[];
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
/// Reset the internal state.
|
||||
public void reset() nothrow @nogc {
|
||||
stateAAD[] = 0;
|
||||
stateAADOff = 0;
|
||||
stateCipher[] = 0;
|
||||
stateCipherOff = 0;
|
||||
lenCipher = 0;
|
||||
lenAAD = 0;
|
||||
|
||||
lenAADPre = 0;
|
||||
stateAADPre[] = 0;
|
||||
|
||||
aadInput = true;
|
||||
}
|
||||
|
||||
/// xor X with input bytes and do GF multiplication by H if buffer is full
|
||||
/// Params:
|
||||
/// input = incoming data
|
||||
/// state = update this state
|
||||
/// statePos = pointer to the location where the next byte gets written
|
||||
private void update(ubyte[] state, ref ubyte statePos, in ubyte[] input...) nothrow @nogc
|
||||
in {
|
||||
assert(state.length == 16);
|
||||
}
|
||||
body {
|
||||
import std.algorithm: min;
|
||||
|
||||
const(ubyte)[] iBuf = input;
|
||||
|
||||
if(statePos == BLOCKSIZE) {
|
||||
multiplyH(state);
|
||||
statePos = 0;
|
||||
}
|
||||
|
||||
while(iBuf.length > 0) {
|
||||
|
||||
size_t procLen = min(iBuf.length, BLOCKSIZE-statePos);
|
||||
|
||||
state[statePos..statePos+procLen] ^= iBuf[0..procLen];
|
||||
|
||||
statePos += procLen;
|
||||
iBuf = iBuf[procLen..$];
|
||||
|
||||
if(statePos == BLOCKSIZE) {
|
||||
multiplyH(state);
|
||||
statePos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply x by H, store result in x.
|
||||
private void multiplyH(ubyte[] x) nothrow @nogc {
|
||||
gcmMult.multiply(x);
|
||||
}
|
||||
|
||||
// unittests
|
||||
|
||||
unittest {
|
||||
GHash gHash = GHash(cast(const(ubyte)[])x"66e94bd4ef8a2c3b884cfa59ca342b2e");
|
||||
|
||||
ubyte[16] token;
|
||||
gHash.doFinal(token);
|
||||
|
||||
const(ubyte)[] EK0 = cast(const(ubyte)[])x"58e2fccefa7e3061367f1d57a4e7455a";
|
||||
token[] ^= EK0[];
|
||||
|
||||
assert(token == x"58e2fccefa7e3061367f1d57a4e7455a");
|
||||
}
|
||||
|
||||
unittest {
|
||||
|
||||
GHash gHash = GHash(cast(const(ubyte)[])x"66e94bd4ef8a2c3b884cfa59ca342b2e");
|
||||
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"0388dace60b6a392f328c2b971b2fe78");
|
||||
|
||||
// check X1
|
||||
assert(gHash.stateCipher == cast(const(ubyte)[])x"5e2ec746917062882c85b0685353deb7");
|
||||
|
||||
ubyte[16] hash;
|
||||
gHash.doFinal(hash);
|
||||
|
||||
assert(hash == x"f38cbb1ad69223dcc3457ae5b6b0f885");
|
||||
}
|
||||
|
||||
// test vectors from
|
||||
// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
// section 2.1.1
|
||||
unittest {
|
||||
|
||||
GHash gHash = GHash(cast(const(ubyte)[])x"73A23D80121DE2D5A850253FCF43120E");
|
||||
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"D609B1F056637A0D46DF998D88E5222A");
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"B2C2846512153524C0895E8108000F10");
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"1112131415161718191A1B1C1D1E1F20");
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"2122232425262728292A2B2C2D2E2F30");
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"313233340001");
|
||||
|
||||
ubyte[16] token;
|
||||
gHash.doFinal(token);
|
||||
|
||||
assert(token == x"1BDA7DB505D8A165264986A703A6920D");
|
||||
}
|
||||
|
||||
// test vectors from
|
||||
// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
// section 2.4.1
|
||||
unittest {
|
||||
|
||||
GHash gHash = GHash(cast(const(ubyte)[])x"E4E01725D724C1215C7309AD34539257");
|
||||
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"E20106D7CD0DF0761E8DCD3D88E54C2A");
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"76D457ED");
|
||||
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"13B4C72B389DC5018E72A171DD85A5D3");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"752274D3A019FBCAED09A425CD9B2E1C");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"9B72EEE7C9DE7D52B3F3");
|
||||
|
||||
ubyte[16] ghash;
|
||||
gHash.doFinal(ghash);
|
||||
|
||||
assert(ghash == x"2A807BDE4AF8A462D467D2FFA3E1D868");
|
||||
}
|
||||
|
||||
// test vectors from
|
||||
// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||
// section 2.8.1
|
||||
unittest {
|
||||
|
||||
GHash gHash = GHash(cast(const(ubyte)[])x"AE19118C3B704FCE42AE0D15D2C15C7A");
|
||||
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"68F2E77696CE7AE8E2CA4EC588E54D00");
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"2E58495C");
|
||||
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"C31F53D99E5687F7365119B832D2AAE7");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"0741D593F1F9E2AB3455779B078EB8FE");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"ACDFEC1F8E3E5277F8180B43361F6512");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"ADB16D2E38548A2C719DBA7228D840");
|
||||
|
||||
ubyte[16] ghash;
|
||||
gHash.doFinal(ghash);
|
||||
|
||||
assert(ghash == x"5AAA6FD11F06A18BE6E77EF2BC18AF93");
|
||||
}
|
||||
|
||||
/// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
|
||||
/// test case 16
|
||||
unittest {
|
||||
|
||||
// init gHash with H = acbef...
|
||||
GHash gHash = GHash(cast(const(ubyte)[])x"acbef20579b4b8ebce889bac8732dad7");
|
||||
|
||||
// process AAD
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef");
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"abaddad2");
|
||||
|
||||
// process cipher data
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662");
|
||||
|
||||
// get the final hash value
|
||||
ubyte[16] ghash;
|
||||
gHash.doFinal(ghash);
|
||||
|
||||
assert(ghash == x"8bd0c4d8aacd391e67cca447e8c38f65");
|
||||
}
|
||||
|
||||
// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
|
||||
// test case 16
|
||||
// AAD and cipher data come out of order
|
||||
unittest {
|
||||
|
||||
GHash gHash = GHash(cast(const(ubyte)[])x"acbef20579b4b8ebce889bac8732dad7");
|
||||
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef");
|
||||
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662");
|
||||
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"abaddad2");
|
||||
|
||||
|
||||
ubyte[16] ghash;
|
||||
gHash.doFinal(ghash);
|
||||
|
||||
assert(ghash == cast(const(ubyte)[])x"8bd0c4d8aacd391e67cca447e8c38f65");
|
||||
|
||||
// gHash should now be resetted, so do the same thing again
|
||||
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef");
|
||||
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa");
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838");
|
||||
|
||||
gHash.updateAAD(cast(const(ubyte)[])x"abaddad2");
|
||||
|
||||
gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662");
|
||||
|
||||
gHash.doFinal(ghash);
|
||||
|
||||
assert(ghash == x"8bd0c4d8aacd391e67cca447e8c38f65");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
493
full/Angel-payload/angel/utils/cryptography/gcm/multiplier.d
Normal file
493
full/Angel-payload/angel/utils/cryptography/gcm/multiplier.d
Normal file
@@ -0,0 +1,493 @@
|
||||
module angel.utils.cryptography.gcm.multiplier;
|
||||
|
||||
package:
|
||||
|
||||
import angel.utils.cryptography.gcm.galoisfield;
|
||||
import std.algorithm: swap;
|
||||
|
||||
// TODO Dynamically make use of intel pclmulqdq instruction for fast multiplication.
|
||||
|
||||
/// test if T is a GCM multiplier
|
||||
@safe
|
||||
template isGCMMultiplier(T)
|
||||
{
|
||||
enum bool isGCMMultiplier =
|
||||
is(T == struct) &&
|
||||
is(typeof(
|
||||
{
|
||||
ubyte[16] block;
|
||||
T m = void;
|
||||
m.init(block);
|
||||
m.multiply(block);
|
||||
}));
|
||||
}
|
||||
|
||||
/// This struct provides schoolbook multiplication in GF(2^128).
|
||||
@safe
|
||||
struct GCMBasicMultiplier
|
||||
{
|
||||
|
||||
private {
|
||||
ubyte[16] H;
|
||||
}
|
||||
|
||||
this(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
init(H);
|
||||
}
|
||||
|
||||
nothrow @nogc {
|
||||
/**
|
||||
* initialize the multiplicator
|
||||
*/
|
||||
void init(in ubyte[] H)
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
this.H[] = H[];
|
||||
}
|
||||
|
||||
/// Multiply x by H and store result in x.
|
||||
///
|
||||
/// Params:
|
||||
/// x = 16 byte block
|
||||
void multiply(ubyte[] x)
|
||||
in {
|
||||
assert(x.length == 16, "x: invalid length.");
|
||||
}
|
||||
body {
|
||||
GF128.multiply(x, H);
|
||||
}
|
||||
}
|
||||
|
||||
/// test multiplication using schoolbook multiplication
|
||||
unittest {
|
||||
|
||||
immutable ubyte[16] testH = cast(immutable ubyte[16]) x"66e94bd4ef8a2c3b884cfa59ca342b2e";
|
||||
ubyte[16] X1 = cast(immutable ubyte[16]) x"0388dace60b6a392f328c2b971b2fe78";
|
||||
|
||||
GCMBasicMultiplier mult = GCMBasicMultiplier(testH);
|
||||
|
||||
mult.multiply(X1);
|
||||
|
||||
assert(X1 == x"5e2ec746917062882c85b0685353deb7", "GF128 multiplication with 8k table failed!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// This struct provides table driven multiplication in GF(2^128).
|
||||
@safe
|
||||
struct GCMMultiplier8kTable
|
||||
{
|
||||
|
||||
private {
|
||||
ubyte[16][16][32] M;
|
||||
}
|
||||
|
||||
this(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
init(H);
|
||||
}
|
||||
|
||||
nothrow @nogc {
|
||||
/**
|
||||
* initialize the multiplicator
|
||||
*/
|
||||
void init(in ubyte[] H) {
|
||||
tableSetup(H);
|
||||
}
|
||||
|
||||
/// Multiply x by H and store result in x.
|
||||
///
|
||||
/// Params:
|
||||
/// x = 16 byte block
|
||||
void multiply(ubyte[] x)
|
||||
in {
|
||||
assert(x.length == 16, "x: invalid length.");
|
||||
}
|
||||
body {
|
||||
|
||||
ubyte[16] z;
|
||||
|
||||
for(uint i = 0; i < 16; ++i) {
|
||||
z[] ^= M[2*i][x[i]>>4][];
|
||||
z[] ^= M[2*i+1][x[i]&0xF][];
|
||||
}
|
||||
|
||||
x[] = z[];
|
||||
}
|
||||
}
|
||||
|
||||
/// test multiplication using 8k table
|
||||
unittest {
|
||||
|
||||
immutable ubyte[16] H = cast(immutable ubyte[16]) x"66e94bd4ef8a2c3b884cfa59ca342b2e";
|
||||
ubyte[16] X1 = cast(immutable ubyte[16]) x"0388dace60b6a392f328c2b971b2fe78";
|
||||
|
||||
GCMMultiplier8kTable mult = GCMMultiplier8kTable(H);
|
||||
|
||||
mult.multiply(X1);
|
||||
|
||||
assert(X1 == x"5e2ec746917062882c85b0685353deb7", "GF128 multiplication with 8k table failed!");
|
||||
}
|
||||
|
||||
private void tableSetup(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
ubyte[16] Pi;
|
||||
Pi[0] = 0x80;
|
||||
ubyte[1] oneByte;
|
||||
for(int i = 0; i < 32; ++i) {
|
||||
for(uint j = 0; j < 16; ++j) {
|
||||
M[i][j] = H;
|
||||
oneByte[0] = cast(ubyte) (j<<4);
|
||||
GF128.multiply(M[i][j], oneByte);
|
||||
GF128.multiply(M[i][j], Pi);
|
||||
}
|
||||
multiplyP4(Pi);
|
||||
}
|
||||
}
|
||||
|
||||
private void multiplyP4(ubyte[] x) nothrow @nogc {
|
||||
foreach(i;0..4){
|
||||
GF128.multiplyP(x);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// This class provides table driven multiplication in GF(2^128).
|
||||
/// The 64k table is rather large and probably won't fit into the cache.
|
||||
/// Use the 8k table to avoid timing based leaks.
|
||||
@safe
|
||||
struct GCMMultiplier64kTable
|
||||
{
|
||||
|
||||
private {
|
||||
ubyte[16][256][16] M;
|
||||
}
|
||||
|
||||
this(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
init(H);
|
||||
}
|
||||
|
||||
nothrow @nogc {
|
||||
|
||||
/// initialize the multiplicator
|
||||
void init(in ubyte[] H) {
|
||||
tableSetup(H);
|
||||
}
|
||||
|
||||
/// Multiply x by H and store result in x.
|
||||
///
|
||||
/// Params:
|
||||
/// x = 16 byte block
|
||||
void multiply(ubyte[] x)
|
||||
in {
|
||||
assert(x.length == 16, "x: invalid length.");
|
||||
}
|
||||
body {
|
||||
|
||||
ubyte[16] z;
|
||||
|
||||
for(uint i = 0; i < 16; ++i) {
|
||||
z[] ^= M[i][x[i]][];
|
||||
}
|
||||
|
||||
x[] = z[];
|
||||
}
|
||||
}
|
||||
|
||||
/// test multiplication using 64k table
|
||||
unittest {
|
||||
immutable ubyte[16] H = cast(immutable ubyte[16]) x"66e94bd4ef8a2c3b884cfa59ca342b2e";
|
||||
ubyte[16] X1 = cast(immutable ubyte[16]) x"0388dace60b6a392f328c2b971b2fe78";
|
||||
|
||||
GCMMultiplier64kTable mult = GCMMultiplier64kTable(H);
|
||||
|
||||
mult.multiply(X1);
|
||||
|
||||
assert(X1 == x"5e2ec746917062882c85b0685353deb7", "GF128 multiplication with 64k table failed!");
|
||||
}
|
||||
|
||||
private void tableSetup(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
ubyte[16] P;
|
||||
P[0] = 0x80;
|
||||
ubyte[1] oneByte;
|
||||
for(int i = 0; i < 16; ++i) {
|
||||
for(uint j = 0; j <= 255; ++j) {
|
||||
M[i][j] = H;
|
||||
oneByte[0] = cast(ubyte) j;
|
||||
GF128.multiply(M[i][j], oneByte);
|
||||
GF128.multiply(M[i][j], P);
|
||||
}
|
||||
GF128.multiplyP8(P);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// This struct provides hardware accelerated multiplication in GF(2^128)
|
||||
/// using the Intel PCLMULQDQ instruction.
|
||||
///
|
||||
/// See: https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf
|
||||
@safe
|
||||
struct GCMPCLMULQDQMultiplier
|
||||
{
|
||||
|
||||
private {
|
||||
ubyte[16] H;
|
||||
}
|
||||
|
||||
this(in ubyte[] H) nothrow @nogc
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
init(H);
|
||||
}
|
||||
|
||||
nothrow @nogc {
|
||||
/**
|
||||
* initialize the multiplicator
|
||||
*/
|
||||
void init(in ubyte[] H)
|
||||
in {
|
||||
assert(H.length == 16, "H: invalid length");
|
||||
}
|
||||
body {
|
||||
this.H[] = H[];
|
||||
}
|
||||
|
||||
/// Multiply x by H and store result in x.
|
||||
///
|
||||
/// Params:
|
||||
/// x = 16 byte block
|
||||
void multiply(ubyte[] x)
|
||||
in {
|
||||
assert(x.length == 16, "x: invalid length.");
|
||||
}
|
||||
body {
|
||||
//GF128.multiply(x, H);
|
||||
gfmul(x, H);
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplies a with b, result is stored in a.
|
||||
@trusted
|
||||
private void gfmul(ubyte[] a, in ubyte[] b) nothrow @nogc
|
||||
in {
|
||||
assert(a.length == 16, "Invalid length of input. Must be 16 bytes.");
|
||||
assert(b.length == 16, "Invalid length of input. Must be 16 bytes.");
|
||||
}
|
||||
body {
|
||||
auto aLength = a.length;
|
||||
foreach (i; 0 .. aLength / 2) {
|
||||
swap(a[i], a[aLength - 1 - i]);
|
||||
}
|
||||
ubyte[16] revB = b;
|
||||
foreach (i; 0 .. revB.length / 2) {
|
||||
auto bLen = revB.length;
|
||||
swap(revB[i], revB[bLen - 1 - i]);
|
||||
}
|
||||
|
||||
version(D_InlineAsm_X86_64) {
|
||||
__vector(ubyte[16]) va = *cast(__vector(ubyte[16])*)a.ptr;
|
||||
__vector(ubyte[16]) vb = *cast(__vector(ubyte[16])*)revB.ptr;
|
||||
|
||||
__vector(ubyte[16]) r0 = __pclmulqdq(va, vb, 0x00); // a0 * b0
|
||||
__vector(ubyte[16]) r1 = __pclmulqdq(va, vb, 0x10); // a0 * b1
|
||||
__vector(ubyte[16]) r2 = __pclmulqdq(va, vb, 0x01); // a1 * b0
|
||||
__vector(ubyte[16]) r3 = __pclmulqdq(va, vb, 0x11); // a1 * b1
|
||||
|
||||
__vector(ubyte[16]) t1 = r1 ^ r2;
|
||||
__vector(ubyte[16]) t2 = __shiftright(t1, 8);
|
||||
__vector(ubyte[16]) t3 = __shiftleft(t1, 8);
|
||||
__vector(ubyte[16]) t4 = r0 ^ t2;
|
||||
__vector(ubyte[16]) t5 = r3 ^ t3;
|
||||
|
||||
__vector(ubyte[16]) t6 = __shiftleft(t4, 1);
|
||||
__vector(ubyte[16]) t7 = __shiftleft(t5, 1);
|
||||
__vector(ubyte[16]) t8 = __shiftright(t4, 31);
|
||||
__vector(ubyte[16]) t9 = __shiftright(t5, 31);
|
||||
__vector(ubyte[16]) t10 = __shiftleft(t8, 4);
|
||||
__vector(ubyte[16]) t11 = __shiftleft(t9, 4);
|
||||
__vector(ubyte[16]) t12 = __shiftright(t8, 12);
|
||||
__vector(ubyte[16]) t13 = t6 ^ t10;
|
||||
__vector(ubyte[16]) t14 = t7 ^ t11;
|
||||
__vector(ubyte[16]) t15 = t14 ^ t12;
|
||||
|
||||
*cast(__vector(ubyte[16])*)a.ptr = t13 ^ t15;
|
||||
}
|
||||
|
||||
foreach (i; 0 .. a.length / 2) {
|
||||
auto aLen = cast(int)a.length;
|
||||
swap(a[i], a[aLen - 1 - i]);
|
||||
}
|
||||
}
|
||||
|
||||
// test pclmulqdq instruction with multiplication by 1
|
||||
@trusted
|
||||
unittest {
|
||||
import core.cpuid;
|
||||
version(D_InlineAsm_X86_64) {
|
||||
if(aes) {
|
||||
|
||||
ubyte[16] a = cast(const ubyte[16]) x"12345678000000000000000000000000";
|
||||
ubyte[16] b = cast(const ubyte[16]) x"01000000000000000000000000000000";
|
||||
ubyte[16] c;
|
||||
|
||||
asm {
|
||||
movdqu xmm1, [RBP + a];
|
||||
movdqu xmm3, [EBP + b];
|
||||
|
||||
db 0x66, 0x0f, 0x3a, 0x44, 0xd9, 0x00; // pclmulqdq xmm3, xmm1, 0x00; // xmm3 holds a0*b0
|
||||
|
||||
movdqu [EBP + c], xmm3;
|
||||
}
|
||||
|
||||
assert(c == x"12345678000000000000000000000000");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// test pclmulqdq instruction with test vectors from
|
||||
/// https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf
|
||||
@trusted
|
||||
unittest {
|
||||
import core.cpuid;
|
||||
|
||||
version(D_InlineAsm_X86_64) {
|
||||
if(aes) {
|
||||
|
||||
/// Python code to convert test vectors into little endian format.
|
||||
/// Reverses the string by bytes (not by hexits):
|
||||
///
|
||||
/// import binascii
|
||||
/// def conv(xmmstr):
|
||||
/// bytearr=bytearray.fromhex(xmmstr)[::-1]
|
||||
/// return binascii.hexlify(bytearr)
|
||||
///
|
||||
/// conv('7b5b54657374566563746f725d53475d')
|
||||
/// conv('48692853686179295b477565726f6e5d')
|
||||
/// conv('1d4d84c85c3440c0929633d5d36f0451')
|
||||
///
|
||||
|
||||
ubyte[16] a = cast(const ubyte[16]) x"5d47535d726f74636556747365545b7b"; // xxm1 high: 7b5b546573745665 low: 63746f725d53475d
|
||||
ubyte[16] b = cast(const ubyte[16]) x"5d6e6f726575475b2979616853286948"; // 4869285368617929 5b477565726f6e5d
|
||||
ubyte[16] c;
|
||||
|
||||
asm {
|
||||
movdqu xmm1, [RBP + a];
|
||||
movdqu xmm3, [EBP + b];
|
||||
|
||||
db 0x66, 0x0f, 0x3a, 0x44, 0xd9, 0x00; // pclmulqdq xmm3, xmm1, 0x00; // xmm3 holds a0*b0
|
||||
|
||||
movdqu [EBP + c], xmm3;
|
||||
}
|
||||
assert(c == x"51046fd3d5339692c040345cc8844d1d");
|
||||
|
||||
asm {
|
||||
movdqu xmm1, [RBP + a];
|
||||
movdqu xmm3, [EBP + b];
|
||||
|
||||
db 0x66, 0x0f, 0x3a, 0x44, 0xd9, 0x01;
|
||||
|
||||
movdqu [EBP + c], xmm3;
|
||||
}
|
||||
assert(c == x"1513282aac40a57fa1b56a558d7cd11b");
|
||||
|
||||
asm {
|
||||
movdqu xmm1, [RBP + a];
|
||||
movdqu xmm3, [EBP + b];
|
||||
|
||||
db 0x66, 0x0f, 0x3a, 0x44, 0xd9, 0x10;
|
||||
|
||||
movdqu [EBP + c], xmm3;
|
||||
}
|
||||
assert(c == x"c9d5b7f42d26bfba2f86303adbf62b1a");
|
||||
|
||||
asm {
|
||||
movdqu xmm1, [RBP + a];
|
||||
movdqu xmm3, [EBP + b];
|
||||
|
||||
db 0x66, 0x0f, 0x3a, 0x44, 0xd9, 0x11;
|
||||
|
||||
movdqu [EBP + c], xmm3;
|
||||
}
|
||||
assert(c == x"edd40f413ee06ed6457c2e592c1f1e1d");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /// test hardware accelerated multiplication (pclmulqdq)
|
||||
// unittest {
|
||||
//
|
||||
// immutable ubyte[16] H = cast(immutable ubyte[16]) x"00000000000000000000000000000080"; // neutral element
|
||||
// ubyte[16] X1 = cast(immutable ubyte[16]) x"0388dace60b6a392f328c2b971b2fe78";
|
||||
//
|
||||
// GCMPCLMULQDQMultiplier mult = GCMPCLMULQDQMultiplier(H);
|
||||
//
|
||||
// mult.multiply(X1);
|
||||
//
|
||||
// assert(X1 == x"0388dace60b6a392f328c2b971b2fe78", "GF128 multiplication with pclmulqdq failed!");
|
||||
// }
|
||||
|
||||
/// test hardware accelerated multiplication (pclmulqdq)
|
||||
unittest {
|
||||
|
||||
import std.algorithm: reverse;
|
||||
|
||||
ubyte[16] testH = cast(immutable ubyte[16]) x"952b2a56a5604ac0b32b6656a05b40b6";
|
||||
ubyte[16] X1 = cast(immutable ubyte[16]) x"dfa6bf4ded81db03ffcaff95f830f061";
|
||||
|
||||
ubyte[16] expected = cast(immutable ubyte[16]) x"da53eb0ad2c55bb64fc4802cc3feda60";
|
||||
|
||||
// reverse(H[]);
|
||||
// reverse(X1[]);
|
||||
// reverse(expected[]);
|
||||
|
||||
//GCMMultiplier8kTable mult = GCMMultiplier8kTable(H);
|
||||
GCMPCLMULQDQMultiplier mult = GCMPCLMULQDQMultiplier(testH);
|
||||
|
||||
mult.multiply(X1);
|
||||
|
||||
assert(X1 == expected, "GF128 multiplication with pclmulqdq failed!");
|
||||
}
|
||||
|
||||
// /// test hardware accelerated multiplication (pclmulqdq)
|
||||
// unittest {
|
||||
//
|
||||
// ulong[2] H = [0xb32b6656a05b40b6, 0x952b2a56a5604ac0];
|
||||
// ulong[2] X1 = [0xffcaff95f830f061, 0xdfa6bf4ded81db03];
|
||||
//
|
||||
// ulong[2] expected = [0x4fc4802cc3feda60, 0xda53eb0ad2c55bb6];
|
||||
//
|
||||
// //GCMMultiplier8kTable mult = GCMMultiplier8kTable(H);
|
||||
// GCMPCLMULQDQMultiplier mult = GCMPCLMULQDQMultiplier(cast(ubyte[16])H);
|
||||
//
|
||||
// mult.multiply(cast(ubyte[16])X1);
|
||||
//
|
||||
// assert(X1 == expected, "GF128 multiplication with pclmulqdq failed!");
|
||||
// }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user