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