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