Botan 3.5.0
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
70 EC_Point input_point = group.OS2ECP(w, w_len);
71 input_point.randomize_repr(m_rng);
72
73 const EC_Point S = group.blinded_var_point_multiply(input_point, m_key.private_value(), m_rng, m_ws);
74
75 if(S.on_the_curve() == false) {
76 throw Internal_Error("ECDH agreed value was not on the curve");
77 }
78
79 return S.x_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> ECIES_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
89 std::string_view /*params*/,
90 std::string_view /*provider*/) const {
91 return std::make_unique<ECIES_ECDH_KA_Operation>(*this, rng);
92}
93
94/**
95* Creates a PK_Key_Agreement instance for the given key and ecies_params
96* Returns either ECIES_ECDH_KA_Operation or the default implementation for the given key,
97* depending on the key and ecies_params
98* @param private_key the private key used for the key agreement
99* @param ecies_params settings for ecies
100* @param for_encryption disable cofactor mode if the secret will be used for encryption
101* (according to ISO 18033 cofactor mode is only used during decryption)
102*/
103PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key,
104 const ECIES_KA_Params& ecies_params,
105 bool for_encryption,
106 RandomNumberGenerator& rng) {
107 const ECDH_PrivateKey* ecdh_key = dynamic_cast<const ECDH_PrivateKey*>(&private_key);
108
109 if(ecdh_key == nullptr &&
110 (ecies_params.cofactor_mode() || ecies_params.old_cofactor_mode() || ecies_params.check_mode())) {
111 // assume we have a private key from an external provider (e.g. pkcs#11):
112 // there is no way to determine or control whether the provider uses cofactor mode or not.
113 // ISO 18033 does not allow cofactor mode in combination with old cofactor mode or check mode
114 // => disable cofactor mode, old cofactor mode and check mode for unknown keys/providers (as a precaution).
115 throw Invalid_Argument("ECIES: cofactor, old cofactor and check mode are only supported for ECDH_PrivateKey");
116 }
117
118 if(ecdh_key && (for_encryption || !ecies_params.cofactor_mode())) {
119 // ECDH_KA_Operation uses cofactor mode: use own key agreement method if cofactor should not be used.
120 return PK_Key_Agreement(ECIES_PrivateKey(*ecdh_key), rng, "Raw");
121 }
122
123 return PK_Key_Agreement(private_key, rng, "Raw"); // use default implementation
124}
125} // namespace
126
128 const ECIES_KA_Params& ecies_params,
129 bool for_encryption,
131 m_ka(create_key_agreement(private_key, ecies_params, for_encryption, rng)), m_params(ecies_params) {}
132
133/**
134* ECIES secret derivation according to ISO 18033-2
135*/
136SymmetricKey ECIES_KA_Operation::derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
137 const EC_Point& other_public_key_point) const {
138 if(other_public_key_point.is_zero()) {
139 throw Invalid_Argument("ECIES: other public key point is zero");
140 }
141
142 auto kdf = KDF::create_or_throw(m_params.kdf_spec());
143
144 EC_Point other_point = other_public_key_point;
145
146 // ISO 18033: step b
147 if(m_params.old_cofactor_mode()) {
148 other_point *= m_params.domain().get_cofactor();
149 }
150
151 secure_vector<uint8_t> derivation_input;
152
153 // ISO 18033: encryption step e / decryption step g
154 if(!m_params.single_hash_mode()) {
155 derivation_input += eph_public_key_bin;
156 }
157
158 // ISO 18033: encryption step f / decryption step h
159 std::vector<uint8_t> other_public_key_bin = other_point.encode(m_params.compression_type());
160 // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because
161 // "Raw" is passed to the `PK_Key_Agreement` if the implementation of botan is used.
162 const SymmetricKey peh =
163 m_ka.derive_key(m_params.domain().get_order_bytes(), other_public_key_bin.data(), other_public_key_bin.size());
164 derivation_input.insert(derivation_input.end(), peh.begin(), peh.end());
165
166 // ISO 18033: encryption step g / decryption step i
167 return SymmetricKey(kdf->derive_key(m_params.secret_length(), derivation_input));
168}
169
171 std::string_view kdf_spec,
172 size_t length,
173 EC_Point_Format compression_type,
174 ECIES_Flags flags) :
175 m_domain(domain), m_kdf_spec(kdf_spec), m_length(length), m_compression_mode(compression_type), m_flags(flags) {}
176
178 std::string_view kdf_spec,
179 std::string_view dem_algo_spec,
180 size_t dem_key_len,
181 std::string_view mac_spec,
182 size_t mac_key_len,
183 EC_Point_Format compression_type,
184 ECIES_Flags flags) :
185 ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags),
186 m_dem_spec(dem_algo_spec),
187 m_dem_keylen(dem_key_len),
188 m_mac_spec(mac_spec),
189 m_mac_keylen(mac_key_len) {
190 // ISO 18033: "At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1."
191 if(size_t(cofactor_mode()) + size_t(old_cofactor_mode()) + size_t(check_mode()) > 1) {
192 throw Invalid_Argument("ECIES: only one of cofactor_mode, old_cofactor_mode and check_mode can be set");
193 }
194}
195
197 std::string_view kdf_spec,
198 std::string_view dem_algo_spec,
199 size_t dem_key_len,
200 std::string_view mac_spec,
201 size_t mac_key_len) :
202 ECIES_System_Params(domain,
203 kdf_spec,
204 dem_algo_spec,
205 dem_key_len,
206 mac_spec,
207 mac_key_len,
209 ECIES_Flags::None) {}
210
211std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const {
213}
214
215std::unique_ptr<Cipher_Mode> ECIES_System_Params::create_cipher(Cipher_Dir direction) const {
216 return Cipher_Mode::create_or_throw(m_dem_spec, direction);
217}
218
219/*
220* ECIES_Encryptor Constructor
221*/
223 const ECIES_System_Params& ecies_params,
225 m_ka(private_key, ecies_params, true, rng),
226 m_params(ecies_params),
227 m_eph_public_key_bin(private_key.public_value()), // returns the uncompressed public key, see conversion below
228 m_iv(),
229 m_other_point(),
230 m_label() {
231 if(ecies_params.compression_type() != EC_Point_Format::Uncompressed) {
232 // ISO 18033: step d
233 // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format
234 m_eph_public_key_bin = m_params.domain().OS2ECP(m_eph_public_key_bin).encode(ecies_params.compression_type());
235 }
236 m_mac = m_params.create_mac();
237 m_cipher = m_params.create_cipher(Cipher_Dir::Encryption);
238}
239
240/*
241* ECIES_Encryptor Constructor
242*/
244 ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.domain()), ecies_params, rng) {}
245
246size_t ECIES_Encryptor::maximum_input_size() const {
247 /*
248 ECIES should just be used for key transport so this (arbitrary) limit
249 seems sufficient
250 */
251 return 64;
252}
253
254size_t ECIES_Encryptor::ciphertext_length(size_t ptext_len) const {
255 return m_eph_public_key_bin.size() + m_mac->output_length() + m_cipher->output_length(ptext_len);
256}
257
258/*
259* ECIES Encryption according to ISO 18033-2
260*/
261std::vector<uint8_t> ECIES_Encryptor::enc(const uint8_t data[],
262 size_t length,
263 RandomNumberGenerator& /*unused*/) const {
264 if(m_other_point.is_zero()) {
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
272 m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
273 if(m_iv.empty() && !m_cipher->valid_nonce_length(m_iv.size())) {
274 throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
275 }
276
277 m_cipher->start(m_iv.bits_of());
278
279 secure_vector<uint8_t> encrypted_data(data, data + length);
280 m_cipher->finish(encrypted_data);
281
282 // compute the MAC
283 m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
284 m_mac->update(encrypted_data);
285 if(!m_label.empty()) {
286 m_mac->update(m_label);
287 }
288 const auto mac = m_mac->final();
289
290 // concat elements
291 return concat(m_eph_public_key_bin, encrypted_data, mac);
292}
293
295 const ECIES_System_Params& ecies_params,
297 m_ka(key, ecies_params, false, rng), m_params(ecies_params), m_iv(), m_label() {
298 // ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order)
299 if(!ecies_params.check_mode()) {
300 const BigInt& cofactor = m_params.domain().get_cofactor();
301 if(cofactor > 1 && gcd(cofactor, m_params.domain().get_order()) != 1) {
302 throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0");
303 }
304 }
305
306 m_mac = m_params.create_mac();
307 m_cipher = m_params.create_cipher(Cipher_Dir::Decryption);
308}
309
310size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const {
311 const size_t point_size = m_params.domain().point_size(m_params.compression_type());
312 const size_t overhead = point_size + m_mac->output_length();
313
314 if(ctext_len < overhead) {
315 return 0;
316 }
317
318 return m_cipher->output_length(ctext_len - overhead);
319}
320
321/**
322* ECIES Decryption according to ISO 18033-2
323*/
324secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const {
325 const size_t point_size = m_params.domain().point_size(m_params.compression_type());
326
327 if(in_len < point_size + m_mac->output_length()) {
328 throw Decoding_Error("ECIES decryption: ciphertext is too short");
329 }
330
331 // extract data
332 const std::vector<uint8_t> other_public_key_bin(in, in + point_size); // the received (ephemeral) public key
333 const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length());
334 const std::vector<uint8_t> mac_data(in + in_len - m_mac->output_length(), in + in_len);
335
336 // ISO 18033: step a
337 EC_Point other_public_key = m_params.domain().OS2ECP(other_public_key_bin);
338
339 // ISO 18033: step b
340 if(m_params.check_mode() && !other_public_key.on_the_curve()) {
341 throw Decoding_Error("ECIES decryption: received public key is not on the curve");
342 }
343
344 // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree)
345 // throws Illegal_Transformation if the point is zero)
346 const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);
347
348 // validate mac
349 m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
350 m_mac->update(encrypted_data);
351 if(!m_label.empty()) {
352 m_mac->update(m_label);
353 }
354 const secure_vector<uint8_t> calculated_mac = m_mac->final();
355 valid_mask = CT::is_equal(mac_data.data(), calculated_mac.data(), mac_data.size()).value();
356
357 if(valid_mask) {
358 // decrypt data
359
360 m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
361 if(m_iv.empty() && !m_cipher->valid_nonce_length(m_iv.size())) {
362 throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
363 }
364 m_cipher->start(m_iv.bits_of());
365
366 try {
367 // the decryption can fail:
368 // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
369 secure_vector<uint8_t> decrypted_data(encrypted_data.begin(), encrypted_data.end());
370 m_cipher->finish(decrypted_data);
371 return decrypted_data;
372 } catch(...) {
373 valid_mask = 0;
374 }
375 }
376 return secure_vector<uint8_t>();
377}
378
379} // 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:294
ECIES_Encryptor(const PK_Key_Agreement_Key &private_key, const ECIES_System_Params &ecies_params, RandomNumberGenerator &rng)
Definition ecies.cpp:222
SymmetricKey derive_secret(const std::vector< uint8_t > &eph_public_key_bin, const EC_Point &other_public_key_point) const
Definition ecies.cpp:136
ECIES_KA_Operation(const PK_Key_Agreement_Key &private_key, const ECIES_KA_Params &ecies_params, bool for_encryption, RandomNumberGenerator &rng)
Definition ecies.cpp:127
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:170
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:196
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:215
std::unique_ptr< MessageAuthenticationCode > create_mac() const
creates an instance of the message authentication code
Definition ecies.cpp:211
const BigInt & get_cofactor() const
Definition ec_group.cpp:583
const BigInt & get_order() const
Definition ec_group.cpp:571
size_t point_size(EC_Point_Format format) const
Definition ec_group.cpp:619
EC_Point OS2ECP(const uint8_t bits[], size_t len) const
Definition ec_group.cpp:628
size_t get_order_bytes() const
Definition ec_group.cpp:551
bool is_zero() const
Definition ec_point.h:156
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
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:486
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:262
BigInt gcd(const BigInt &a, const BigInt &b)
Definition numthry.cpp:193
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61