1 module tagion.crypto.aes.tiny_aes.tiny_aes; 2 /* 3 4 This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. 5 Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. 6 7 The implementation is verified against the test vectors in: 8 National Institute of Standards and Technology Special Publication 800-38A 2001 ED 9 10 ECB-AES128 11 ---------- 12 13 plain-text: 14 6bc1bee22e409f96e93d7e117393172a 15 ae2d8a571e03ac9c9eb76fac45af8e51 16 30c81c46a35ce411e5fbc1191a0a52ef 17 f69f2445df4f9b17ad2b417be66c3710 18 19 key: 20 2b7e151628aed2a6abf7158809cf4f3c 21 22 resulting cipher 23 3ad77bb40d7a3660a89ecaf32466ef97 24 f5d3d58503b9699de785895a96fdbaaf 25 43b1cd7f598ece23881b00e3ed030688 26 7b0c785e27e8ad3f8223207104725dd4 27 28 29 NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 30 You should pad the end of the string with zeros if this is not the case. 31 For AES192/256 the key size is proportionally larger. 32 33 */ 34 35 //version = PRINT; 36 37 /+ 38 The different modes is explained in 39 https://www.highgo.ca/2019/08/08/the-difference-in-five-modes-in-the-aes-encryption-algorithm/ 40 +/ 41 enum Mode { 42 ECB, /// mode: Electronic Code Book mode 43 CBC, /// mode: Cipher Block Chaining mode 44 CFB, /// mode: Cipher FeedBack mode 45 // OFB, /// mode: Output FeedBack mode 46 CTR /// mode: Counter mode 47 48 } 49 50 @safe @nogc 51 struct Tiny_AES(int KEY_LENGTH, Mode mode = Mode.CBC) { 52 pure nothrow { 53 enum KEY_SIZE = KEY_LENGTH >> 3; 54 enum BLOCK_SIZE = 16; // Block length in bytes - AES is 128b block only 55 56 enum Nb = 4; 57 static if (KEY_LENGTH is 256) { 58 enum Nk = 8; 59 enum Nr = 14; 60 enum keyExpSize = 240; 61 62 } 63 else static if (KEY_LENGTH is 192) { 64 enum Nk = 6; 65 enum Nr = 12; 66 enum keyExpSize = 208; 67 68 } 69 else static if (KEY_LENGTH is 128) { 70 enum Nk = 4; // The number of 32 bit words in a key. 71 enum Nr = 10; // The number of rounds in AES Cipher. 72 enum keyExpSize = 176; 73 } 74 75 struct Context { 76 ubyte[keyExpSize] round_key; 77 static if (mode !is Mode.ECB) { 78 ubyte[BLOCK_SIZE] Iv; 79 } 80 } 81 82 // jcallan@github points out that declaring Multiply as a function 83 // reduces code size considerably with the Keil ARM compiler. 84 // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 85 // #ifndef MULTIPLY_AS_A_FUNCTION 86 // #define MULTIPLY_AS_A_FUNCTION 0 87 // #endif 88 89 /*****************************************************************************/ 90 /* Private variables: */ 91 /*****************************************************************************/ 92 // state - array holding the intermediate results during decryption. 93 alias state_t = ubyte[4][4]; 94 union State { 95 state_t* state_p; 96 protected ubyte* buf_p; 97 @trusted 98 static ref state_t opCall(ref return scope ubyte[] buf) 99 in { 100 assert(buf.length >= state_t.sizeof); 101 } 102 do { 103 State state; 104 state.buf_p = buf.ptr; 105 return *state.state_p; 106 } 107 108 @trusted 109 static ref state_t opCall(ref return scope ubyte[BLOCK_SIZE] buf) { 110 State state; 111 state.buf_p = cast(ubyte*)&buf; 112 return *state.state_p; 113 } 114 115 static assert(state_t.sizeof is BLOCK_SIZE); 116 } 117 118 // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 119 // The numbers below can be computed dynamically trading ROM for RAM - 120 // This can be useful in (embedded) bootloader applications, where ROM is often limited. 121 shared static immutable(ubyte[256]) sbox; 122 shared static immutable(ubyte[256]) rsbox; 123 shared static this() { 124 // The sbox was changed to from enum to immutable becuase of some odd segment fault 125 sbox = [ 126 //0 1 2 3 4 5 6 7 8 9 A B C D E F 127 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 128 0x2b, 0xfe, 0xd7, 0xab, 0x76, 129 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 130 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 131 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 132 0xf1, 0x71, 0xd8, 0x31, 0x15, 133 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 134 0xe2, 0xeb, 0x27, 0xb2, 0x75, 135 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 136 0xb3, 0x29, 0xe3, 0x2f, 0x84, 137 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 138 0x39, 0x4a, 0x4c, 0x58, 0xcf, 139 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 140 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 141 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 142 0x21, 0x10, 0xff, 0xf3, 0xd2, 143 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 144 0x3d, 0x64, 0x5d, 0x19, 0x73, 145 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 146 0x14, 0xde, 0x5e, 0x0b, 0xdb, 147 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 148 0x62, 0x91, 0x95, 0xe4, 0x79, 149 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 150 0xea, 0x65, 0x7a, 0xae, 0x08, 151 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 152 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 153 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 154 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 155 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 156 0xe9, 0xce, 0x55, 0x28, 0xdf, 157 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 158 0x0f, 0xb0, 0x54, 0xbb, 0x16 159 ]; 160 static immutable(ubyte[256]) _reverse_sbox() { 161 ubyte[256] result; 162 static foreach (i; 0 .. 256) { 163 result[sbox[i]] = i; 164 } 165 return result; 166 } 167 // Generate the reverse sbox 168 rsbox = _reverse_sbox(); 169 } 170 171 // The round constant word array, Rcon[i], contains the values given by 172 // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 173 enum Rcon = cast(ubyte[11])[ 174 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 175 ]; 176 177 /* 178 * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), 179 * that you can remove most of the elements in the Rcon array, because they are unused. 180 * 181 * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon 182 * 183 * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 184 * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." 185 */ 186 187 // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 188 private void keyExpansion(ref const(ubyte[KEY_SIZE]) Key) { 189 ubyte[4] tempa; // Used for the column/row operations 190 foreach (i; 0 .. Nk) { 191 static foreach (j; 0 .. 4) { 192 ctx.round_key[(i * 4) + j] = Key[(i * 4) + j]; 193 } 194 } 195 // All other round keys are found from the previous round keys. 196 static foreach (i; Nk .. Nb * (Nr + 1)) { 197 static foreach (j; 0 .. 4) { 198 { 199 const k = (i - 1) * 4; 200 tempa[j] = ctx.round_key[k + j]; 201 } 202 } 203 204 if (i % Nk == 0) { 205 // This function shifts the 4 bytes in a word to the left once. 206 // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 207 208 // Function RotWord() 209 { 210 const ubyte u8tmp = tempa[0]; 211 tempa[0] = tempa[1]; 212 tempa[1] = tempa[2]; 213 tempa[2] = tempa[3]; 214 tempa[3] = u8tmp; 215 } 216 217 // SubWord() is a function that takes a four-byte input word and 218 // applies the S-box to each of the four bytes to produce an output word. 219 220 // Function Subword() 221 static foreach (j; 0 .. 4) { 222 tempa[j] = sbox[tempa[j]]; 223 } 224 225 tempa[0] = tempa[0] ^ Rcon[i / Nk]; 226 } 227 static if (KEY_LENGTH == 256) { 228 if (i % Nk is 4) { 229 // Function Subword() 230 static foreach (j; 0 .. 4) { 231 tempa[j] = sbox[tempa[j]]; 232 } 233 } 234 } 235 { 236 const j = i * 4; 237 const k = (i - Nk) * 4; 238 static foreach (l; 0 .. 4) { 239 ctx.round_key[j + l] = ctx.round_key[k + l] ^ tempa[l]; 240 } 241 } 242 } 243 } 244 245 Context ctx; 246 static if (mode is Mode.ECB) { 247 this(ref const(ubyte[KEY_SIZE]) key) { 248 keyExpansion(key); 249 } 250 } 251 else { 252 this(ref const(ubyte[KEY_SIZE]) key, ref const(ubyte[BLOCK_SIZE]) iv) { 253 keyExpansion(key); 254 ctx.Iv = iv; 255 } 256 257 void iv(ref const(ubyte[BLOCK_SIZE]) iv) { 258 ctx.Iv = iv; 259 } 260 } 261 262 private { 263 // This function adds the round key to state. 264 // The round key is added to the state by an XOR function. 265 void addRoundKey(ubyte round, ref scope state_t state) const { 266 static foreach (i; 0 .. 4) { 267 static foreach (j; 0 .. 4) { 268 state[i][j] ^= ctx.round_key[(round * Nb * 4) + (i * Nb) + j]; 269 } 270 } 271 } 272 273 // The SubBytes Function Substitutes the values in the 274 // state matrix with values in an S-box. 275 static void SubBytes(ref scope state_t state) { 276 static foreach (i; 0 .. 4) { 277 static foreach (j; 0 .. 4) { 278 state[j][i] = sbox[state[j][i]]; 279 } 280 } 281 } 282 283 // The ShiftRows() function shifts the rows in the state to the left. 284 // Each row is shifted with different offset. 285 // Offset = Row number. So the first row is not shifted. 286 static void ShiftRows(ref scope state_t state) { 287 ubyte temp; 288 // Rotate first row 1 columns to left 289 temp = state[0][1]; 290 state[0][1] = state[1][1]; 291 state[1][1] = state[2][1]; 292 state[2][1] = state[3][1]; 293 state[3][1] = temp; 294 295 // Rotate second row 2 columns to left 296 temp = state[0][2]; 297 state[0][2] = state[2][2]; 298 state[2][2] = temp; 299 300 temp = state[1][2]; 301 state[1][2] = state[3][2]; 302 state[3][2] = temp; 303 304 // Rotate third row 3 columns to left 305 temp = state[0][3]; 306 state[0][3] = state[3][3]; 307 state[3][3] = state[2][3]; 308 state[2][3] = state[1][3]; 309 state[1][3] = temp; 310 } 311 312 static ubyte xtime(ubyte x) { 313 return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & ubyte.max; 314 } 315 316 // MixColumns function mixes the columns of the state matrix 317 static void MixColumns(ref scope state_t state) { 318 ubyte Tmp, Tm; 319 static foreach (i; 0 .. 4) { 320 { 321 const t = state[i][0]; 322 Tmp = state[i][0] ^ state[i][1] ^ state[i][2] ^ state[i][3]; 323 Tm = state[i][0] ^ state[i][1]; 324 Tm = xtime(Tm); 325 state[i][0] ^= Tm ^ Tmp; 326 Tm = state[i][1] ^ state[i][2]; 327 Tm = xtime(Tm); 328 state[i][1] ^= Tm ^ Tmp; 329 Tm = state[i][2] ^ state[i][3]; 330 Tm = xtime(Tm); 331 state[i][2] ^= Tm ^ Tmp; 332 Tm = state[i][3] ^ t; 333 Tm = xtime(Tm); 334 state[i][3] ^= Tm ^ Tmp; 335 } 336 } 337 } 338 339 // Multiply is used to multiply numbers in the field GF(2^8) 340 // Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary 341 // The compiler seems to be able to vectorize the operation better this way. 342 // See https://github.com/kokke/tiny-AES-c/pull/34 343 static ubyte Multiply(ubyte x, ubyte y) { 344 return (((y & 1) * x) ^ 345 ((y >> 1 & 1) * xtime(x)) ^ 346 ((y >> 2 & 1) * xtime(xtime(x))) ^ 347 ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ 348 ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); 349 /* this last call to xtime() can be omitted */ 350 } 351 352 // static ubyte getSBoxInvert(ubyte num) {return rsbox[num];}; 353 354 // MixColumns function mixes the columns of the state matrix. 355 // The method used to multiply may be difficult to understand for the inexperienced. 356 // Please use the references to gain more information. 357 static void InvMixColumns(ref scope state_t state) { 358 static foreach (i; 0 .. 4) { 359 { 360 const a = state[i][0]; 361 const b = state[i][1]; 362 const c = state[i][2]; 363 const d = state[i][3]; 364 365 state[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 366 state[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 367 state[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 368 state[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 369 } 370 } 371 } 372 373 // The SubBytes Function Substitutes the values in the 374 // state matrix with values in an S-box. 375 static void InvSubBytes(ref scope state_t state) { 376 static foreach (i; 0 .. 4) { 377 static foreach (j; 0 .. 4) { 378 state[j][i] = rsbox[state[j][i]]; 379 } 380 } 381 } 382 383 static void InvShiftRows(ref scope state_t state) { 384 ubyte temp; 385 386 // Rotate first row 1 columns to right 387 temp = state[3][1]; 388 state[3][1] = state[2][1]; 389 state[2][1] = state[1][1]; 390 state[1][1] = state[0][1]; 391 state[0][1] = temp; 392 393 // Rotate second row 2 columns to right 394 temp = state[0][2]; 395 state[0][2] = state[2][2]; 396 state[2][2] = temp; 397 398 temp = state[1][2]; 399 state[1][2] = state[3][2]; 400 state[3][2] = temp; 401 402 // Rotate third row 3 columns to right 403 temp = state[0][3]; 404 state[0][3] = state[1][3]; 405 state[1][3] = state[2][3]; 406 state[2][3] = state[3][3]; 407 state[3][3] = temp; 408 } 409 410 // Cipher is the main function that encrypts the PlainText. 411 void cipher(ref scope state_t state) { 412 ubyte round = 0; 413 414 // Add the First round key to the state before starting the rounds. 415 addRoundKey(0, state); 416 417 // There will be Nr rounds. 418 // The first Nr-1 rounds are identical. 419 // These Nr rounds are executed in the loop below. 420 // Last one without MixColumns() 421 for (round = 1;; ++round) { 422 SubBytes(state); 423 ShiftRows(state); 424 if (round == Nr) { 425 break; 426 } 427 MixColumns(state); 428 addRoundKey(round, state); 429 } 430 // Add round key to last round 431 addRoundKey(Nr, state); 432 } 433 434 void InvCipher(ref scope state_t state) { 435 ubyte round = 0; 436 437 // Add the First round key to the state before starting the rounds. 438 addRoundKey(Nr, state); 439 440 // There will be Nr rounds. 441 // The first Nr-1 rounds are identical. 442 // These Nr rounds are executed in the loop below. 443 // Last one without InvMixColumn() 444 for (round = (Nr - 1);; --round) { 445 InvShiftRows(state); 446 InvSubBytes(state); 447 addRoundKey(round, state); 448 if (round == 0) { 449 break; 450 } 451 InvMixColumns(state); 452 } 453 454 } 455 456 static void xorWithIv(ubyte[] buf, ref const(ubyte[BLOCK_SIZE]) Iv) { 457 static foreach (i; 0 .. BLOCK_SIZE) { 458 buf[i] ^= Iv[i]; 459 } 460 } 461 462 } 463 /*****************************************************************************/ 464 /* Public functions: */ 465 /*****************************************************************************/ 466 467 // version(unittest) { 468 static if (mode is Mode.ECB) { 469 void encrypt(scope ubyte[] buf) { 470 // The next function call encrypts the PlainText with the Key using AES algorithm. 471 while (buf.length) { 472 cipher(State(buf)); 473 buf = buf[BLOCK_SIZE .. $]; 474 } 475 } 476 477 void decrypt(scope ubyte[] buf) { 478 // The next function call decrypts the PlainText with the Key using AES algorithm. 479 while (buf.length) { 480 InvCipher(State(buf)); 481 buf = buf[BLOCK_SIZE .. $]; 482 } 483 } 484 } 485 486 static if (mode is Mode.CBC) { 487 void encrypt(scope ubyte[] buf) { 488 auto Iv = ctx.Iv; 489 while (buf.length) { 490 xorWithIv(buf, Iv); 491 cipher(State(buf)); 492 Iv = buf[0 .. BLOCK_SIZE]; 493 buf = buf[BLOCK_SIZE .. $]; 494 } 495 /* store Iv in ctx for next call */ 496 ctx.Iv = Iv; 497 } 498 499 void decrypt(scope ubyte[] buf) { 500 ubyte[BLOCK_SIZE] storeNextIv; 501 while (buf.length) { 502 storeNextIv = buf[0 .. BLOCK_SIZE]; 503 InvCipher(State(buf)); 504 xorWithIv(buf, ctx.Iv); 505 ctx.Iv = storeNextIv; 506 buf = buf[BLOCK_SIZE .. $]; 507 } 508 } 509 } 510 511 static if (mode is Mode.CFB) { 512 void encrypt(scope ubyte[] buf) { 513 scope storeNextIv = ctx.Iv; 514 while (buf.length) { 515 cipher(State(storeNextIv)); 516 xorWithIv(buf, storeNextIv); 517 storeNextIv = buf[0 .. BLOCK_SIZE]; 518 buf = buf[BLOCK_SIZE .. $]; 519 } 520 } 521 522 void decrypt(scope ubyte[] buf) { 523 scope storeNextIv = ctx.Iv; 524 while (buf.length) { 525 cipher(State(storeNextIv)); 526 const xor = storeNextIv; 527 storeNextIv = buf[0 .. BLOCK_SIZE]; 528 xorWithIv(buf, xor); 529 buf = buf[BLOCK_SIZE .. $]; 530 } 531 } 532 } 533 534 /* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ 535 static if (mode is Mode.CTR) { 536 alias encrypt = xcrypt; 537 alias decrypt = xcrypt; 538 void xcrypt(scope ubyte[] buf) { 539 ubyte[BLOCK_SIZE] buffer; 540 size_t bi; //=BLOCK_SIZE; 541 foreach (i; 0 .. buf.length) { 542 if (i % BLOCK_SIZE == 0) { //bi == BLOCK_SIZE) { /* we need to regen xor compliment in buffer */ 543 buffer[0 .. BLOCK_SIZE] = ctx.Iv; 544 cipher(State(buffer)); 545 546 /* Increment Iv and handle overflow */ 547 foreach_reverse (j; 0 .. BLOCK_SIZE) { 548 /* inc will overflow */ 549 if (ctx.Iv[j] == 255) { 550 ctx.Iv[j] = 0; 551 continue; 552 } 553 ctx.Iv[j] += 1; 554 break; 555 } 556 bi = 0; 557 } 558 559 buf[i] ^= buffer[bi]; 560 bi++; 561 } 562 } 563 } 564 565 } 566 unittest { 567 version (PRINT) import std.stdio; 568 569 version (PRINT) 570 writefln("Start unittest %s %s", KEY_LENGTH, mode); 571 // prints string as hex 572 static void phex(ubyte[] str) { 573 version (PRINT) { 574 ubyte len = KEY_SIZE; 575 ubyte i; 576 for (i = 0; i < len; ++i) 577 writef("%.2x", str[i]); 578 writeln; 579 } 580 } 581 582 version (PRINT) 583 static if (mode is Mode.ECB && KEY_LENGTH is 128) { 584 { 585 // Example of more verbose verification 586 587 // 128bit key 588 ubyte[KEY_SIZE] key = [ 589 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 590 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c 591 ]; 592 // 512bit text 593 ubyte[64] plain_text = [ 594 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 595 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 596 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 597 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 598 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 599 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 600 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 601 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 602 ]; 603 604 // print text to encrypt, key and IV 605 writeln("ECB encrypt verbose:\n"); 606 writeln("plain text:\n"); 607 foreach (i; 0 .. 4) { 608 phex(plain_text[i * 16 .. $]); 609 } 610 writeln(); 611 612 write("key:"); 613 phex(key); 614 writeln(); 615 616 // print the resulting cipher as 4 x 16 byte strings 617 writeln("ciphertext:"); 618 //ctx ctx; 619 auto aes = Tiny_AES(key); 620 621 foreach (i; 0 .. 4) { 622 aes.encrypt(plain_text[i * 16 .. $]); 623 phex(plain_text[i * 16 .. $]); 624 } 625 writeln(); 626 } 627 } 628 629 static if (mode is Mode.ECB) { 630 { // test_encrypt_ecb 631 static if (KEY_LENGTH is 256) { 632 ubyte[KEY_SIZE] key = [ 633 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 634 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 635 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 636 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 637 ]; 638 ubyte[] outdata = [ 639 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 640 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8 641 ]; 642 } 643 else static if (KEY_LENGTH is 192) { 644 ubyte[KEY_SIZE] key = [ 645 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 646 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 647 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b 648 ]; 649 ubyte[] outdata = [ 650 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 651 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc 652 ]; 653 } 654 else static if (KEY_LENGTH is 128) { 655 ubyte[KEY_SIZE] key = [ 656 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 657 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c 658 ]; 659 ubyte[] outdata = [ 660 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 661 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97 662 ]; 663 } 664 665 ubyte[] indata = [ 666 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 667 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a 668 ]; 669 //Tiny_AES aes; 670 auto aes = Tiny_AES(key); 671 aes.encrypt(indata); 672 673 version (PRINT) 674 writeln("ECB encrypt: "); 675 676 assert(outdata == indata); 677 } 678 } 679 680 static if (mode is Mode.CBC) { 681 { // test_decrypt_cbc 682 static if (KEY_LENGTH is 256) { 683 ubyte[KEY_SIZE] key = [ 684 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 685 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 686 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 687 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 688 ]; 689 ubyte[] indata = [ 690 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 691 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, 692 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 693 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, 694 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 695 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, 696 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 697 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b 698 ]; 699 } 700 else static if (KEY_LENGTH is 192) { 701 ubyte[KEY_SIZE] key = [ 702 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 703 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 704 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b 705 ]; 706 ubyte[] indata = [ 707 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 708 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, 709 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 710 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, 711 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 712 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, 713 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 714 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd 715 ]; 716 } 717 else static if (KEY_LENGTH is 128) { 718 ubyte[KEY_SIZE] key = [ 719 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 720 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c 721 ]; 722 ubyte[] indata = [ 723 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 724 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, 725 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 726 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, 727 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 728 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, 729 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 730 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 731 ]; 732 } 733 734 ubyte[BLOCK_SIZE] iv = [ 735 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 736 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 737 ]; 738 ubyte[] outdata = [ 739 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 740 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 741 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 742 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 743 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 744 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 745 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 746 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 747 ]; 748 749 //Tiny_AES aes; 750 auto aes = Tiny_AES(key, iv); 751 aes.decrypt(indata); 752 753 version (PRINT) 754 writeln("CBC decrypt: "); 755 756 assert(outdata == indata); 757 } 758 } 759 760 static if (mode is Mode.CBC) { 761 { // test_encrypt_cbc 762 static if (KEY_LENGTH is 256) { 763 ubyte[KEY_SIZE] key = [ 764 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 765 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 766 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 767 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 768 ]; 769 ubyte[] outdata = [ 770 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 771 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, 772 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 773 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, 774 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 775 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, 776 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 777 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b 778 ]; 779 } 780 else static if (KEY_LENGTH is 192) { 781 782 ubyte[KEY_SIZE] key = [ 783 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 784 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 785 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b 786 ]; 787 ubyte[] outdata = [ 788 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 789 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, 790 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 791 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, 792 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 793 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, 794 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 795 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd 796 ]; 797 } 798 else static if (KEY_LENGTH is 128) { 799 ubyte[KEY_SIZE] key = [ 800 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 801 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c 802 ]; 803 ubyte[] outdata = [ 804 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 805 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, 806 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 807 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, 808 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 809 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, 810 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 811 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 812 ]; 813 } 814 815 ubyte[BLOCK_SIZE] iv = [ 816 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 817 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 818 ]; 819 ubyte[] indata = [ 820 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 821 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 822 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 823 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 824 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 825 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 826 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 827 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 828 ]; 829 // ctx ctx; 830 //Tiny_AES aes; 831 auto aes = Tiny_AES(key, iv); 832 aes.encrypt(indata); 833 834 version (PRINT) 835 writeln("CBC encrypt: "); 836 837 assert(outdata == indata); 838 } 839 } 840 841 static if (mode is Mode.CTR) { 842 { // test ctr 843 static if (KEY_LENGTH is 256) { 844 ubyte[KEY_SIZE] key = [ 845 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 846 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 847 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 848 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 849 ]; 850 ubyte[64] indata = [ 851 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 852 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28, 853 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 854 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5, 855 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 856 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d, 857 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 858 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6 859 ]; 860 } 861 else static if (KEY_LENGTH is 192) { 862 ubyte[KEY_SIZE] key = [ 863 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 864 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 865 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b 866 ]; 867 ubyte[64] indata = [ 868 0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 869 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b, 870 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 871 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94, 872 0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 873 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7, 874 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 875 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50 876 ]; 877 } 878 else static if (KEY_LENGTH is 128) { 879 ubyte[KEY_SIZE] key = [ 880 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 881 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c 882 ]; 883 ubyte[64] indata = [ 884 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 885 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, 886 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 887 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, 888 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 889 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, 890 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 891 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee 892 ]; 893 } 894 ubyte[16] iv = [ 895 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 896 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 897 ]; 898 ubyte[64] outdata = [ 899 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 900 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 901 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 902 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 903 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 904 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 905 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 906 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 907 ]; 908 //Tiny_AES aes; 909 auto aes = Tiny_AES(key, iv); 910 aes.xcrypt(indata); 911 912 assert(outdata == indata); 913 } 914 } 915 916 static if (mode is Mode.ECB) { 917 { // test_decrypt_ecb 918 static if (KEY_LENGTH is 256) { 919 ubyte[KEY_SIZE] key = [ 920 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 921 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 922 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 923 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 924 ]; 925 ubyte[] indata = [ 926 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 927 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8 928 ]; 929 } 930 else static if (KEY_LENGTH is 192) { 931 ubyte[KEY_SIZE] key = [ 932 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 933 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 934 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b 935 ]; 936 ubyte[] indata = [ 937 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 938 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc 939 ]; 940 } 941 else static if (KEY_LENGTH is 128) { 942 ubyte[KEY_SIZE] key = [ 943 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 944 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c 945 ]; 946 ubyte[] indata = [ 947 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 948 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97 949 ]; 950 } 951 952 ubyte[] outdata = [ 953 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 954 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a 955 ]; 956 //Tiny_AES aes; 957 auto aes = Tiny_AES(key); 958 aes.decrypt(indata); 959 960 version (PRINT) 961 writeln("ECB decrypt: "); 962 963 assert(outdata == indata); 964 } 965 } 966 } 967 } 968 969 unittest { 970 version (PRINT) import std.stdio; 971 import std.traits : EnumMembers; 972 973 static foreach (key_size; [128, 192, 256]) { 974 static foreach (mode; EnumMembers!Mode) { 975 { 976 alias AES = Tiny_AES!(key_size, mode); 977 AES aes; 978 version (PRINT) 979 writefln("%s", AES.stringof); 980 } 981 } 982 } 983 }