1 /** 2 * Stream Cipher 3 * 4 * Copyright: 5 * (C) 1999-2007 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.stream.stream_cipher; 12 13 import botan.constants; 14 public import botan.algo_base.sym_algo; 15 /** 16 * Base class for all stream ciphers 17 */ 18 interface StreamCipher : SymmetricAlgorithm 19 { 20 public: 21 /** 22 * Encrypt or decrypt a message 23 * Params: 24 * input = the plaintext 25 * output = the ubyte array to hold the output, i.e. the ciphertext 26 * len = the length of both in and out in bytes 27 */ 28 abstract void cipher(const(ubyte)* input, ubyte* output, size_t len); 29 30 /** 31 * Encrypt or decrypt a message 32 * Params: 33 * buf = the plaintext / ciphertext 34 * len = the length of buf in bytes 35 */ 36 final void cipher1(const(ubyte)* buf, size_t len) 37 { cipher(buf, cast(ubyte*)buf, len); } 38 39 /** 40 * Encrypt or decrypt a message 41 * Params: 42 * buf = the plaintext / ciphertext 43 */ 44 final void cipher1(ref ubyte[] buf) 45 { cipher(buf.ptr, buf.ptr, buf.length); } 46 47 final void encipher(Alloc)(ref Vector!( ubyte, Alloc ) inoutput) 48 { cipher(inoutput.ptr, inoutput.ptr, inoutput.length); } 49 50 final void encrypt(Alloc)(ref Vector!( ubyte, Alloc ) inoutput) 51 { cipher(inoutput.ptr, inoutput.ptr, inoutput.length); } 52 53 final void decrypt(Alloc)(ref Vector!( ubyte, Alloc ) inoutput) 54 { cipher(inoutput.ptr, inoutput.ptr, inoutput.length); } 55 56 /** 57 * Resync the cipher using the IV 58 * Params: 59 * iv = the initialization vector 60 * iv_len = the length of the IV in bytes 61 */ 62 abstract void setIv(const(ubyte)* iv, size_t iv_len); 63 // { if (iv_len) throw new InvalidArgument("The stream cipher " ~ name ~ " does not support resyncronization"); } 64 65 /** 66 * Params: 67 * iv_len = the length of the IV in bytes 68 * Returns: if the length is valid for this algorithm 69 */ 70 abstract bool validIvLength(size_t iv_len) const; 71 // { return (iv_len == 0); } 72 73 /** 74 * Get a new object representing the same algorithm as this 75 */ 76 abstract StreamCipher clone() const; 77 } 78 79 static if (BOTAN_TEST): 80 import botan.test; 81 import botan.libstate.libstate; 82 import botan.codec.hex; 83 import core.atomic; 84 import memutils.hashmap; 85 private shared size_t total_tests; 86 87 size_t streamTest(string algo, 88 string key_hex, 89 string in_hex, 90 string out_hex, 91 string nonce_hex) 92 { 93 const SecureVector!ubyte key = hexDecodeLocked(key_hex); 94 const SecureVector!ubyte pt = hexDecodeLocked(in_hex); 95 const SecureVector!ubyte ct = hexDecodeLocked(out_hex); 96 const SecureVector!ubyte nonce = hexDecodeLocked(nonce_hex); 97 98 AlgorithmFactory af = globalState().algorithmFactory(); 99 100 const auto providers = af.providersOf(algo); 101 size_t fails = 0; 102 103 if (providers.empty) 104 { 105 logTrace("Unknown algo " ~ algo); 106 ++fails; 107 } 108 109 foreach (provider; providers[]) 110 { 111 atomicOp!"+="(total_tests, 1); 112 const StreamCipher proto = af.prototypeStreamCipher(algo, provider); 113 114 if (!proto) 115 { 116 logError("Unable to get " ~ algo ~ " from provider '" ~ provider ~ "'"); 117 ++fails; 118 continue; 119 } 120 121 Unique!StreamCipher cipher = proto.clone(); 122 cipher.setKey(key); 123 124 if (nonce.length) 125 cipher.setIv(nonce.ptr, nonce.length); 126 127 SecureVector!ubyte buf = pt.dup; 128 129 cipher.encrypt(buf); 130 131 if (buf != ct) 132 { 133 logError(algo ~ " " ~ provider ~ " enc " ~ hexEncode(buf) ~ " != " ~ out_hex); 134 ++fails; 135 } 136 } 137 138 return fails; 139 } 140 141 static if (BOTAN_HAS_TESTS && !SKIP_STREAM_CIPHER_TEST) unittest 142 { 143 logDebug("Testing stream_cipher.d ..."); 144 auto test = delegate(string input) 145 { 146 File vec = File(input, "r"); 147 148 return runTestsBb(vec, "StreamCipher", "Out", true, 149 (ref HashMap!(string, string) m) { 150 return streamTest(m["StreamCipher"], m["Key"], m["In"], m["Out"], m.get("Nonce")); 151 }); 152 }; 153 154 size_t fails = runTestsInDir("../test_data/stream", test); 155 156 testReport("stream", total_tests, fails); 157 }