1 module dcrypt.streamcipher.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 = T.name;
15 						c.start(true, cast(const ubyte[]) block, cast(const ubyte[]) block); // init with key and IV
16 						ubyte[] outSlice = c.processBytes(cast(const ubyte[]) block, block);
17 						ubyte b = c.processByte(ubyte(0));
18 					}));
19 }
20 
21 @safe
22 public interface IStreamCipher {
23 
24 	/// Initialize the cipher.
25 	/// 
26 	/// Params:
27 	/// forEncryption	=	Encrypt if true, decrypt if false.
28 	/// key	=	Secret key.
29 	/// nonce =	Initialization vector.
30 	@safe
31 	public void start(bool forEncryption, in ubyte[] key, in ubyte[] nonce);
32 
33 	/// Returns: the name of the algorithm the cipher implements.
34 	@safe
35 	public string name() pure nothrow;
36 
37 	/// Process a block of bytes from in putting the result into out.
38 	///
39 	/// Params:
40 	/// input = The input byte array.
41 	/// output = The output buffer the processed bytes go into.
42 	/// 
43 	/// Returns:
44 	/// Returns a slice pointing to the output data.
45 	@safe
46 	public ubyte[] processBytes(in ubyte[] input, ubyte[] output);
47 }
48 
49 @safe
50 public class StreamCipherWrapper(T) if(isStreamCipher!T): IStreamCipher {
51 
52 	private T cipher;
53 
54 	/// Params:
55 	/// forEncryption = encrypt or decrypt
56 	/// key = Secret key.
57 	/// iv = Initialization vector.
58 	@safe
59 	public void start(bool forEncryption, in ubyte[] key, in ubyte[] iv = null) {
60 		cipher.start(forEncryption, key, iv);
61 	}
62 	
63 
64 	/// Returns: the name of the algorithm the cipher implements.
65 	@safe @property
66 	public string name() pure nothrow {
67 		return cipher.name;
68 	}
69 
70 	
71 	/// Process a block of bytes from in putting the result into out.
72 	///
73 	/// Params:
74 	/// input = The input byte array.
75 	/// output = The output buffer the processed bytes go into.
76 	/// 
77 	/// Returns:
78 	/// Returns a slice pointing to the output data.
79 	@safe
80 	public ubyte[] processBytes(in ubyte[] input, ubyte[] output) {
81 		return cipher.processBytes(input, output);
82 	}
83 }
84 
85 version(unittest) {
86 	/// Use this to test a stream cipher with multiple keys, plaintexts and ivs.
87 	/// 
88 	/// Params:
89 	/// c	=	the cipher engine
90 	/// keys	=	keys in binary format
91 	/// plaintexts	=	plaintexts in binary format
92 	/// ciphertexts	=	cipher texts in binary format
93 	/// ivs	=	initialisation vectors, could be 'null'
94 	@safe
95 	void streamCipherTest(IStreamCipher c, string[] keys, string[] plaintexts, string[] ciphertexts, string[] ivs = null) 
96 	in {
97 		assert(keys.length == plaintexts.length, "expected as much plaintexts as keys");
98 		assert(keys.length == ciphertexts.length, "expected as much ciphertexts as keys");
99 
100 		if(ivs != null) {
101 			assert(keys.length == ivs.length, "expected as much ivs as keys");
102 		}
103 	}
104 	body {
105 		import std.conv: text;
106 		alias const(ubyte)[] octets;
107 
108 		ubyte[] buf;
109 
110 		import std.range: zip;
111 
112 		void doTest(in ubyte[] key, in ubyte[] plain, in ubyte[] ciphertext, in ubyte[] iv) {
113 			
114 			c.start(true, key, iv);
115 			
116 			buf.length = plain.length;
117 			
118 			c.processBytes(plain, buf);
119 
120 			assert(buf == ciphertext, text(c.name, ": encryption failed."));
121 		}
122 
123 		if(ivs !is null) {
124 			foreach(key, plain, cipher, iv; zip(keys, plaintexts, ciphertexts, ivs)) {
125 				doTest(cast(octets) key, cast(octets) plain, cast(octets) cipher, cast(octets) iv);
126 			}
127 		}else {
128 			foreach(key, plain, cipher; zip(keys, plaintexts, ciphertexts)) {
129 				doTest(cast(octets) key, cast(octets) plain, cast(octets) cipher, null);
130 			}
131 		}
132 	}
133 }