Botan 2.1.0 Crypto and TLS for C++11

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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