1 module dcrypt.blockcipher.modes.ctr; 2 3 import dcrypt.blockcipher.blockcipher; 4 5 6 7 /// test AES/CTR encryption 8 /// test vectors: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf 9 @safe 10 unittest { 11 import dcrypt.blockcipher.aes; 12 import std.range; 13 import std.conv: text; 14 15 CTR!AES ctr; 16 17 const ubyte[] key = cast(const ubyte[])x"2b7e151628aed2a6abf7158809cf4f3c"; 18 const ubyte[] iv = cast(const ubyte[])x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; 19 20 const ubyte[] plain = cast(const ubyte[])x" 21 6bc1bee22e409f96e93d7e117393172a 22 ae2d8a571e03ac9c9eb76fac45af8e51 23 30c81c46a35ce411e5fbc1191a0a52ef 24 f69f2445df4f9b17ad2b417be66c3710 25 "; 26 27 const ubyte[] expected_ciphertext = cast(const ubyte[])x" 28 874d6191b620e3261bef6864990db6ce 29 9806f66b7970fdff8617187bb9fffdff 30 5ae4df3edbd5d35e5b4f09020db03eab 31 1e031dda2fbe03d1792170a0f3009cee 32 "; 33 34 35 // encryption mode 36 ctr.start(true, key, iv); 37 38 ubyte[plain.length] buf; 39 buf = plain; 40 41 foreach(block; chunks(buf[],16)) { 42 ctr.processBlock(block,block); 43 } 44 45 assert(buf == expected_ciphertext, text(ctr.name,": encryption failed")); 46 47 // decryption mode 48 ctr.start(false, key, iv); 49 50 foreach(block; chunks(buf[],16)) { 51 ctr.processBlock(block,block); 52 } 53 54 assert(buf == plain, text(ctr.name,": decryption failed")); 55 56 } 57 58 // OOP API wrapper 59 alias CTRBlockCipher(T) = BlockCipherWrapper!(CTR!T); 60 61 @safe 62 public struct CTR(Cipher) if(isBlockCipher!Cipher) { 63 64 public enum blockSize = Cipher.blockSize; 65 public enum name = Cipher.name ~ "/CTR"; 66 67 private { 68 ubyte[blockSize] counter; 69 ubyte[blockSize] nonce; 70 ubyte[blockSize] buf; 71 72 Cipher cipher; 73 } 74 75 /// Params: 76 /// forEncryption = Does not matter for CTR because encryption and decryption is the same in this mode. 77 /// userKey = secret key 78 /// iv = initialisation vector 79 public void start(bool forEncryption, in ubyte[] userKey, in ubyte[] iv) 80 in { 81 assert(iv !is null, "CTR without IV is not supported."); 82 // does the IV match the block size? 83 assert(iv.length == blockSize, "length of IV has to be the same as the block size"); 84 } 85 body { 86 cipher.start(true, userKey); 87 nonce[] = iv[]; 88 reset(); 89 } 90 91 public void reset() nothrow @nogc { 92 cipher.reset(); 93 counter[0..blockSize] = nonce[0..blockSize]; 94 } 95 96 public uint processBlock(in ubyte[] input, ubyte[] output) nothrow @nogc 97 in { 98 assert(input.length == blockSize, "CTR: input.length != blockSize"); 99 assert(output.length >= blockSize, "CTR: output buffer too small"); 100 } 101 body { 102 103 // encrypt counter 104 cipher.processBlock(counter, buf); 105 106 // xor input and key stream 107 output[0..blockSize] = input[]^buf[]; // byte wise xor 108 109 // increment counter 110 111 for(uint i = blockSize -1; i >= 0; --i) { 112 if(++counter[i] != 0) { 113 break; 114 } 115 // increment next element on overflow of the previous 116 } 117 118 return blockSize; 119 } 120 121 }