198 lines
5.1 KiB
D
198 lines
5.1 KiB
D
module angel.utils.cryptography.aead;
|
|
|
|
public import angel.utils.cryptography.blockcipher;
|
|
|
|
///
|
|
/// Test if T is a AEAD cipher.
|
|
///
|
|
@safe
|
|
template isAEADCipher(T)
|
|
{
|
|
enum bool isAEADCipher =
|
|
is(T == struct) &&
|
|
is(typeof(
|
|
{
|
|
ubyte[0] block;
|
|
T bc = void; //Can define
|
|
|
|
bc.start(true, block, block); // start with key, iv
|
|
|
|
string name = T.name;
|
|
uint macSize = T.macSize;
|
|
|
|
//BlockCipher c = bc.getUnderlyingCipher();
|
|
bc.processAADBytes(cast (const ubyte[])block);
|
|
|
|
ubyte[] slice = bc.processBytes(cast(const ubyte[]) [0], cast(ubyte[]) [0]);
|
|
//ubyte[] mac = bc.finish(block);
|
|
|
|
size_t len = bc.finish(cast(ubyte[]) [0], cast(ubyte[]) [0]);
|
|
size_t s1 = bc.getUpdateOutputSize(cast(size_t) 0);
|
|
size_t s2 = bc.getOutputSize(cast(size_t) 0);
|
|
}));
|
|
}
|
|
|
|
@safe
|
|
public interface IAEADEngine
|
|
{
|
|
|
|
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[] nonce) nothrow @nogc;
|
|
|
|
/// Returns: Returns the name of the algorithm.
|
|
@property
|
|
string name() pure nothrow;
|
|
|
|
|
|
/// Process additional authenticated data.
|
|
void processAADBytes(in ubyte[] aad) nothrow;
|
|
|
|
/// Encrypt or decrypt a block of bytes.
|
|
///
|
|
/// Params:
|
|
/// input = Input buffer.
|
|
/// output = Output buffer.
|
|
///
|
|
/// Returns: A slice pointing to the output data.
|
|
ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow;
|
|
|
|
/// Close the AEAD cipher by producing the remaining output and a authentication tag.
|
|
///
|
|
/// Params:
|
|
/// macBuf = Buffer for the MAC tag.
|
|
/// output = Buffer for remaining output data.
|
|
///
|
|
/// Note: In decryption mode this does not verify the integrity of the data. Verification has to be done by the programmer!
|
|
///
|
|
size_t finish(ubyte[] macBuf, ubyte[] output);
|
|
|
|
/// Returns: Return the size of the output buffer required for a processBytes an input of len bytes.
|
|
size_t getUpdateOutputSize(size_t len) nothrow const;
|
|
|
|
/// 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 const;
|
|
|
|
}
|
|
}
|
|
|
|
// TODO AEAD cipher wrapper
|
|
/// Wrapper class for AEAD ciphers
|
|
@safe
|
|
public class AEADCipherWrapper(T) if(isAEADCipher!T): IAEADEngine
|
|
{
|
|
|
|
private T cipher;
|
|
|
|
public {
|
|
|
|
void start(in ubyte[] key, in ubyte[] iv) {
|
|
cipher.start(key, iv);
|
|
}
|
|
|
|
@property
|
|
string name() pure nothrow {
|
|
return cipher.name;
|
|
}
|
|
|
|
void processAADBytes(in ubyte[] aad) nothrow {
|
|
cipher.processAADBytes(aad);
|
|
}
|
|
|
|
|
|
ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
version(unittest) {
|
|
|
|
// unittest helper functions
|
|
|
|
|
|
/// Runs decryption and encryption using AEADCipher cipher with given keys, plaintexts, and ciphertexts.
|
|
///
|
|
/// Params:
|
|
/// hexKeys = the keys encoded in hex
|
|
/// hexIVs = hex encoded nonces
|
|
/// hexPlaintexts = the plaintexts encoded in hex
|
|
/// hexAAD = additional authenticated data
|
|
/// hexCiphertexts = the corresponding ciphertexts in hex
|
|
/// macSize = MAC sizes in bits
|
|
///
|
|
/// Throws:
|
|
/// AssertionError if encryption or decryption failed
|
|
@safe
|
|
public void AEADCipherTest(
|
|
IAEADEngine cipher,
|
|
in string[] keys,
|
|
in string[] ivs,
|
|
in string[] plaintexts,
|
|
in string[] aads,
|
|
in string[] ciphertexts,
|
|
in uint[] macSize
|
|
) {
|
|
|
|
import dcrypt.aead.aead;
|
|
import std.format: format;
|
|
|
|
alias const (ubyte)[] octets;
|
|
|
|
foreach (uint i, string test_key; keys)
|
|
{
|
|
octets plain = cast(octets) plaintexts[i];
|
|
octets aad = cast(octets) aads[i];
|
|
octets ciphertext = cast(octets) ciphertexts[i];
|
|
|
|
ubyte[] output = new ubyte[plain.length];
|
|
|
|
// set to encryption mode
|
|
cipher.start(true, cast(octets) test_key, cast(octets) ivs[i]);
|
|
|
|
output.length = cipher.getOutputSize(plain.length);
|
|
|
|
immutable size_t taglen = macSize[i]/8;
|
|
octets expectedMac = ciphertext[$-taglen..$];
|
|
ciphertext = ciphertext[0..$-taglen];
|
|
|
|
// assert(cipher.getUpdateOutputSize(plain.length) == plain.length);
|
|
assert(output.length >= cipher.getUpdateOutputSize(plain.length));
|
|
|
|
|
|
assert(output.length >= cipher.getUpdateOutputSize(plain.length));
|
|
|
|
// test encryption
|
|
cipher.processAADBytes(aad);
|
|
ubyte[] out_slice = cipher.processBytes(plain, output);
|
|
|
|
ubyte[16] mac;
|
|
size_t len = out_slice.length+cipher.finish(mac, output[out_slice.length..$]);
|
|
|
|
assert(output == ciphertext,
|
|
format("%s encrypt: %(%.2x%) != %(%.2x%)", cipher.name, output, ciphertexts[i]));
|
|
|
|
assert(mac[0..taglen] == expectedMac);
|
|
|
|
}
|
|
}
|
|
} |