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