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 }