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 }