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 }