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