Botan 3.0.0
Crypto and TLS for C&
ecies.cpp
Go to the documentation of this file.
1/*
2* ECIES
3* (C) 2016 Philipp Weber
4* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/ecies.h>
10#include <botan/numthry.h>
11#include <botan/cipher_mode.h>
12#include <botan/mac.h>
13#include <botan/internal/pk_ops_impl.h>
14
15namespace Botan {
16
17namespace {
18
19/**
20* Private key type for ECIES_ECDH_KA_Operation
21*/
22
25
26class ECIES_PrivateKey final : public EC_PrivateKey, public PK_Key_Agreement_Key
27 {
28 public:
29 explicit ECIES_PrivateKey(const ECDH_PrivateKey& private_key) :
30 EC_PublicKey(private_key),
31 EC_PrivateKey(private_key),
32 PK_Key_Agreement_Key(),
33 m_key(private_key)
34 {
35 }
36
37 std::vector<uint8_t> public_value() const override
38 {
39 return m_key.public_value();
40 }
41
42 std::string algo_name() const override
43 {
44 return "ECIES";
45 }
46
47 std::unique_ptr<Public_Key> public_key() const override
48 {
49 return m_key.public_key();
50 }
51
52 bool supports_operation(PublicKeyOperation op) const override
53 {
55 }
56
57 std::unique_ptr<PK_Ops::Key_Agreement>
58 create_key_agreement_op(RandomNumberGenerator& rng,
59 std::string_view params,
60 std::string_view provider) const override;
61
62 private:
63 ECDH_PrivateKey m_key;
64 };
65
67
68/**
69* Implements ECDH key agreement without using the cofactor mode
70*/
71class ECIES_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
72 {
73 public:
74 ECIES_ECDH_KA_Operation(const ECIES_PrivateKey& private_key, RandomNumberGenerator& rng) :
75 PK_Ops::Key_Agreement_with_KDF("Raw"),
76 m_key(private_key),
77 m_rng(rng)
78 {
79 }
80
81 size_t agreed_value_size() const override { return m_key.domain().get_p_bytes(); }
82
83 secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
84 {
85 const EC_Group& group = m_key.domain();
86
87 EC_Point input_point = group.OS2ECP(w, w_len);
88 input_point.randomize_repr(m_rng);
89
90 const EC_Point S = group.blinded_var_point_multiply(
91 input_point, m_key.private_value(), m_rng, m_ws);
92
93 if(S.on_the_curve() == false)
94 throw Internal_Error("ECDH agreed value was not on the curve");
95 return BigInt::encode_1363(S.get_affine_x(), group.get_p_bytes());
96 }
97
98 private:
99 ECIES_PrivateKey m_key;
100 RandomNumberGenerator& m_rng;
101 std::vector<BigInt> m_ws;
102 };
103
104std::unique_ptr<PK_Ops::Key_Agreement>
105ECIES_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
106 std::string_view /*params*/,
107 std::string_view /*provider*/) const
108 {
109 return std::make_unique<ECIES_ECDH_KA_Operation>(*this, rng);
110 }
111
112/**
113* Creates a PK_Key_Agreement instance for the given key and ecies_params
114* Returns either ECIES_ECDH_KA_Operation or the default implementation for the given key,
115* depending on the key and ecies_params
116* @param private_key the private key used for the key agreement
117* @param ecies_params settings for ecies
118* @param for_encryption disable cofactor mode if the secret will be used for encryption
119* (according to ISO 18033 cofactor mode is only used during decryption)
120*/
121PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key,
122 const ECIES_KA_Params& ecies_params,
123 bool for_encryption,
124 RandomNumberGenerator& rng)
125 {
126 const ECDH_PrivateKey* ecdh_key = dynamic_cast<const ECDH_PrivateKey*>(&private_key);
127
128 if(ecdh_key == nullptr && (ecies_params.cofactor_mode() || ecies_params.old_cofactor_mode()
129 || ecies_params.check_mode()))
130 {
131 // assume we have a private key from an external provider (e.g. pkcs#11):
132 // there is no way to determine or control whether the provider uses cofactor mode or not.
133 // ISO 18033 does not allow cofactor mode in combination with old cofactor mode or check mode
134 // => disable cofactor mode, old cofactor mode and check mode for unknown keys/providers (as a precaution).
135 throw Invalid_Argument("ECIES: cofactor, old cofactor and check mode are only supported for ECDH_PrivateKey");
136 }
137
138 if(ecdh_key && (for_encryption || !ecies_params.cofactor_mode()))
139 {
140 // ECDH_KA_Operation uses cofactor mode: use own key agreement method if cofactor should not be used.
141 return PK_Key_Agreement(ECIES_PrivateKey(*ecdh_key), rng, "Raw");
142 }
143
144 return PK_Key_Agreement(private_key, rng, "Raw"); // use default implementation
145 }
146}
147
149 const ECIES_KA_Params& ecies_params,
150 bool for_encryption,
152 m_ka(create_key_agreement(private_key, ecies_params, for_encryption, rng)),
153 m_params(ecies_params)
154 {
155 }
156
157/**
158* ECIES secret derivation according to ISO 18033-2
159*/
160SymmetricKey ECIES_KA_Operation::derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
161 const EC_Point& other_public_key_point) const
162 {
163 if(other_public_key_point.is_zero())
164 {
165 throw Invalid_Argument("ECIES: other public key point is zero");
166 }
167
168 auto kdf = KDF::create_or_throw(m_params.kdf_spec());
169
170 EC_Point other_point = other_public_key_point;
171
172 // ISO 18033: step b
173 if(m_params.old_cofactor_mode())
174 {
175 other_point *= m_params.domain().get_cofactor();
176 }
177
178 secure_vector<uint8_t> derivation_input;
179
180 // ISO 18033: encryption step e / decryption step g
181 if(!m_params.single_hash_mode())
182 {
183 derivation_input += eph_public_key_bin;
184 }
185
186 // ISO 18033: encryption step f / decryption step h
187 std::vector<uint8_t> other_public_key_bin = other_point.encode(m_params.compression_type());
188 // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because
189 // "Raw" is passed to the `PK_Key_Agreement` if the implementation of botan is used.
190 const SymmetricKey peh = m_ka.derive_key(m_params.domain().get_order().bytes(), other_public_key_bin.data(), other_public_key_bin.size());
191 derivation_input.insert(derivation_input.end(), peh.begin(), peh.end());
192
193 // ISO 18033: encryption step g / decryption step i
194 return SymmetricKey(kdf->derive_key(m_params.secret_length(), derivation_input));
195 }
196
197
199 std::string_view kdf_spec,
200 size_t length,
201 EC_Point_Format compression_type,
202 ECIES_Flags flags) :
203 m_domain(domain),
204 m_kdf_spec(kdf_spec),
205 m_length(length),
206 m_compression_mode(compression_type),
207 m_flags(flags)
208 {
209 }
210
212 std::string_view kdf_spec,
213 std::string_view dem_algo_spec,
214 size_t dem_key_len,
215 std::string_view mac_spec,
216 size_t mac_key_len,
217 EC_Point_Format compression_type,
218 ECIES_Flags flags) :
219 ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags),
220 m_dem_spec(dem_algo_spec),
221 m_dem_keylen(dem_key_len),
222 m_mac_spec(mac_spec),
223 m_mac_keylen(mac_key_len)
224 {
225 // ISO 18033: "At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1."
226 if(size_t(cofactor_mode()) + size_t(old_cofactor_mode()) + size_t(check_mode()) > 1)
227 {
228 throw Invalid_Argument("ECIES: only one of cofactor_mode, old_cofactor_mode and check_mode can be set");
229 }
230 }
231
233 std::string_view kdf_spec,
234 std::string_view dem_algo_spec,
235 size_t dem_key_len,
236 std::string_view mac_spec,
237 size_t mac_key_len) :
238 ECIES_System_Params(domain, kdf_spec, dem_algo_spec, dem_key_len, mac_spec, mac_key_len,
240 {
241 }
242
243std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const
244 {
246 }
247
248std::unique_ptr<Cipher_Mode> ECIES_System_Params::create_cipher(Cipher_Dir direction) const
249 {
250 return Cipher_Mode::create_or_throw(m_dem_spec, direction);
251 }
252
253
254/*
255* ECIES_Encryptor Constructor
256*/
258 const ECIES_System_Params& ecies_params,
260 m_ka(private_key, ecies_params, true, rng),
261 m_params(ecies_params),
262 m_eph_public_key_bin(private_key.public_value()), // returns the uncompressed public key, see conversion below
263 m_iv(),
264 m_other_point(),
265 m_label()
266 {
268 {
269 // ISO 18033: step d
270 // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format
271 m_eph_public_key_bin = m_params.domain().OS2ECP(m_eph_public_key_bin).encode(ecies_params.compression_type());
272 }
273 m_mac = m_params.create_mac();
274 m_cipher = m_params.create_cipher(Cipher_Dir::Encryption);
275 }
276
277/*
278* ECIES_Encryptor Constructor
279*/
281 ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.domain()), ecies_params, rng)
282 {
283 }
284
285size_t ECIES_Encryptor::maximum_input_size() const
286 {
287 /*
288 ECIES should just be used for key transport so this (arbitrary) limit
289 seems sufficient
290 */
291 return 64;
292 }
293
294size_t ECIES_Encryptor::ciphertext_length(size_t ptext_len) const
295 {
296 return m_eph_public_key_bin.size() +
297 m_mac->output_length() +
298 m_cipher->output_length(ptext_len);
299 }
300
301/*
302* ECIES Encryption according to ISO 18033-2
303*/
304std::vector<uint8_t> ECIES_Encryptor::enc(const uint8_t data[], size_t length, RandomNumberGenerator& /*unused*/) const
305 {
306 if(m_other_point.is_zero())
307 {
308 throw Invalid_State("ECIES: the other key is zero");
309 }
310
311 const SymmetricKey secret_key = m_ka.derive_secret(m_eph_public_key_bin, m_other_point);
312
313 // encryption
314
315 m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
316 if(m_iv.empty() && !m_cipher->valid_nonce_length(m_iv.size()))
317 throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
318
319 m_cipher->start(m_iv.bits_of());
320
321 secure_vector<uint8_t> encrypted_data(data, data + length);
322 m_cipher->finish(encrypted_data);
323
324 // concat elements
325
326 std::vector<uint8_t> out(m_eph_public_key_bin.size() + encrypted_data.size() + m_mac->output_length());
327 buffer_insert(out, 0, m_eph_public_key_bin);
328 buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data);
329
330 // mac
331 m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
332 m_mac->update(encrypted_data);
333 if(!m_label.empty())
334 {
335 m_mac->update(m_label);
336 }
337 m_mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size());
338
339 return out;
340 }
341
342
344 const ECIES_System_Params& ecies_params,
346 m_ka(key, ecies_params, false, rng),
347 m_params(ecies_params),
348 m_iv(),
349 m_label()
350 {
351 // ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order)
352 if(!ecies_params.check_mode())
353 {
354 const BigInt& cofactor = m_params.domain().get_cofactor();
355 if(cofactor > 1 && gcd(cofactor, m_params.domain().get_order()) != 1)
356 {
357 throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0");
358 }
359 }
360
361 m_mac = m_params.create_mac();
362 m_cipher = m_params.create_cipher(Cipher_Dir::Decryption);
363 }
364
365size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const
366 {
367 const size_t point_size = m_params.domain().point_size(m_params.compression_type());
368 const size_t overhead = point_size + m_mac->output_length();
369
370 if(ctext_len < overhead)
371 return 0;
372
373 return m_cipher->output_length(ctext_len - overhead);
374 }
375
376/**
377* ECIES Decryption according to ISO 18033-2
378*/
379secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const
380 {
381 const size_t point_size = m_params.domain().point_size(m_params.compression_type());
382
383 if(in_len < point_size + m_mac->output_length())
384 {
385 throw Decoding_Error("ECIES decryption: ciphertext is too short");
386 }
387
388 // extract data
389 const std::vector<uint8_t> other_public_key_bin(in, in + point_size); // the received (ephemeral) public key
390 const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length());
391 const std::vector<uint8_t> mac_data(in + in_len - m_mac->output_length(), in + in_len);
392
393 // ISO 18033: step a
394 EC_Point other_public_key = m_params.domain().OS2ECP(other_public_key_bin);
395
396 // ISO 18033: step b
397 if(m_params.check_mode() && !other_public_key.on_the_curve())
398 {
399 throw Decoding_Error("ECIES decryption: received public key is not on the curve");
400 }
401
402 // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree)
403 // throws Illegal_Transformation if the point is zero)
404 const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);
405
406 // validate mac
407 m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
408 m_mac->update(encrypted_data);
409 if(!m_label.empty())
410 {
411 m_mac->update(m_label);
412 }
413 const secure_vector<uint8_t> calculated_mac = m_mac->final();
414 valid_mask = ct_compare_u8(mac_data.data(), calculated_mac.data(), mac_data.size());
415
416 if(valid_mask)
417 {
418 // decrypt data
419
420 m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
421 if(m_iv.empty() && !m_cipher->valid_nonce_length(m_iv.size()))
422 throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
423 m_cipher->start(m_iv.bits_of());
424
425 try
426 {
427 // the decryption can fail:
428 // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
429 secure_vector<uint8_t> decrypted_data(encrypted_data.begin(), encrypted_data.end());
430 m_cipher->finish(decrypted_data);
431 return decrypted_data;
432 }
433 catch(...)
434 {
435 valid_mask = 0;
436 }
437 }
438 return secure_vector<uint8_t>();
439 }
440
441}
static secure_vector< uint8_t > encode_1363(const BigInt &n, size_t bytes)
Definition: big_code.cpp:107
size_t bytes() const
Definition: bigint.cpp:297
static std::unique_ptr< Cipher_Mode > create_or_throw(std::string_view algo, Cipher_Dir direction, std::string_view provider="")
Definition: cipher_mode.cpp:40
ECIES_Decryptor(const PK_Key_Agreement_Key &private_key, const ECIES_System_Params &ecies_params, RandomNumberGenerator &rng)
Definition: ecies.cpp:343
ECIES_Encryptor(const PK_Key_Agreement_Key &private_key, const ECIES_System_Params &ecies_params, RandomNumberGenerator &rng)
Definition: ecies.cpp:257
SymmetricKey derive_secret(const std::vector< uint8_t > &eph_public_key_bin, const EC_Point &other_public_key_point) const
Definition: ecies.cpp:160
ECIES_KA_Operation(const PK_Key_Agreement_Key &private_key, const ECIES_KA_Params &ecies_params, bool for_encryption, RandomNumberGenerator &rng)
Definition: ecies.cpp:148
ECIES_KA_Params(const EC_Group &domain, std::string_view kdf_spec, size_t length, EC_Point_Format compression_type, ECIES_Flags flags)
Definition: ecies.cpp:198
bool check_mode() const
Definition: ecies.h:101
size_t secret_length() const
Definition: ecies.h:81
bool old_cofactor_mode() const
Definition: ecies.h:96
const std::string kdf_spec() const
Definition: ecies.h:111
EC_Point_Format compression_type() const
Definition: ecies.h:106
bool cofactor_mode() const
Definition: ecies.h:91
bool single_hash_mode() const
Definition: ecies.h:86
const EC_Group & domain() const
Definition: ecies.h:76
size_t dem_keylen() const
returns the length of the key used by the data encryption method
Definition: ecies.h:164
ECIES_System_Params(const EC_Group &domain, std::string_view kdf_spec, std::string_view dem_algo_spec, size_t dem_key_len, std::string_view mac_spec, size_t mac_key_len)
Definition: ecies.cpp:232
size_t mac_keylen() const
returns the length of the key used by the message authentication code
Definition: ecies.h:170
std::unique_ptr< Cipher_Mode > create_cipher(Cipher_Dir direction) const
creates an instance of the data encryption method
Definition: ecies.cpp:248
std::unique_ptr< MessageAuthenticationCode > create_mac() const
creates an instance of the message authentication code
Definition: ecies.cpp:243
const BigInt & get_cofactor() const
Definition: ec_group.cpp:560
const BigInt & get_order() const
Definition: ec_group.cpp:545
size_t point_size(EC_Point_Format format) const
Definition: ec_group.cpp:600
EC_Point OS2ECP(const uint8_t bits[], size_t len) const
Definition: ec_group.cpp:609
bool is_zero() const
Definition: ec_point.h:183
std::vector< uint8_t > encode(EC_Point_Format format) const
Definition: ec_point.cpp:600
static std::unique_ptr< KDF > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition: kdf.cpp:213
static std::unique_ptr< MessageAuthenticationCode > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition: mac.cpp:134
secure_vector< uint8_t > bits_of() const
Definition: symkey.h:35
const uint8_t * end() const
Definition: symkey.h:45
const uint8_t * begin() const
Definition: symkey.h:40
size_t size() const
Definition: symkey.h:29
bool empty() const
Definition: symkey.h:30
SymmetricKey derive_key(size_t key_len, const uint8_t in[], size_t in_len, const uint8_t params[], size_t params_len) const
Definition: pubkey.cpp:234
int(* final)(unsigned char *, CTX *)
#define BOTAN_DIAGNOSTIC_POP
Definition: compiler.h:204
#define BOTAN_DIAGNOSTIC_PUSH
Definition: compiler.h:201
#define BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
Definition: compiler.h:203
Definition: alg_id.cpp:12
PublicKeyOperation
Definition: pk_keys.h:43
size_t buffer_insert(std::vector< T, Alloc > &buf, size_t buf_offset, const T input[], size_t input_length)
Definition: mem_ops.h:221
EC_Point_Format
Definition: ec_point.h:19
OctetString SymmetricKey
Definition: symkey.h:145
Cipher_Dir
Definition: cipher_mode.h:26
uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len)
Definition: mem_ops.cpp:66
ECIES_Flags
Definition: ecies.h:27
BigInt gcd(const BigInt &a, const BigInt &b)
Definition: numthry.cpp:220
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:64