1 module dcrypt.crypto.streamcipher;
2 
3 
4 /// test if struct is a stream cipher
5 @safe
6 template isStreamCipher(T)
7 {
8 	enum bool isStreamCipher =
9 		is(T == struct) &&
10 			is(typeof(
11 					{
12 						ubyte[0] block;
13 						T c = void; //Can define
14 						string name = c.name;
15 						c.start(true, cast(const ubyte[]) block, cast(const ubyte[]) block); // init with key and IV
16 						ubyte b = c.returnByte(cast(ubyte)0);
17 						ubyte[] outSlice = c.processBytes(cast(const ubyte[]) block, block);
18 						c.reset();
19 					}));
20 }
21 
22 @safe
23 public interface StreamCipher {
24 	/**
25 	 * Initialize the cipher.
26 	 *
27 	 * Params: forEncryption = if true the cipher is initialised for
28 	 *  encryption, if false for decryption.
29 	 *  params = the key and other data required by the cipher.	 
30 	 * Throws: IllegalArgumentException if the params argument is
31 	 * inappropriate.
32 	 */
33 	@safe
34 	public void start(bool forEncryption, in ubyte[] key, in ubyte[] iv);
35 
36 	/**
37 	 * Returns: the name of the algorithm the cipher implements.
38 	 */
39 	@safe
40 	public string name() pure nothrow;
41 
42 	/**
43 	 * encrypt/decrypt a single byte returning the result.
44 	 *
45 	 * Params: in the byte to be processed.
46 	 * Returns: the result of processing the input byte.
47 	 */
48 	@safe
49 	public ubyte returnByte(ubyte input);
50 
51 	/**
52 	 * process a block of bytes from in putting the result into out.
53 	 *
54 	 * Params: input = the input byte array.
55 	 * output = the output buffer the processed bytes go into.
56 	 * Throws: BufferLengthException if the output buffer is too small.
57 	 */
58 	@safe
59 	public ubyte[] processBytes(in ubyte[] input, ubyte[] output);
60 	
61 	@safe
62 	public void reset();
63 	
64 	/**
65 	 * process a block of bytes from in putting the result into out.
66 	 *
67 	 * Params: input = the input byte array.
68 	 * Returns: slice containing the encrypted or decrypted data
69 	 * Throws: BufferLengthException if the output buffer is too small.
70 	 */
71 	@safe
72 	public final ubyte[] processBytes(in ubyte[] input) {
73 		ubyte[] output = new ubyte[input.length];
74 		processBytes(input, output);
75 		return output;
76 	}
77 
78 }
79 
80 @safe
81 public class StreamCipherWrapper(T) if(isStreamCipher!T): StreamCipher {
82 
83 	private T cipher;
84 
85 	/// Params:
86 	/// forEncryption = encrypt or decrypt
87 	/// key = Secret key.
88 	/// iv = Initialization vector.
89 	@safe
90 	public void start(bool forEncryption, in ubyte[] key, in ubyte[] iv = null) {
91 		cipher.start(forEncryption, key, iv);
92 	}
93 	
94 
95 	/// Returns: the name of the algorithm the cipher implements.
96 	@safe @property
97 	public string name() pure nothrow {
98 		return cipher.name;
99 	}
100 	
101 	/**
102 	 * encrypt/decrypt a single byte returning the result.
103 	 *
104 	 * Params: in = the byte to be processed.
105 	 * Returns: the result of processing the input byte.
106 	 */
107 	@safe
108 	public ubyte returnByte(ubyte input) {
109 		return cipher.returnByte(input);
110 	}
111 	
112 	/**
113 	 * process a block of bytes from in putting the result into out.
114 	 *
115 	 * Params: input = the input byte array.
116 	 * output = the output buffer the processed bytes go into.
117 	 * 
118 	 * Returns: Slice pointing to encrypted or decrypted data. Might be smaller than `output` buffer.
119 	 * 
120 	 * Throws: BufferLengthException if the output buffer is too small.
121 	 * 
122 	 */
123 	@safe
124 	public ubyte[] processBytes(in ubyte[] input, ubyte[] output) {
125 		return cipher.processBytes(input, output);
126 	}
127 	
128 	@safe
129 	deprecated("The reset() function might lead to insecure use of a stream cipher.")
130 	public void reset() {
131 		cipher.reset();
132 	}
133 	
134 }
135 
136 /// Use this to test a stream cipher with multiple keys, plaintexts and ivs.
137 /// 
138 /// Params:
139 /// c	=	the cipher engine
140 /// keys	=	keys in binary format
141 /// plaintexts	=	plaintexts in binary format
142 /// ciphertexts	=	cipher texts in binary format
143 /// ivs	=	initialisation vectors, could be 'null'
144 @safe
145 void streamCipherTest(StreamCipher c, string[] keys, string[] plaintexts, string[] ciphertexts, string[] ivs = null) 
146 in {
147 	assert(keys.length == plaintexts.length, "expected as much plaintexts as keys");
148 	assert(keys.length == ciphertexts.length, "expected as much ciphertexts as keys");
149 
150 	if(ivs != null) {
151 		assert(keys.length == ivs.length, "expected as much ivs as keys");
152 	}
153 }
154 body {
155 	import std.conv: text;
156 	import dcrypt.encoders.hex;
157 	alias const(ubyte)[] octets;
158 
159 	ubyte[] buf;
160 
161 	import std.range: zip;
162 
163 	void doTest(in ubyte[] key, in ubyte[] plain, in ubyte[] ciphertext, in ubyte[] iv) {
164 		
165 		c.start(true, key, iv);
166 		
167 		buf.length = plain.length;
168 		
169 		c.processBytes(plain, buf);
170 		
171 		//debug writeln(hexEncode(buf));
172 		assert(buf == ciphertext, text(c.name(), " encryption failed: ", buf.toHexStr(),
173 				" != ", ciphertext.toHexStr));
174 	}
175 
176 	if(ivs !is null) {
177 		foreach(key, plain, cipher, iv; zip(keys, plaintexts, ciphertexts, ivs)) {
178 			doTest(cast(octets) key, cast(octets) plain, cast(octets) cipher, cast(octets) iv);
179 		}
180 	}else {
181 		foreach(key, plain, cipher; zip(keys, plaintexts, ciphertexts)) {
182 			doTest(cast(octets) key, cast(octets) plain, cast(octets) cipher, null);
183 		}
184 	}
185 
186 	
187 
188 }