1 module dcrypt.aead.aead; 2 3 public import dcrypt.blockcipher.blockcipher; 4 5 /// 6 /// Test if T is a AEAD cipher. 7 /// 8 @safe 9 template isAEADCipher(T) 10 { 11 enum bool isAEADCipher = 12 is(T == struct) && 13 is(typeof( 14 { 15 ubyte[0] block; 16 T bc = void; //Can define 17 18 bc.start(true, block, block); // start with key, iv 19 20 string name = T.name; 21 uint macSize = T.macSize; 22 23 //BlockCipher c = bc.getUnderlyingCipher(); 24 bc.processAADBytes(cast (const ubyte[])block); 25 26 ubyte[] slice = bc.processBytes(cast(const ubyte[]) [0], cast(ubyte[]) [0]); 27 //ubyte[] mac = bc.finish(block); 28 29 size_t len = bc.finish(cast(ubyte[]) [0], cast(ubyte[]) [0]); 30 size_t s1 = bc.getUpdateOutputSize(cast(size_t) 0); 31 size_t s2 = bc.getOutputSize(cast(size_t) 0); 32 })); 33 } 34 35 @safe 36 public interface IAEADEngine 37 { 38 39 public { 40 41 /// Initialize the underlying cipher. 42 /// Params: 43 /// forEncryption = true if we are setting up for encryption, false otherwise. 44 /// key = Secret key. 45 /// nonce = Number used only once. 46 void start(bool forEncryption, in ubyte[] key, in ubyte[] nonce) nothrow @nogc; 47 48 /// Returns: Returns the name of the algorithm. 49 @property 50 string name() pure nothrow; 51 52 53 /// Process additional authenticated data. 54 void processAADBytes(in ubyte[] aad) nothrow; 55 56 /// Encrypt or decrypt a block of bytes. 57 /// 58 /// Params: 59 /// input = Input buffer. 60 /// output = Output buffer. 61 /// 62 /// Returns: A slice pointing to the output data. 63 ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow; 64 65 /// Close the AEAD cipher by producing the remaining output and a authentication tag. 66 /// 67 /// Params: 68 /// macBuf = Buffer for the MAC tag. 69 /// output = Buffer for remaining output data. 70 /// 71 /// Note: In decryption mode this does not verify the integrity of the data. Verification has to be done by the programmer! 72 /// 73 size_t finish(ubyte[] macBuf, ubyte[] output); 74 75 /// Returns: Return the size of the output buffer required for a processBytes an input of len bytes. 76 size_t getUpdateOutputSize(size_t len) nothrow const; 77 78 /// Returns: Return the size of the output buffer required for a processBytes plus a finish with an input of len bytes. 79 size_t getOutputSize(size_t len) nothrow const; 80 81 } 82 } 83 84 // TODO AEAD cipher wrapper 85 /// Wrapper class for AEAD ciphers 86 @safe 87 public class AEADCipherWrapper(T) if(isAEADCipher!T): IAEADEngine 88 { 89 90 private T cipher; 91 92 public { 93 94 void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) { 95 cipher.start(forEncryption, key, iv); 96 } 97 98 @property 99 string name() pure nothrow { 100 return cipher.name; 101 } 102 103 void processAADBytes(in ubyte[] aad) nothrow { 104 cipher.processAADBytes(aad); 105 } 106 107 108 ubyte[] processBytes(in ubyte[] input, ubyte[] output) nothrow { 109 return cipher.processBytes(input, output); 110 } 111 112 size_t finish(ubyte[] macBuf, ubyte[] output){ 113 return cipher.finish(macBuf, output); 114 } 115 116 size_t getUpdateOutputSize(size_t len) nothrow const { 117 return cipher.getUpdateOutputSize(len); 118 } 119 120 size_t getOutputSize(size_t len) nothrow const { 121 return cipher.getOutputSize(len); 122 } 123 } 124 } 125 126 127 128 version(unittest) { 129 130 // unittest helper functions 131 132 133 /// Runs decryption and encryption using AEADCipher cipher with given keys, plaintexts, and ciphertexts. 134 /// 135 /// Params: 136 /// hexKeys = the keys encoded in hex 137 /// hexIVs = hex encoded nonces 138 /// hexPlaintexts = the plaintexts encoded in hex 139 /// hexAAD = additional authenticated data 140 /// hexCiphertexts = the corresponding ciphertexts in hex 141 /// macSize = MAC sizes in bits 142 /// 143 /// Throws: 144 /// AssertionError if encryption or decryption failed 145 @safe 146 public void AEADCipherTest( 147 IAEADEngine cipher, 148 in string[] keys, 149 in string[] ivs, 150 in string[] plaintexts, 151 in string[] aads, 152 in string[] ciphertexts, 153 in uint[] macSize 154 ) { 155 156 import dcrypt.aead.aead; 157 import std.format: format; 158 159 alias const (ubyte)[] octets; 160 161 foreach (uint i, string test_key; keys) 162 { 163 octets plain = cast(octets) plaintexts[i]; 164 octets aad = cast(octets) aads[i]; 165 octets ciphertext = cast(octets) ciphertexts[i]; 166 167 ubyte[] output = new ubyte[plain.length]; 168 169 // set to encryption mode 170 cipher.start(true, cast(octets) test_key, cast(octets) ivs[i]); 171 172 output.length = cipher.getOutputSize(plain.length); 173 174 immutable size_t taglen = macSize[i]/8; 175 octets expectedMac = ciphertext[$-taglen..$]; 176 ciphertext = ciphertext[0..$-taglen]; 177 178 // assert(cipher.getUpdateOutputSize(plain.length) == plain.length); 179 assert(output.length >= cipher.getUpdateOutputSize(plain.length)); 180 181 182 assert(output.length >= cipher.getUpdateOutputSize(plain.length)); 183 184 // test encryption 185 cipher.processAADBytes(aad); 186 ubyte[] out_slice = cipher.processBytes(plain, output); 187 188 ubyte[16] mac; 189 size_t len = out_slice.length+cipher.finish(mac, output[out_slice.length..$]); 190 191 assert(output == ciphertext, 192 format("%s encrypt: %(%.2x%) != %(%.2x%)", cipher.name, output, ciphertexts[i])); 193 194 assert(mac[0..taglen] == expectedMac); 195 196 } 197 } 198 }