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 }