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 }