1 module dcrypt.crypto.modes.aead; 2 3 public import dcrypt.crypto.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 size_t outLen = bc.processBytes(cast(const ubyte[]) [0], cast(ubyte[]) [0]); 26 // TODO: ubyte[] slice = bc.processBytes(cast(const ubyte[]) [0], cast(ubyte[]) [0]); 27 //bc.doFinal(cast(const ubyte[]) [0]); 28 // TODO: ubyte[] mac = finish(block); 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 //bc.reset(); 33 })); 34 } 35 36 @safe 37 public interface AEADCipher 38 { 39 40 public { 41 /** 42 * initialize the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object. 43 * Params: 44 * forEncryption = true if we are setting up for encryption, false otherwise. 45 * params = the necessary parameters for the underlying cipher to be initialised. 46 * macSize = Size of mac tag in bits. 47 */ 48 void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) nothrow @nogc; 49 50 /** 51 * Return the name of the algorithm. 52 * 53 * Returns: the algorithm name. 54 */ 55 @property 56 string name() pure nothrow; 57 58 59 /** 60 * Add a sequence of bytes to the associated data check. 61 * <br>If the implementation supports it, this will be an online operation and will not retain the associated data. 62 * 63 * Params: in = the input byte array. 64 */ 65 void processAADBytes(in ubyte[] aad) nothrow; 66 67 /** 68 * process a block of bytes from in putting the result into out. 69 * Params: 70 * in = the input byte array. 71 * out = the output buffer the processed bytes go into. 72 * Returns: the number of bytes written to out. 73 * Throws: Error if the output buffer is too small. 74 */ 75 size_t processBytes(in ubyte[] input, ubyte[] output) nothrow; 76 77 /** 78 * Finish the operation either appending or verifying the MAC at the end of the data. 79 * 80 * Params: 81 * out = space for any resulting output data. 82 * macBuf = Buffer for MAC tag. 83 * Returns: number of bytes written into out. 84 * Throws: IllegalStateError = if the cipher is in an inappropriate state. 85 * dcrypt.exceptions.InvalidCipherTextException = if the MAC fails to match. 86 */ 87 size_t doFinal(ubyte[] macBuf, ubyte[] output); 88 89 90 /** 91 * return the size of the output buffer required for a processBytes 92 * an input of len bytes. 93 * 94 * Params: len = the length of the input. 95 * Returns: the space required to accommodate a call to processBytes 96 * with len bytes of input. 97 */ 98 size_t getUpdateOutputSize(size_t len) nothrow; 99 100 /** 101 * return the size of the output buffer required for a processBytes plus a 102 * doFinal with an input of len bytes. 103 * 104 * Params: 105 * len = the length of the input. 106 * Returns: the space required to accommodate a call to processBytes and doFinal 107 * with len bytes of input. 108 */ 109 size_t getOutputSize(size_t len) nothrow; 110 111 } 112 } 113 114 // TODO AEAD cipher wrapper 115 /// Wrapper class for AEAD ciphers 116 @safe 117 public class AEADCipherWrapper(T) if(isAEADCipher!T): AEADCipher 118 { 119 120 private T cipher; 121 122 public { 123 124 // /// Params: c = underlying block cipher 125 // this(BlockCipher c) { 126 // cipher = T(c); 127 // } 128 129 /** 130 * initialize the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object. 131 * Params: 132 * forEncryption = true if we are setting up for encryption, false otherwise. 133 * params = the necessary parameters for the underlying cipher to be initialised. 134 */ 135 void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) { 136 cipher.start(forEncryption, key, iv); 137 } 138 139 /** 140 * Return the name of the algorithm. 141 * 142 * Returns: the algorithm name. 143 */ 144 @property 145 string name() pure nothrow { 146 return cipher.name; 147 } 148 149 150 /** 151 * Add a sequence of bytes to the associated data check. 152 * If the implementation supports it, this will be an online operation and will not retain the associated data. 153 * 154 * Params: in = the input byte array. 155 */ 156 void processAADBytes(in ubyte[] aad) nothrow { 157 cipher.processAADBytes(aad); 158 } 159 160 /** 161 * process a block of bytes from in putting the result into out. 162 * Params: 163 * in = the input byte array. 164 * out = the output buffer the processed bytes go into. 165 * Returns: the number of bytes written to out. 166 * Throws: Error if the output buffer is too small. 167 */ 168 size_t processBytes(in ubyte[] input, ubyte[] output) nothrow { 169 return cipher.processBytes(input, output); 170 } 171 172 /** 173 * Finish the operation. Does not verify the mac. 174 * 175 * Params: 176 * out = space for any resulting output data. 177 * macBuf = Buffer for MAC tag. 178 * Returns: number of bytes written into out. 179 * Throws: IllegalStateError = if the cipher is in an inappropriate state. 180 */ 181 size_t doFinal(ubyte[] macBuf, ubyte[] output){ 182 return cipher.finish(macBuf, output); 183 } 184 185 /** 186 * return the size of the output buffer required for a processBytes 187 * an input of len bytes. 188 * 189 * Params: len = the length of the input. 190 * Returns: the space required to accommodate a call to processBytes 191 * with len bytes of input. 192 */ 193 size_t getUpdateOutputSize(size_t len) nothrow { 194 return cipher.getUpdateOutputSize(len); 195 } 196 197 /** 198 * return the size of the output buffer required for a processBytes plus a 199 * doFinal with an input of len bytes. 200 * 201 * Params: 202 * len = the length of the input. 203 * Returns: the space required to accommodate a call to processBytes and doFinal 204 * with len bytes of input. 205 */ 206 size_t getOutputSize(size_t len) nothrow { 207 return cipher.getOutputSize(len); 208 } 209 } 210 } 211 212 213 214 version(unittest) { 215 216 // unittest helper functions 217 218 219 /// Runs decryption and encryption using AEADCipher cipher with given keys, plaintexts, and ciphertexts. 220 /// 221 /// Params: 222 /// hexKeys = the keys encoded in hex 223 /// hexIVs = hex encoded nonces 224 /// hexPlaintexts = the plaintexts encoded in hex 225 /// hexAAD = additional authenticated data 226 /// hexCiphertexts = the corresponding ciphertexts in hex 227 /// macSize = MAC sizes in bits 228 /// 229 /// Throws: 230 /// AssertionError if encryption or decryption failed 231 @safe 232 public void AEADCipherTest( 233 AEADCipher cipher, 234 in string[] hexKeys, 235 in string[] hexIVs, 236 in string[] hexPlaintexts, 237 in string[] hexAAD, 238 in string[] hexCipherTexts, 239 in uint[] macSize 240 ) { 241 242 import dcrypt.crypto.modes.aead; 243 import dcrypt.encoders.hex; 244 import std.conv: text; 245 246 foreach (uint i, string test_key; hexKeys) 247 { 248 ubyte[] plain = hexDecode(hexPlaintexts[i]); 249 ubyte[] aad = hexDecode(hexAAD[i]); 250 ubyte[] ciphertext = hexDecode(hexCipherTexts[i]); 251 252 ubyte[] output = new ubyte[plain.length]; 253 254 // set to encryption mode 255 cipher.start(true, hexDecode(test_key), hexDecode(hexIVs[i])); 256 257 output.length = cipher.getOutputSize(plain.length); 258 259 immutable size_t taglen = macSize[i]/8; 260 ubyte[] expectedMac = ciphertext[$-taglen..$]; 261 ciphertext = ciphertext[0..$-taglen]; 262 263 // assert(cipher.getUpdateOutputSize(plain.length) == plain.length); 264 assert(output.length >= cipher.getUpdateOutputSize(plain.length)); 265 266 267 assert(output.length >= cipher.getUpdateOutputSize(plain.length)); 268 269 // test encryption 270 cipher.processAADBytes(aad); 271 size_t offset = cipher.processBytes(plain, output); 272 273 ubyte[16] mac; 274 size_t len = offset+cipher.doFinal(mac, output[offset..$]); 275 276 assert(output == ciphertext, 277 text(cipher.name~" encrypt: (",hexEncode(output),") != ("~hexCipherTexts[i]~")")); 278 279 assert(mac[0..taglen] == expectedMac); 280 281 } 282 } 283 }