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