191 lines
4.8 KiB
D
191 lines
4.8 KiB
D
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));
|
|
}
|
|
}
|
|
} |