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 }