1 /*
2 * (C) 2016 Philipp Weber
3 * (C) 2016 Daniel Neus
4 *
5 * Botan is released under the Simplified BSD License (see license.txt)
6 */
7
8 #include "tests.h"
9
10 #if defined(BOTAN_HAS_ECIES)
11 #include <botan/ecies.h>
12 #include <botan/ecdh.h>
13 #endif
14
15 namespace Botan_Tests {
16
17 namespace {
18
19 #if defined(BOTAN_HAS_ECIES) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC)
20
21 using Flags = Botan::ECIES_Flags;
22
get_compression_type(const std::string & format)23 Botan::PointGFp::Compression_Type get_compression_type(const std::string& format)
24 {
25 if(format == "uncompressed")
26 {
27 return Botan::PointGFp::UNCOMPRESSED;
28 }
29 else if(format == "compressed")
30 {
31 return Botan::PointGFp::COMPRESSED;
32 }
33 else if(format == "hybrid")
34 {
35 return Botan::PointGFp::HYBRID;
36 }
37 throw Botan::Invalid_Argument("invalid compression format");
38 }
39
ecies_flags(bool cofactor_mode,bool old_cofactor_mode,bool check_mode,bool single_hash_mode)40 Flags ecies_flags(bool cofactor_mode, bool old_cofactor_mode, bool check_mode, bool single_hash_mode)
41 {
42 return (cofactor_mode ? Flags::COFACTOR_MODE : Flags::NONE)
43 | (single_hash_mode ? Flags::SINGLE_HASH_MODE : Flags::NONE)
44 | (old_cofactor_mode ? Flags::OLD_COFACTOR_MODE : Flags::NONE)
45 | (check_mode ? Flags::CHECK_MODE : Flags::NONE);
46 }
47
check_encrypt_decrypt(Test::Result & result,const Botan::ECDH_PrivateKey & private_key,const Botan::ECDH_PrivateKey & other_private_key,const Botan::ECIES_System_Params & ecies_params,const Botan::InitializationVector & iv,const std::string & label,const std::vector<uint8_t> & plaintext,const std::vector<uint8_t> & ciphertext)48 void check_encrypt_decrypt(Test::Result& result, const Botan::ECDH_PrivateKey& private_key,
49 const Botan::ECDH_PrivateKey& other_private_key,
50 const Botan::ECIES_System_Params& ecies_params,
51 const Botan::InitializationVector& iv, const std::string& label,
52 const std::vector<uint8_t>& plaintext, const std::vector<uint8_t>& ciphertext)
53 {
54 try
55 {
56 Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
57 ecies_enc.set_other_key(other_private_key.public_point());
58 Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, Test::rng());
59 if(!iv.bits_of().empty())
60 {
61 ecies_enc.set_initialization_vector(iv);
62 ecies_dec.set_initialization_vector(iv);
63 }
64 if(!label.empty())
65 {
66 ecies_enc.set_label(label);
67 ecies_dec.set_label(label);
68 }
69
70 const std::vector<uint8_t> encrypted = ecies_enc.encrypt(plaintext, Test::rng());
71 if(!ciphertext.empty())
72 {
73 result.test_eq("encrypted data", encrypted, ciphertext);
74 }
75 const Botan::secure_vector<uint8_t> decrypted = ecies_dec.decrypt(encrypted);
76 result.test_eq("decrypted data equals plaintext", decrypted, plaintext);
77
78 std::vector<uint8_t> invalid_encrypted = encrypted;
79 uint8_t& last_byte = invalid_encrypted[invalid_encrypted.size() - 1];
80 last_byte = ~last_byte;
81 result.test_throws("throw on invalid ciphertext", [&ecies_dec, &invalid_encrypted]
82 {
83 ecies_dec.decrypt(invalid_encrypted);
84 });
85 }
86 catch(Botan::Lookup_Error& e)
87 {
88 result.test_note(std::string("Test not executed: ") + e.what());
89 }
90 }
91
check_encrypt_decrypt(Test::Result & result,const Botan::ECDH_PrivateKey & private_key,const Botan::ECDH_PrivateKey & other_private_key,const Botan::ECIES_System_Params & ecies_params,size_t iv_length=0)92 void check_encrypt_decrypt(Test::Result& result, const Botan::ECDH_PrivateKey& private_key,
93 const Botan::ECDH_PrivateKey& other_private_key,
94 const Botan::ECIES_System_Params& ecies_params, size_t iv_length = 0)
95 {
96 const std::vector<uint8_t> plaintext { 1, 2, 3 };
97 check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, std::vector<uint8_t>(iv_length, 0), "",
98 plaintext, std::vector<uint8_t>());
99 }
100
101 #if defined(BOTAN_HAS_KDF1_18033) && defined(BOTAN_HAS_SHA1)
102
103 class ECIES_ISO_Tests final : public Text_Based_Test
104 {
105 public:
ECIES_ISO_Tests()106 ECIES_ISO_Tests() : Text_Based_Test(
107 "pubkey/ecies-18033.vec",
108 "format,p,a,b,mu,nu,gx,gy,hx,hy,x,r,C0,K") {}
109
run_one_test(const std::string &,const VarMap & vars)110 Test::Result run_one_test(const std::string&, const VarMap& vars) override
111 {
112 Test::Result result("ECIES-ISO");
113
114 // get test vectors defined by ISO 18033
115 const Botan::PointGFp::Compression_Type compression_type = get_compression_type(vars.get_req_str("format"));
116 const Botan::BigInt p = vars.get_req_bn("p");
117 const Botan::BigInt a = vars.get_req_bn("a");
118 const Botan::BigInt b = vars.get_req_bn("b");
119 const Botan::BigInt mu = vars.get_req_bn("mu"); // order
120 const Botan::BigInt nu = vars.get_req_bn("nu"); // cofactor
121 const Botan::BigInt gx = vars.get_req_bn("gx"); // base point x
122 const Botan::BigInt gy = vars.get_req_bn("gy"); // base point y
123 const Botan::BigInt hx = vars.get_req_bn("hx"); // x of public point of bob
124 const Botan::BigInt hy = vars.get_req_bn("hy"); // y of public point of bob
125 const Botan::BigInt x = vars.get_req_bn("x"); // private key of bob
126 const Botan::BigInt r = vars.get_req_bn("r"); // (ephemeral) private key of alice
127 const std::vector<uint8_t> c0 = vars.get_req_bin("C0"); // expected encoded (ephemeral) public key
128 const std::vector<uint8_t> k = vars.get_req_bin("K"); // expected derived secret
129
130 const Botan::EC_Group domain(p, a, b, gx, gy, mu, nu);
131
132 // keys of bob
133 const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, x);
134 const Botan::PointGFp other_public_key_point = domain.point(hx, hy);
135 const Botan::ECDH_PublicKey other_public_key(domain, other_public_key_point);
136
137 // (ephemeral) keys of alice
138 const Botan::ECDH_PrivateKey eph_private_key(Test::rng(), domain, r);
139 const Botan::PointGFp eph_public_key_point = eph_private_key.public_point();
140 const std::vector<uint8_t> eph_public_key_bin = eph_public_key_point.encode(compression_type);
141 result.test_eq("encoded (ephemeral) public key", eph_public_key_bin, c0);
142
143 // test secret derivation: ISO 18033 test vectors use KDF1 from ISO 18033
144 // no cofactor-/oldcofactor-/singlehash-/check-mode and 128 byte secret length
145 Botan::ECIES_KA_Params ka_params(eph_private_key.domain(), "KDF1-18033(SHA-1)", 128, compression_type, Flags::NONE);
146 const Botan::ECIES_KA_Operation ka(eph_private_key, ka_params, true, Test::rng());
147 const Botan::SymmetricKey secret_key = ka.derive_secret(eph_public_key_bin, other_public_key_point);
148 result.test_eq("derived secret key", secret_key.bits_of(), k);
149
150 // test encryption / decryption
151
152 for(auto comp_type : { Botan::PointGFp::UNCOMPRESSED, Botan::PointGFp::COMPRESSED, Botan::PointGFp::HYBRID })
153 {
154 for(bool cofactor_mode : { true, false })
155 {
156 for(bool single_hash_mode : { true, false })
157 {
158 for(bool old_cofactor_mode : { true, false })
159 {
160 for(bool check_mode : { true, false })
161 {
162 Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode);
163
164 if(size_t(cofactor_mode) + size_t(check_mode) + size_t(old_cofactor_mode) > 1)
165 {
166 auto onThrow = [&]()
167 {
168 Botan::ECIES_System_Params(eph_private_key.domain(),
169 "KDF2(SHA-1)", "AES-256/CBC", 32, "HMAC(SHA-1)", 20,
170 comp_type, flags);
171 };
172 result.test_throws("throw on invalid ECIES_Flags", onThrow);
173 continue;
174 }
175
176 Botan::ECIES_System_Params ecies_params(eph_private_key.domain(), "KDF2(SHA-1)", "AES-256/CBC",
177 32, "HMAC(SHA-1)", 20, comp_type, flags);
178 check_encrypt_decrypt(result, eph_private_key, other_private_key, ecies_params, 16);
179 }
180 }
181 }
182 }
183 }
184
185 return result;
186 }
187 };
188
189 BOTAN_REGISTER_TEST("pubkey", "ecies_iso", ECIES_ISO_Tests);
190
191 #endif
192
193 class ECIES_Tests final : public Text_Based_Test
194 {
195 public:
ECIES_Tests()196 ECIES_Tests()
197 : Text_Based_Test(
198 "pubkey/ecies.vec",
199 "Curve,PrivateKey,OtherPrivateKey,Kdf,Dem,DemKeyLen,Mac,MacKeyLen,Format,"
200 "CofactorMode,OldCofactorMode,CheckMode,SingleHashMode,Label,Plaintext,Ciphertext",
201 "Iv") {}
202
run_one_test(const std::string &,const VarMap & vars)203 Test::Result run_one_test(const std::string&, const VarMap& vars) override
204 {
205 Test::Result result("ECIES");
206
207 const std::string curve = vars.get_req_str("Curve");
208 const Botan::BigInt private_key_value = vars.get_req_bn("PrivateKey");
209 const Botan::BigInt other_private_key_value = vars.get_req_bn("OtherPrivateKey");
210 const std::string kdf = vars.get_req_str("Kdf");
211 const std::string dem = vars.get_req_str("Dem");
212 const size_t dem_key_len = vars.get_req_sz("DemKeyLen");
213 const std::vector<uint8_t> iv = vars.get_opt_bin("Iv");
214 const std::string mac = vars.get_req_str("Mac");
215 const size_t mac_key_len = vars.get_req_sz("MacKeyLen");
216 const Botan::PointGFp::Compression_Type compression_type = get_compression_type(vars.get_req_str("Format"));
217 const bool cofactor_mode = vars.get_req_sz("CofactorMode") != 0;
218 const bool old_cofactor_mode = vars.get_req_sz("OldCofactorMode") != 0;
219 const bool check_mode = vars.get_req_sz("CheckMode") != 0;
220 const bool single_hash_mode = vars.get_req_sz("SingleHashMode") != 0;
221 const std::string label = vars.get_req_str("Label");
222 const std::vector<uint8_t> plaintext = vars.get_req_bin("Plaintext");
223 const std::vector<uint8_t> ciphertext = vars.get_req_bin("Ciphertext");
224
225 const Flags flags = ecies_flags(cofactor_mode, old_cofactor_mode, check_mode, single_hash_mode);
226 const Botan::EC_Group domain(curve);
227 const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
228 const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value);
229
230 const Botan::ECIES_System_Params ecies_params(private_key.domain(), kdf, dem, dem_key_len, mac, mac_key_len,
231 compression_type, flags);
232 check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext);
233
234 return result;
235 }
236
237 };
238
239 BOTAN_REGISTER_TEST("pubkey", "ecies", ECIES_Tests);
240
241 #if defined(BOTAN_HAS_KDF1_18033) && defined(BOTAN_HAS_HMAC) && defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_SHA2_64)
242
test_other_key_not_set()243 Test::Result test_other_key_not_set()
244 {
245 Test::Result result("ECIES other key not set");
246
247 const Flags flags = ecies_flags(false, false, false, true);
248 const Botan::EC_Group domain("secp521r1");
249 const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
250 "5002761777100287880684822948852132235484464537021197213998300006"
251 "547176718172344447619746779823");
252
253 const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
254 const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
255 "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
256 flags);
257
258 Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
259
260 result.test_throws("encrypt not possible without setting other public key", [ &ecies_enc ]()
261 {
262 ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
263 });
264
265 return result;
266 }
267
test_kdf_not_found()268 Test::Result test_kdf_not_found()
269 {
270 Test::Result result("ECIES kdf not found");
271
272 const Flags flags = ecies_flags(false, false, false, true);
273 const Botan::EC_Group domain("secp521r1");
274 const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
275 "5002761777100287880684822948852132235484464537021197213998300006"
276 "547176718172344447619746779823");
277
278 const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
279 const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF-XYZ(SHA-512)", "AES-256/CBC", 32,
280 "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
281 flags);
282
283 result.test_throws("kdf not found", [&]()
284 {
285 Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
286 ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
287 });
288
289 return result;
290 }
291
test_mac_not_found()292 Test::Result test_mac_not_found()
293 {
294 Test::Result result("ECIES mac not found");
295
296 const Flags flags = ecies_flags(false, false, false, true);
297 const Botan::EC_Group domain("secp521r1");
298 const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
299 "5002761777100287880684822948852132235484464537021197213998300006"
300 "547176718172344447619746779823");
301
302 const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
303 const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
304 "XYZMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
305 flags);
306
307 result.test_throws("mac not found", [&]()
308 {
309 Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
310 ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
311 });
312
313 return result;
314 }
315
test_cipher_not_found()316 Test::Result test_cipher_not_found()
317 {
318 Test::Result result("ECIES cipher not found");
319
320 const Flags flags = ecies_flags(false, false, false, true);
321 const Botan::EC_Group domain("secp521r1");
322 const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
323 "5002761777100287880684822948852132235484464537021197213998300006"
324 "547176718172344447619746779823");
325
326 const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
327 const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-XYZ-256/CBC", 32,
328 "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED,
329 flags);
330
331 result.test_throws("cipher not found", [&]()
332 {
333 Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng());
334 ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng());
335 });
336
337 return result;
338 }
339
test_system_params_short_ctor()340 Test::Result test_system_params_short_ctor()
341 {
342 Test::Result result("ECIES short system params ctor");
343
344 const Botan::EC_Group domain("secp521r1");
345 const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
346 "5002761777100287880684822948852132235484464537021197213998300006"
347 "547176718172344447619746779823");
348
349 const Botan::BigInt other_private_key_value("2294226772740614508941417891614236736606752960073669253551166842"
350 "5866095315090327914760325168219669828915074071456176066304457448"
351 "25404691681749451640151380153");
352
353 const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
354 const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value);
355
356 const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
357 "HMAC(SHA-512)", 16);
358
359 const Botan::InitializationVector iv("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
360 const std::string label = "Test";
361
362 const std::vector<uint8_t> plaintext = Botan::hex_decode("000102030405060708090A0B0C0D0E0F");
363
364 // generated with botan
365 const std::vector<uint8_t> ciphertext = Botan::hex_decode("0401519EAA0489FF9D51E98E4C22349463E2001CD06F8CE47D81D4007A"
366 "79ACF98E92C814686477CEA666EFC277DC84E15FC95E38AFF8E16D478A"
367 "44CD5C5F1517F8B1F300000591317F261C3D04A7207F01EAE3EC70F2360"
368 "0F82C53CC0B85BE7AC9F6CE79EF2AB416E5934D61BA9D346385D7545C57F"
369 "77C7EA7C58E18C70CBFB0A24AE1B9943EC5A8D0657522CCDF30BA95674D81"
370 "B397635D215178CD13BD9504AE957A9888F4128FFC0F0D3F1CEC646AEC8CE"
371 "3F2463D233B22A7A12B679F4C06501F584D4DEFF6D26592A8D873398BD892"
372 "B477B3468813C053DA43C4F3D49009F7A12D6EF7");
373
374 check_encrypt_decrypt(result, private_key, other_private_key, ecies_params, iv, label, plaintext, ciphertext);
375
376 return result;
377 }
378
test_ciphertext_too_short()379 Test::Result test_ciphertext_too_short()
380 {
381 Test::Result result("ECIES ciphertext too short");
382
383 const Botan::EC_Group domain("secp521r1");
384 const Botan::BigInt private_key_value("405029866705438137604064977397053031159826489755682166267763407"
385 "5002761777100287880684822948852132235484464537021197213998300006"
386 "547176718172344447619746779823");
387
388 const Botan::BigInt other_private_key_value("2294226772740614508941417891614236736606752960073669253551166842"
389 "5866095315090327914760325168219669828915074071456176066304457448"
390 "25404691681749451640151380153");
391
392 const Botan::ECDH_PrivateKey private_key(Test::rng(), domain, private_key_value);
393 const Botan::ECDH_PrivateKey other_private_key(Test::rng(), domain, other_private_key_value);
394
395 const Botan::ECIES_System_Params ecies_params(private_key.domain(), "KDF1-18033(SHA-512)", "AES-256/CBC", 32,
396 "HMAC(SHA-512)", 16);
397
398 Botan::ECIES_Decryptor ecies_dec(other_private_key, ecies_params, Test::rng());
399
400 result.test_throws("ciphertext too short", [ &ecies_dec ]()
401 {
402 ecies_dec.decrypt(Botan::hex_decode("0401519EAA0489FF9D51E98E4C22349A"));
403 });
404
405 return result;
406 }
407
408 class ECIES_Unit_Tests final : public Test
409 {
410 public:
run()411 std::vector<Test::Result> run() override
412 {
413 std::vector<Test::Result> results;
414
415 std::vector<std::function<Test::Result()>> fns =
416 {
417 test_other_key_not_set,
418 test_kdf_not_found,
419 test_mac_not_found,
420 test_cipher_not_found,
421 test_system_params_short_ctor,
422 test_ciphertext_too_short
423 };
424
425 for(size_t i = 0; i != fns.size(); ++i)
426 {
427 try
428 {
429 results.emplace_back(fns[ i ]());
430 }
431 catch(std::exception& e)
432 {
433 results.emplace_back(Test::Result::Failure("ECIES unit tests " + std::to_string(i), e.what()));
434 }
435 }
436
437 return results;
438 }
439 };
440
441 BOTAN_REGISTER_TEST("pubkey", "ecies_unit", ECIES_Unit_Tests);
442
443 #endif
444
445 #endif
446
447 }
448
449 }
450