1 module dcrypt.crypto.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 BlockCipher {
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): BlockCipher {
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 	
147 	/// Runs decryption and encryption using BlockCipher bc with given keys, plaintexts, and ciphertexts
148 	///
149 	/// Params:
150 	/// hexKeys	=	the keys encoded in hex
151 	/// hexPlaintexts	=	the plaintexts encoded in hex
152 	/// hexCiphertexts	=	the corresponding ciphertexts in hex
153 	///
154 	/// Throws:
155 	/// AssertionError	if encryption or decryption failed
156 	///
157 	@safe
158 	public void blockCipherTest(BlockCipher bc, string[] keys, string[] plaintexts, string[] cipherTexts, string[] ivs = null) {
159 		import dcrypt.encoders.hex;
160 		import std.conv: text;
161 		
162 		foreach (uint i, string test_key; keys)
163 		{
164 			ubyte[] buffer = new ubyte[bc.blockSize];
165 
166 
167 			const ubyte[] key = cast(const ubyte[]) test_key;
168 			const (ubyte)[] iv = null;
169 			if(ivs !is null) { 
170 				iv = cast(const (ubyte)[]) ivs[i]; 
171 			}
172 
173 			// Encryption
174 			bc.start(true, key, iv);
175 			bc.processBlock(cast(const ubyte[]) plaintexts[i], buffer);
176 			
177 			assert(buffer == cipherTexts[i],
178 				text(bc.name, " encrypt: (", hexEncode(buffer), ") != (", hexEncode(cipherTexts[i]), ")"));
179 			
180 			// Decryption
181 			bc.start(false, key, iv);
182 			bc.processBlock(cast(const ubyte[]) cipherTexts[i], buffer);
183 			assert(buffer == plaintexts[i],
184 				text(bc.name, " decrypt: (", hexEncode(buffer),") != (", hexEncode(plaintexts[i]), ")"));
185 		}
186 	}
187 }