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