1 /*
2 * (C) 2014,2015 Jack Lloyd
3 *
4 * Botan is released under the Simplified BSD License (see license.txt)
5 */
6 
7 #include "tests.h"
8 
9 #if defined(BOTAN_HAS_BLOCK_CIPHER)
10 
11 #include <botan/block_cipher.h>
12 
13 namespace Botan_Tests {
14 
15 class Block_Cipher_Tests final : public Text_Based_Test
16    {
17    public:
Block_Cipher_Tests()18       Block_Cipher_Tests() : Text_Based_Test("block", "Key,In,Out", "Tweak,Iterations") {}
19 
possible_providers(const std::string & algo)20       std::vector<std::string> possible_providers(const std::string& algo) override
21          {
22          return provider_filter(Botan::BlockCipher::providers(algo));
23          }
24 
run_one_test(const std::string & algo,const VarMap & vars)25       Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
26          {
27          const std::vector<uint8_t> key      = vars.get_req_bin("Key");
28          const std::vector<uint8_t> input    = vars.get_req_bin("In");
29          const std::vector<uint8_t> expected = vars.get_req_bin("Out");
30          const std::vector<uint8_t> tweak    = vars.get_opt_bin("Tweak");
31          const size_t iterations             = vars.get_opt_sz("Iterations", 1);
32 
33          Test::Result result(algo);
34 
35          if(iterations > 1 && run_long_tests() == false)
36             {
37             return result;
38             }
39 
40          const std::vector<std::string> providers = possible_providers(algo);
41 
42          if(providers.empty())
43             {
44             result.note_missing("block cipher " + algo);
45             return result;
46             }
47 
48          for(auto const& provider_ask : providers)
49             {
50             std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create(algo, provider_ask));
51 
52             if(!cipher)
53                {
54                result.test_failure("Cipher " + algo + " supported by " + provider_ask + " but not found");
55                continue;
56                }
57 
58             const std::string provider(cipher->provider());
59             result.test_is_nonempty("provider", provider);
60             result.test_eq(provider, cipher->name(), algo);
61             result.test_gte(provider, cipher->parallelism(), 1);
62             result.test_gte(provider, cipher->block_size(), 8);
63             result.test_gte(provider, cipher->parallel_bytes(), cipher->block_size() * cipher->parallelism());
64 
65             // Test that trying to encrypt or decrypt with now key set throws Botan::Invalid_State
66             try
67                {
68                std::vector<uint8_t> block(cipher->block_size());
69                cipher->encrypt(block);
70                result.test_failure("Was able to encrypt without a key being set");
71                }
72             catch(Botan::Invalid_State&)
73                {
74                result.test_success("Trying to encrypt with no key set fails");
75                }
76 
77             try
78                {
79                std::vector<uint8_t> block(cipher->block_size());
80                cipher->decrypt(block);
81                result.test_failure("Was able to decrypt without a key being set");
82                }
83             catch(Botan::Invalid_State&)
84                {
85                result.test_success("Trying to encrypt with no key set fails");
86                }
87 
88             // Test to make sure clear() resets what we need it to
89             cipher->set_key(Test::rng().random_vec(cipher->key_spec().maximum_keylength()));
90             Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(cipher->block_size());
91             cipher->encrypt(garbage);
92             cipher->clear();
93 
94             /*
95             * Different providers may have additional restrictions on key sizes.
96             * Avoid testing the cipher with a key size that it does not natively support.
97             */
98             if(!cipher->valid_keylength(key.size()))
99                {
100                result.test_note("Skipping test with provider " + provider +
101                                 " as it does not support key length " + std::to_string(key.size()));
102                continue;
103                }
104 
105             cipher->set_key(key);
106 
107             if(tweak.size() > 0)
108                {
109                Botan::Tweakable_Block_Cipher* tbc = dynamic_cast<Botan::Tweakable_Block_Cipher*>(cipher.get());
110                if(tbc == nullptr)
111                   result.test_failure("Tweak set in test data but cipher is not a Tweakable_Block_Cipher");
112                else
113                   tbc->set_tweak(tweak.data(), tweak.size());
114                }
115 
116             // Test that clone works and does not affect parent object
117             std::unique_ptr<Botan::BlockCipher> clone(cipher->clone());
118             result.confirm("Clone has different pointer", cipher.get() != clone.get());
119             result.test_eq("Clone has same name", cipher->name(), clone->name());
120             clone->set_key(Test::rng().random_vec(cipher->maximum_keylength()));
121 
122             // have called set_key on clone: process input values
123             std::vector<uint8_t> buf = input;
124 
125             for(size_t i = 0; i != iterations; ++i)
126                {
127                cipher->encrypt(buf);
128                }
129 
130             result.test_eq(provider, "encrypt", buf, expected);
131 
132             // always decrypt expected ciphertext vs what we produced above
133             buf = expected;
134 
135             for(size_t i = 0; i != iterations; ++i)
136                {
137                cipher->decrypt(buf);
138                }
139 
140             result.test_eq(provider, "decrypt", buf, input);
141 
142             // Now test misaligned buffers
143             const size_t blocks = input.size() / cipher->block_size();
144             buf.resize(input.size() + 1);
145             Botan::copy_mem(buf.data() + 1, input.data(), input.size());
146 
147             for(size_t i = 0; i != iterations; ++i)
148                {
149                cipher->encrypt_n(buf.data() + 1, buf.data() + 1, blocks);
150                }
151 
152             result.test_eq(provider.c_str(), "encrypt misaligned",
153                            buf.data() + 1, buf.size() - 1,
154                            expected.data(), expected.size());
155 
156             // always decrypt expected ciphertext vs what we produced above
157             Botan::copy_mem(buf.data() + 1, expected.data(), expected.size());
158 
159             for(size_t i = 0; i != iterations; ++i)
160                {
161                cipher->decrypt_n(buf.data() + 1, buf.data() + 1, blocks);
162                }
163 
164             result.test_eq(provider.c_str(), "decrypt misaligned",
165                            buf.data() + 1, buf.size() - 1,
166                            input.data(), input.size());
167 
168             cipher->clear();
169 
170             try
171                {
172                std::vector<uint8_t> block(cipher->block_size());
173                cipher->encrypt(block);
174                result.test_failure("Was able to encrypt without a key being set");
175                }
176             catch(Botan::Invalid_State&)
177                {
178                result.test_success("Trying to encrypt with no key set (after clear) fails");
179                }
180 
181             try
182                {
183                std::vector<uint8_t> block(cipher->block_size());
184                cipher->decrypt(block);
185                result.test_failure("Was able to decrypt without a key being set");
186                }
187             catch(Botan::Invalid_State&)
188                {
189                result.test_success("Trying to decrypt with no key set (after clear) fails");
190                }
191 
192             }
193 
194          return result;
195          }
196 
197    };
198 
199 BOTAN_REGISTER_TEST("block", "block", Block_Cipher_Tests);
200 
201 }
202 
203 #endif
204