Botan  2.11.0
Crypto and TLS for C++11
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 
15 namespace Botan {
16 
17 namespace {
18 
19 /**
20 * Private key type for ECIES_ECDH_KA_Operation
21 */
22 class 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 */
55 class 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 
88 std::unique_ptr<PK_Ops::Key_Agreement>
89 ECIES_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 */
105 PK_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,
135  RandomNumberGenerator& rng) :
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 */
144 SymmetricKey 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 
182 ECIES_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 
192 ECIES_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 
209 ECIES_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,
213  ECIES_Flags::NONE)
214  {
215  }
216 
217 std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const
218  {
220  }
221 
222 std::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,
233  RandomNumberGenerator& rng) :
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 
259 size_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 
268 size_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 */
278 std::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)
291  {
292  m_cipher->start(m_iv.bits_of());
293  }
294  secure_vector<uint8_t> encrypted_data(data, data + length);
295  m_cipher->finish(encrypted_data);
296 
297  // concat elements
298 
299  std::vector<uint8_t> out(m_eph_public_key_bin.size() + encrypted_data.size() + m_mac->output_length());
300  buffer_insert(out, 0, m_eph_public_key_bin);
301  buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data);
302 
303  // mac
304  m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
305  m_mac->update(encrypted_data);
306  if(!m_label.empty())
307  {
308  m_mac->update(m_label);
309  }
310  m_mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size());
311 
312  return out;
313  }
314 
315 
317  const ECIES_System_Params& ecies_params,
318  RandomNumberGenerator& rng) :
319  m_ka(key, ecies_params, false, rng),
320  m_params(ecies_params),
321  m_iv(),
322  m_label()
323  {
324  // ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order)
325  if(!ecies_params.check_mode())
326  {
327  const Botan::BigInt& cofactor = m_params.domain().get_cofactor();
328  if(cofactor > 1 && Botan::gcd(cofactor, m_params.domain().get_order()) != 1)
329  {
330  throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0");
331  }
332  }
333 
334  m_mac = m_params.create_mac();
335  m_cipher = m_params.create_cipher(DECRYPTION);
336  }
337 
338 size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const
339  {
340  const size_t point_size = m_params.domain().point_size(m_params.compression_type());
341  const size_t overhead = point_size + m_mac->output_length();
342 
343  if(ctext_len < overhead)
344  return 0;
345 
346  return m_cipher->output_length(ctext_len - overhead);
347  }
348 
349 /**
350 * ECIES Decryption according to ISO 18033-2
351 */
352 secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const
353  {
354  const size_t point_size = m_params.domain().point_size(m_params.compression_type());
355 
356  if(in_len < point_size + m_mac->output_length())
357  {
358  throw Decoding_Error("ECIES decryption: ciphertext is too short");
359  }
360 
361  // extract data
362  const std::vector<uint8_t> other_public_key_bin(in, in + point_size); // the received (ephemeral) public key
363  const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length());
364  const std::vector<uint8_t> mac_data(in + in_len - m_mac->output_length(), in + in_len);
365 
366  // ISO 18033: step a
367  PointGFp other_public_key = m_params.domain().OS2ECP(other_public_key_bin);
368 
369  // ISO 18033: step b
370  if(m_params.check_mode() && !other_public_key.on_the_curve())
371  {
372  throw Decoding_Error("ECIES decryption: received public key is not on the curve");
373  }
374 
375  // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree)
376  // throws Illegal_Transformation if the point is zero)
377  const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);
378 
379  // validate mac
380  m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
381  m_mac->update(encrypted_data);
382  if(!m_label.empty())
383  {
384  m_mac->update(m_label);
385  }
386  const secure_vector<uint8_t> calculated_mac = m_mac->final();
387  valid_mask = ct_compare_u8(mac_data.data(), calculated_mac.data(), mac_data.size());
388 
389  if(valid_mask)
390  {
391  // decrypt data
392 
393  m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
394  if(m_iv.size() != 0)
395  {
396  m_cipher->start(m_iv.bits_of());
397  }
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 }
std::unique_ptr< MessageAuthenticationCode > create_mac() const
creates an instance of the message authentication code
Definition: ecies.cpp:217
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
SymmetricKey derive_secret(const std::vector< uint8_t > &eph_public_key_bin, const PointGFp &other_public_key_point) const
Definition: ecies.cpp:144
BigInt gcd(const BigInt &a, const BigInt &b)
Definition: numthry.cpp:52
const BigInt & get_order() const
Definition: ec_group.cpp:479
ECIES_Flags
Definition: ecies.h:27
size_t size() const
Definition: symkey.h:26
std::vector< uint8_t > encode(PointGFp::Compression_Type format) const
Definition: point_gfp.cpp:595
bool check_mode() const
Definition: ecies.h:100
static std::vector< uint8_t > encode(const BigInt &n)
Definition: bigint.h:768
PointGFp::Compression_Type compression_type() const
Definition: ecies.h:105
int(* final)(unsigned char *, CTX *)
Flags flags(Flag flags)
Definition: p11.h:858
size_t dem_keylen() const
returns the length of the key used by the data encryption method
Definition: ecies.h:163
size_t point_size(PointGFp::Compression_Type format) const
Definition: ec_group.cpp:529
const std::string & kdf_spec() const
Definition: ecies.h:110
ECIES_Encryptor(const PK_Key_Agreement_Key &private_key, const ECIES_System_Params &ecies_params, RandomNumberGenerator &rng)
Definition: ecies.cpp:231
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 single_hash_mode() const
Definition: ecies.h:85
uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len)
Definition: mem_ops.cpp:53
bool cofactor_mode() const
Definition: ecies.h:90
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 mac_keylen() const
returns the length of the key used by the message authentication code
Definition: ecies.h:169
const BigInt & get_cofactor() const
Definition: ec_group.cpp:494
PointGFp OS2ECP(const uint8_t bits[], size_t len) const
Definition: ec_group.cpp:538
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
const uint8_t * end() const
Definition: symkey.h:41
Definition: alg_id.cpp:13
std::unique_ptr< Cipher_Mode > create_cipher(Botan::Cipher_Dir direction) const
creates an instance of the data encryption method
Definition: ecies.cpp:222
OctetString SymmetricKey
Definition: symkey.h:141
size_t bytes() const
Definition: bigint.cpp:266
const uint8_t * begin() const
Definition: symkey.h:36
bool old_cofactor_mode() const
Definition: ecies.h:95
const EC_Group & domain() const
Definition: ecies.h:75
Cipher_Dir
Definition: cipher_mode.h:23
ECIES_Decryptor(const PK_Key_Agreement_Key &private_key, const ECIES_System_Params &ecies_params, RandomNumberGenerator &rng)
Definition: ecies.cpp:316
size_t buffer_insert(std::vector< T, Alloc > &buf, size_t buf_offset, const T input[], size_t input_length)
Definition: secmem.h:80
static std::unique_ptr< Cipher_Mode > create_or_throw(const std::string &algo, Cipher_Dir direction, const std::string &provider="")
Definition: cipher_mode.cpp:44
bool is_zero() const
Definition: point_gfp.h:183
static std::unique_ptr< MessageAuthenticationCode > create_or_throw(const std::string &algo_spec, const std::string &provider="")
Definition: mac.cpp:141
static secure_vector< uint8_t > encode_1363(const BigInt &n, size_t bytes)
Definition: big_code.cpp:111
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:65
size_t secret_length() const
Definition: ecies.h:80
secure_vector< uint8_t > bits_of() const
Definition: symkey.h:31
static std::unique_ptr< KDF > create_or_throw(const std::string &algo_spec, const std::string &provider="")
Definition: kdf.cpp:222
const RSA_PrivateKey & m_key
Definition: rsa.cpp:296