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