• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5133556677

31 May 2023 02:11PM UTC coverage: 91.735% (-0.3%) from 92.012%
5133556677

Pull #3568

github

web-flow
Merge de48a2eb6 into 1cbeffafb
Pull Request #3568: Change clang-format AllowShortBlocksOnASingleLine from true to Empty

76059 of 82912 relevant lines covered (91.73%)

12004312.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.24
/src/lib/pubkey/ecies/ecies.cpp
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

11
#include <botan/cipher_mode.h>
12
#include <botan/mac.h>
13
#include <botan/numthry.h>
14
#include <botan/internal/pk_ops_impl.h>
15

16
namespace Botan {
17

18
namespace {
19

20
/**
21
* Private key type for ECIES_ECDH_KA_Operation
22
*/
23

24
BOTAN_DIAGNOSTIC_PUSH
25
BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
26

27
class ECIES_PrivateKey final : public EC_PrivateKey,
28
                               public PK_Key_Agreement_Key {
29
   public:
30
      explicit ECIES_PrivateKey(const ECDH_PrivateKey& private_key) :
113✔
31
            EC_PublicKey(private_key), EC_PrivateKey(private_key), PK_Key_Agreement_Key(), m_key(private_key) {}
113✔
32

33
      std::vector<uint8_t> public_value() const override { return m_key.public_value(); }
×
34

35
      std::string algo_name() const override { return "ECIES"; }
×
36

37
      std::unique_ptr<Public_Key> public_key() const override { return m_key.public_key(); }
×
38

39
      bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::KeyAgreement); }
×
40

41
      std::unique_ptr<PK_Ops::Key_Agreement> create_key_agreement_op(RandomNumberGenerator& rng,
42
                                                                     std::string_view params,
43
                                                                     std::string_view provider) const override;
44

45
   private:
46
      ECDH_PrivateKey m_key;
47
};
48

49
BOTAN_DIAGNOSTIC_POP
50

51
/**
52
* Implements ECDH key agreement without using the cofactor mode
53
*/
54
class ECIES_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF {
×
55
   public:
56
      ECIES_ECDH_KA_Operation(const ECIES_PrivateKey& private_key, RandomNumberGenerator& rng) :
113✔
57
            PK_Ops::Key_Agreement_with_KDF("Raw"), m_key(private_key), m_rng(rng) {}
113✔
58

59
      size_t agreed_value_size() const override { return m_key.domain().get_p_bytes(); }
×
60

61
      secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override {
153✔
62
         const EC_Group& group = m_key.domain();
153✔
63

64
         EC_Point input_point = group.OS2ECP(w, w_len);
153✔
65
         input_point.randomize_repr(m_rng);
153✔
66

67
         const EC_Point S = group.blinded_var_point_multiply(input_point, m_key.private_value(), m_rng, m_ws);
153✔
68

69
         if(S.on_the_curve() == false) {
153✔
70
            throw Internal_Error("ECDH agreed value was not on the curve");
×
71
         }
72
         return BigInt::encode_1363(S.get_affine_x(), group.get_p_bytes());
306✔
73
      }
153✔
74

75
   private:
76
      ECIES_PrivateKey m_key;
77
      RandomNumberGenerator& m_rng;
78
      std::vector<BigInt> m_ws;
79
};
80

81
std::unique_ptr<PK_Ops::Key_Agreement> ECIES_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
113✔
82
                                                                                 std::string_view /*params*/,
83
                                                                                 std::string_view /*provider*/) const {
84
   return std::make_unique<ECIES_ECDH_KA_Operation>(*this, rng);
113✔
85
}
86

87
/**
88
* Creates a PK_Key_Agreement instance for the given key and ecies_params
89
* Returns either ECIES_ECDH_KA_Operation or the default implementation for the given key,
90
* depending on the key and ecies_params
91
* @param private_key the private key used for the key agreement
92
* @param ecies_params settings for ecies
93
* @param for_encryption disable cofactor mode if the secret will be used for encryption
94
* (according to ISO 18033 cofactor mode is only used during decryption)
95
*/
96
PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key,
129✔
97
                                      const ECIES_KA_Params& ecies_params,
98
                                      bool for_encryption,
99
                                      RandomNumberGenerator& rng) {
100
   const ECDH_PrivateKey* ecdh_key = dynamic_cast<const ECDH_PrivateKey*>(&private_key);
129✔
101

102
   if(ecdh_key == nullptr &&
129✔
103
      (ecies_params.cofactor_mode() || ecies_params.old_cofactor_mode() || ecies_params.check_mode())) {
×
104
      // assume we have a private key from an external provider (e.g. pkcs#11):
105
      // there is no way to determine or control whether the provider uses cofactor mode or not.
106
      // ISO 18033 does not allow cofactor mode in combination with old cofactor mode or check mode
107
      // => disable cofactor mode, old cofactor mode and check mode for unknown keys/providers (as a precaution).
108
      throw Invalid_Argument("ECIES: cofactor, old cofactor and check mode are only supported for ECDH_PrivateKey");
×
109
   }
110

111
   if(ecdh_key && (for_encryption || !ecies_params.cofactor_mode())) {
129✔
112
      // ECDH_KA_Operation uses cofactor mode: use own key agreement method if cofactor should not be used.
113
      return PK_Key_Agreement(ECIES_PrivateKey(*ecdh_key), rng, "Raw");
113✔
114
   }
115

116
   return PK_Key_Agreement(private_key, rng, "Raw");  // use default implementation
16✔
117
}
118
}  // namespace
119

120
ECIES_KA_Operation::ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key,
129✔
121
                                       const ECIES_KA_Params& ecies_params,
122
                                       bool for_encryption,
123
                                       RandomNumberGenerator& rng) :
129✔
124
      m_ka(create_key_agreement(private_key, ecies_params, for_encryption, rng)), m_params(ecies_params) {}
129✔
125

126
/**
127
* ECIES secret derivation according to ISO 18033-2
128
*/
129
SymmetricKey ECIES_KA_Operation::derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
185✔
130
                                               const EC_Point& other_public_key_point) const {
131
   if(other_public_key_point.is_zero()) {
187✔
132
      throw Invalid_Argument("ECIES: other public key point is zero");
×
133
   }
134

135
   auto kdf = KDF::create_or_throw(m_params.kdf_spec());
185✔
136

137
   EC_Point other_point = other_public_key_point;
185✔
138

139
   // ISO 18033: step b
140
   if(m_params.old_cofactor_mode()) {
185✔
141
      other_point *= m_params.domain().get_cofactor();
39✔
142
   }
143

144
   secure_vector<uint8_t> derivation_input;
185✔
145

146
   // ISO 18033: encryption step e / decryption step g
147
   if(!m_params.single_hash_mode()) {
185✔
148
      derivation_input += eph_public_key_bin;
80✔
149
   }
150

151
   // ISO 18033: encryption step f / decryption step h
152
   std::vector<uint8_t> other_public_key_bin = other_point.encode(m_params.compression_type());
185✔
153
   // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because
154
   // "Raw" is passed to the `PK_Key_Agreement` if the implementation of botan is used.
155
   const SymmetricKey peh =
185✔
156
      m_ka.derive_key(m_params.domain().get_order().bytes(), other_public_key_bin.data(), other_public_key_bin.size());
185✔
157
   derivation_input.insert(derivation_input.end(), peh.begin(), peh.end());
185✔
158

159
   // ISO 18033: encryption step g / decryption step i
160
   return SymmetricKey(kdf->derive_key(m_params.secret_length(), derivation_input));
185✔
161
}
740✔
162

163
ECIES_KA_Params::ECIES_KA_Params(const EC_Group& domain,
116✔
164
                                 std::string_view kdf_spec,
165
                                 size_t length,
166
                                 EC_Point_Format compression_type,
167
                                 ECIES_Flags flags) :
116✔
168
      m_domain(domain), m_kdf_spec(kdf_spec), m_length(length), m_compression_mode(compression_type), m_flags(flags) {}
232✔
169

170
ECIES_System_Params::ECIES_System_Params(const EC_Group& domain,
114✔
171
                                         std::string_view kdf_spec,
172
                                         std::string_view dem_algo_spec,
173
                                         size_t dem_key_len,
174
                                         std::string_view mac_spec,
175
                                         size_t mac_key_len,
176
                                         EC_Point_Format compression_type,
177
                                         ECIES_Flags flags) :
114✔
178
      ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags),
179
      m_dem_spec(dem_algo_spec),
162✔
180
      m_dem_keylen(dem_key_len),
114✔
181
      m_mac_spec(mac_spec),
276✔
182
      m_mac_keylen(mac_key_len) {
114✔
183
   // ISO 18033: "At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1."
184
   if(size_t(cofactor_mode()) + size_t(old_cofactor_mode()) + size_t(check_mode()) > 1) {
114✔
185
      throw Invalid_Argument("ECIES: only one of cofactor_mode, old_cofactor_mode and check_mode can be set");
48✔
186
   }
187
}
162✔
188

189
ECIES_System_Params::ECIES_System_Params(const EC_Group& domain,
2✔
190
                                         std::string_view kdf_spec,
191
                                         std::string_view dem_algo_spec,
192
                                         size_t dem_key_len,
193
                                         std::string_view mac_spec,
194
                                         size_t mac_key_len) :
2✔
195
      ECIES_System_Params(domain,
196
                          kdf_spec,
197
                          dem_algo_spec,
198
                          dem_key_len,
199
                          mac_spec,
200
                          mac_key_len,
201
                          EC_Point_Format::Uncompressed,
202
                          ECIES_Flags::None) {}
2✔
203

204
std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const {
127✔
205
   return MessageAuthenticationCode::create_or_throw(m_mac_spec);
127✔
206
}
207

208
std::unique_ptr<Cipher_Mode> ECIES_System_Params::create_cipher(Cipher_Dir direction) const {
126✔
209
   return Cipher_Mode::create_or_throw(m_dem_spec, direction);
126✔
210
}
211

212
/*
213
* ECIES_Encryptor Constructor
214
*/
215
ECIES_Encryptor::ECIES_Encryptor(const PK_Key_Agreement_Key& private_key,
65✔
216
                                 const ECIES_System_Params& ecies_params,
217
                                 RandomNumberGenerator& rng) :
65✔
218
      m_ka(private_key, ecies_params, true, rng),
67✔
219
      m_params(ecies_params),
65✔
220
      m_eph_public_key_bin(private_key.public_value()),  // returns the uncompressed public key, see conversion below
65✔
221
      m_iv(),
65✔
222
      m_other_point(),
65✔
223
      m_label() {
195✔
224
   if(ecies_params.compression_type() != EC_Point_Format::Uncompressed) {
65✔
225
      // ISO 18033: step d
226
      // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format
227
      m_eph_public_key_bin = m_params.domain().OS2ECP(m_eph_public_key_bin).encode(ecies_params.compression_type());
41✔
228
   }
229
   m_mac = m_params.create_mac();
65✔
230
   m_cipher = m_params.create_cipher(Cipher_Dir::Encryption);
64✔
231
}
70✔
232

233
/*
234
* ECIES_Encryptor Constructor
235
*/
236
ECIES_Encryptor::ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_Params& ecies_params) :
×
237
      ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.domain()), ecies_params, rng) {}
×
238

239
size_t ECIES_Encryptor::maximum_input_size() const {
×
240
   /*
241
   ECIES should just be used for key transport so this (arbitrary) limit
242
   seems sufficient
243
   */
244
   return 64;
×
245
}
246

247
size_t ECIES_Encryptor::ciphertext_length(size_t ptext_len) const {
×
248
   return m_eph_public_key_bin.size() + m_mac->output_length() + m_cipher->output_length(ptext_len);
×
249
}
250

251
/*
252
* ECIES Encryption according to ISO 18033-2
253
*/
254
std::vector<uint8_t> ECIES_Encryptor::enc(const uint8_t data[],
63✔
255
                                          size_t length,
256
                                          RandomNumberGenerator& /*unused*/) const {
257
   if(m_other_point.is_zero()) {
65✔
258
      throw Invalid_State("ECIES: the other key is zero");
2✔
259
   }
260

261
   const SymmetricKey secret_key = m_ka.derive_secret(m_eph_public_key_bin, m_other_point);
61✔
262

263
   // encryption
264

265
   m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
122✔
266
   if(m_iv.empty() && !m_cipher->valid_nonce_length(m_iv.size())) {
61✔
267
      throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
×
268
   }
269

270
   m_cipher->start(m_iv.bits_of());
61✔
271

272
   secure_vector<uint8_t> encrypted_data(data, data + length);
61✔
273
   m_cipher->finish(encrypted_data);
61✔
274

275
   // concat elements
276

277
   std::vector<uint8_t> out(m_eph_public_key_bin.size() + encrypted_data.size() + m_mac->output_length());
61✔
278
   buffer_insert(out, 0, m_eph_public_key_bin);
61✔
279
   buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data);
61✔
280

281
   // mac
282
   m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
61✔
283
   m_mac->update(encrypted_data);
61✔
284
   if(!m_label.empty()) {
61✔
285
      m_mac->update(m_label);
11✔
286
   }
287
   m_mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size());
61✔
288

289
   return out;
61✔
290
}
122✔
291

292
ECIES_Decryptor::ECIES_Decryptor(const PK_Key_Agreement_Key& key,
62✔
293
                                 const ECIES_System_Params& ecies_params,
294
                                 RandomNumberGenerator& rng) :
62✔
295
      m_ka(key, ecies_params, false, rng), m_params(ecies_params), m_iv(), m_label() {
62✔
296
   // ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order)
297
   if(!ecies_params.check_mode()) {
62✔
298
      const BigInt& cofactor = m_params.domain().get_cofactor();
50✔
299
      if(cofactor > 1 && gcd(cofactor, m_params.domain().get_order()) != 1) {
55✔
300
         throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0");
×
301
      }
302
   }
303

304
   m_mac = m_params.create_mac();
62✔
305
   m_cipher = m_params.create_cipher(Cipher_Dir::Decryption);
62✔
306
}
62✔
307

308
size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const {
×
309
   const size_t point_size = m_params.domain().point_size(m_params.compression_type());
×
310
   const size_t overhead = point_size + m_mac->output_length();
×
311

312
   if(ctext_len < overhead) {
×
313
      return 0;
314
   }
315

316
   return m_cipher->output_length(ctext_len - overhead);
×
317
}
318

319
/**
320
* ECIES Decryption according to ISO 18033-2
321
*/
322
secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const {
123✔
323
   const size_t point_size = m_params.domain().point_size(m_params.compression_type());
123✔
324

325
   if(in_len < point_size + m_mac->output_length()) {
123✔
326
      throw Decoding_Error("ECIES decryption: ciphertext is too short");
1✔
327
   }
328

329
   // extract data
330
   const std::vector<uint8_t> other_public_key_bin(in, in + point_size);  // the received (ephemeral) public key
122✔
331
   const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length());
122✔
332
   const std::vector<uint8_t> mac_data(in + in_len - m_mac->output_length(), in + in_len);
122✔
333

334
   // ISO 18033: step a
335
   EC_Point other_public_key = m_params.domain().OS2ECP(other_public_key_bin);
122✔
336

337
   // ISO 18033: step b
338
   if(m_params.check_mode() && !other_public_key.on_the_curve()) {
122✔
339
      throw Decoding_Error("ECIES decryption: received public key is not on the curve");
×
340
   }
341

342
   // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree)
343
   // throws Illegal_Transformation if the point is zero)
344
   const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);
122✔
345

346
   // validate mac
347
   m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
122✔
348
   m_mac->update(encrypted_data);
122✔
349
   if(!m_label.empty()) {
122✔
350
      m_mac->update(m_label);
22✔
351
   }
352
   const secure_vector<uint8_t> calculated_mac = m_mac->final();
122✔
353
   valid_mask = ct_compare_u8(mac_data.data(), calculated_mac.data(), mac_data.size());
122✔
354

355
   if(valid_mask) {
122✔
356
      // decrypt data
357

358
      m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
122✔
359
      if(m_iv.empty() && !m_cipher->valid_nonce_length(m_iv.size())) {
61✔
360
         throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
×
361
      }
362
      m_cipher->start(m_iv.bits_of());
61✔
363

364
      try {
61✔
365
         // the decryption can fail:
366
         // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
367
         secure_vector<uint8_t> decrypted_data(encrypted_data.begin(), encrypted_data.end());
61✔
368
         m_cipher->finish(decrypted_data);
61✔
369
         return decrypted_data;
61✔
370
      } catch(...) {
61✔
371
         valid_mask = 0;
×
372
      }
×
373
   }
374
   return secure_vector<uint8_t>();
122✔
375
}
610✔
376

377
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc