1 module dcrypt.blockcipher.blockcipher;
2 
3 /// Use this to check if type is a block cipher.
4 @safe
5 template isBlockCipher(T)
6 {
7 	enum bool isBlockCipher =
8 		is(T == struct) &&
9 			is(typeof(
10 					{
11 						ubyte[0] block;
12 						T bc = T.init; // Can define
13 						string name = T.name;
14 						uint blockSize = T.blockSize;
15 						bc.start(true, cast(const ubyte[]) block, cast(const ubyte[]) block);	// init with secret key and iv
16 						uint len = bc.processBlock(cast (const ubyte[]) block, block);
17 						bc.reset();
18 					}));
19 }
20 
21 /// OOP API for block ciphers
22 @safe
23 public interface IBlockCipher {
24 
25 	
26 	@safe public:
27 
28 	/**
29 	 * Initialize the cipher.
30 	 *
31 	 * Params:
32 	 * forEncryption	=	if true the cipher is initialised for
33 	 *  encryption, if false for decryption.
34 	 * userKey	=	A secret key.
35 	 * iv = A nonce.
36 	 */
37 	void start(bool forEncryption, in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc;
38 
39 	/**
40 	 * Return the name of the algorithm the cipher implements.
41 	 *
42 	 * Returns: the name of the algorithm the cipher implements.
43 	 */
44 	@property
45 	string name() pure nothrow;
46 
47 	/**
48 	 * Return the block size for this cipher (in bytes).
49 	 *
50 	 * Returns: the block size for this cipher in bytes.
51 	 */
52 	@property
53 	uint blockSize() pure nothrow @nogc;
54 
55 	/**
56 	 * Process one block of input from the array in and write it to
57 	 * the out array.
58 	 *
59 	 * Params:
60 	 *	input = the slice containing the input data.
61 	 *  output = the slice the output data will be copied into.
62 	 * Throws: IllegalStateException if the cipher isn't initialised.
63 	 * Returns: the number of bytes processed and produced.
64 	 */
65 	@nogc
66 	uint processBlock(in ubyte[] input, ubyte[] output) nothrow;
67 
68 	/**
69 	 * Reset the cipher. After resetting the cipher is in the same state
70 	 * as it was after the last init (if there was one).
71 	 */
72 	@nogc
73 	void reset() nothrow;
74 }
75 
76 /// Wraps block ciphers into the OOP API
77 @safe
78 public class BlockCipherWrapper(T) if(isBlockCipher!T): IBlockCipher {
79 
80 	private T cipher;
81 
82 	@safe public:
83 	
84 	/**
85 	 * Initialize the cipher.
86 	 *
87 	 * Params:
88 	 *	forEncryption	=	if true the cipher is initialised for
89 	 *  encryption, if false for decryption.
90 	 *  params	=	the key and other data required by the cipher.
91 	 *
92 	 * Throws: IllegalArgumentException if the params argument is
93 	 * inappropriate.
94 	 */
95 	void start(bool forEncryption, in ubyte[] key, in ubyte[] iv = null) nothrow {
96 		cipher.start(forEncryption, key, iv);
97 	}
98 	
99 	/**
100 	 * Return the name of the algorithm the cipher implements.
101 	 *
102 	 * Returns: the name of the algorithm the cipher implements.
103 	 */
104 	@property
105 	string name() pure nothrow {
106 		return cipher.name;
107 	}
108 	
109 	/**
110 	 * Return the block size for this cipher (in bytes).
111 	 *
112 	 * Returns: the block size for this cipher in bytes.
113 	 */
114 	@property
115 	uint blockSize() pure nothrow @nogc {
116 		return T.blockSize;
117 	}
118 	
119 	/**
120 	 * Process one block of input from the array in and write it to
121 	 * the out array.
122 	 *
123 	 * Params:
124 	 *	input = the slice containing the input data.
125 	 *  output = the slice the output data will be copied into.
126 	 * Throws: IllegalStateException if the cipher isn't initialised.
127 	 * Returns: the number of bytes processed and produced.
128 	 */
129 	uint processBlock(in ubyte[] input, ubyte[] output) nothrow @nogc {
130 		return cipher.processBlock(input, output);
131 	}
132 	
133 	/**
134 	 * Reset the cipher. After resetting the cipher is in the same state
135 	 * as it was after the last init (if there was one).
136 	 */
137 	void reset() nothrow @nogc {
138 		cipher.reset();
139 	}
140 }
141 
142 version(unittest) {
143 	
144 	// unittest helper functions
145 
146 	import std.format: format;
147 	
148 	/// Runs decryption and encryption using BlockCipher bc with given keys, plaintexts, and ciphertexts
149 	///
150 	/// Params:
151 	/// keys	=	The encryption/decryption keys.
152 	/// plaintexts	=	Plaintexts.
153 	/// cipherTexts	=	Corresponding ciphertexts.
154 	/// ivs	=	Initialization vectors.
155 	///
156 	@safe
157 	public void blockCipherTest(IBlockCipher bc, string[] keys, string[] plaintexts, string[] cipherTexts, string[] ivs = null) {
158 
159 		foreach (uint i, string test_key; keys)
160 		{
161 			ubyte[] buffer = new ubyte[bc.blockSize];
162 
163 
164 			const ubyte[] key = cast(const ubyte[]) test_key;
165 			const (ubyte)[] iv = null;
166 			if(ivs !is null) { 
167 				iv = cast(const (ubyte)[]) ivs[i]; 
168 			}
169 
170 			// Encryption
171 			bc.start(true, key, iv);
172 			bc.processBlock(cast(const ubyte[]) plaintexts[i], buffer);
173 			
174 			assert(buffer == cipherTexts[i],
175 				format("%s failed to encrypt.", bc.name));
176 			
177 			// Decryption
178 			bc.start(false, key, iv);
179 			bc.processBlock(cast(const ubyte[]) cipherTexts[i], buffer);
180 			assert(buffer == plaintexts[i],
181 				format("%s failed to decrypt.", bc.name));
182 		}
183 	}
184 }