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/numthry.h>
11 #include <botan/cipher_mode.h>
12 #include <botan/mac.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 final : public EC_PrivateKey, public PK_Key_Agreement_Key
23    {
24    public:
ECIES_PrivateKey(const ECDH_PrivateKey & private_key)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 
public_value() const33       std::vector<uint8_t> public_value() const override
34          {
35          return m_key.public_value();
36          }
37 
algo_name() const38       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 final : public PK_Ops::Key_Agreement_with_KDF
56    {
57    public:
ECIES_ECDH_KA_Operation(const ECIES_PrivateKey & private_key,RandomNumberGenerator & rng)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 
agreed_value_size() const65       size_t agreed_value_size() const override { return m_key.domain().get_p_bytes(); }
66 
raw_agree(const uint8_t w[],size_t w_len)67       secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
68          {
69          const EC_Group& group = m_key.domain();
70 
71          PointGFp input_point = group.OS2ECP(w, w_len);
72          input_point.randomize_repr(m_rng);
73 
74          const PointGFp S = group.blinded_var_point_multiply(
75             input_point, m_key.private_value(), m_rng, m_ws);
76 
77          if(S.on_the_curve() == false)
78             throw Internal_Error("ECDH agreed value was not on the curve");
79          return BigInt::encode_1363(S.get_affine_x(), group.get_p_bytes());
80          }
81 
82    private:
83       ECIES_PrivateKey m_key;
84       RandomNumberGenerator& m_rng;
85       std::vector<BigInt> m_ws;
86    };
87 
88 std::unique_ptr<PK_Ops::Key_Agreement>
create_key_agreement_op(RandomNumberGenerator & rng,const std::string &,const std::string &) const89 ECIES_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
90                                           const std::string& /*params*/,
91                                           const std::string& /*provider*/) const
92    {
93    return std::unique_ptr<PK_Ops::Key_Agreement>(new ECIES_ECDH_KA_Operation(*this, rng));
94    }
95 
96 /**
97 * Creates a PK_Key_Agreement instance for the given key and ecies_params
98 * Returns either ECIES_ECDH_KA_Operation or the default implementation for the given key,
99 * depending on the key and ecies_params
100 * @param private_key the private key used for the key agreement
101 * @param ecies_params settings for ecies
102 * @param for_encryption disable cofactor mode if the secret will be used for encryption
103 * (according to ISO 18033 cofactor mode is only used during decryption)
104 */
create_key_agreement(const PK_Key_Agreement_Key & private_key,const ECIES_KA_Params & ecies_params,bool for_encryption,RandomNumberGenerator & rng)105 PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key,
106                                       const ECIES_KA_Params& ecies_params,
107                                       bool for_encryption,
108                                       RandomNumberGenerator& rng)
109    {
110    const ECDH_PrivateKey* ecdh_key = dynamic_cast<const ECDH_PrivateKey*>(&private_key);
111 
112    if(ecdh_key == nullptr && (ecies_params.cofactor_mode() || ecies_params.old_cofactor_mode()
113                               || ecies_params.check_mode()))
114       {
115       // assume we have a private key from an external provider (e.g. pkcs#11):
116       // there is no way to determine or control whether the provider uses cofactor mode or not.
117       // ISO 18033 does not allow cofactor mode in combination with old cofactor mode or check mode
118       // => disable cofactor mode, old cofactor mode and check mode for unknown keys/providers (as a precaution).
119       throw Invalid_Argument("ECIES: cofactor, old cofactor and check mode are only supported for ECDH_PrivateKey");
120       }
121 
122    if(ecdh_key && (for_encryption || !ecies_params.cofactor_mode()))
123       {
124       // ECDH_KA_Operation uses cofactor mode: use own key agreement method if cofactor should not be used.
125       return PK_Key_Agreement(ECIES_PrivateKey(*ecdh_key), rng, "Raw");
126       }
127 
128    return PK_Key_Agreement(private_key, rng, "Raw");        // use default implementation
129    }
130 }
131 
ECIES_KA_Operation(const PK_Key_Agreement_Key & private_key,const ECIES_KA_Params & ecies_params,bool for_encryption,RandomNumberGenerator & rng)132 ECIES_KA_Operation::ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key,
133                                        const ECIES_KA_Params& ecies_params,
134                                        bool for_encryption,
135                                        RandomNumberGenerator& rng) :
136    m_ka(create_key_agreement(private_key, ecies_params, for_encryption, rng)),
137    m_params(ecies_params)
138    {
139    }
140 
141 /**
142 * ECIES secret derivation according to ISO 18033-2
143 */
derive_secret(const std::vector<uint8_t> & eph_public_key_bin,const PointGFp & other_public_key_point) const144 SymmetricKey ECIES_KA_Operation::derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
145       const PointGFp& other_public_key_point) const
146    {
147    if(other_public_key_point.is_zero())
148       {
149       throw Invalid_Argument("ECIES: other public key point is zero");
150       }
151 
152    std::unique_ptr<KDF> kdf = Botan::KDF::create_or_throw(m_params.kdf_spec());
153 
154    PointGFp other_point = other_public_key_point;
155 
156    // ISO 18033: step b
157    if(m_params.old_cofactor_mode())
158       {
159       other_point *= m_params.domain().get_cofactor();
160       }
161 
162    secure_vector<uint8_t> derivation_input;
163 
164    // ISO 18033: encryption step e / decryption step g
165    if(!m_params.single_hash_mode())
166       {
167       derivation_input += eph_public_key_bin;
168       }
169 
170    // ISO 18033: encryption step f / decryption step h
171    std::vector<uint8_t> other_public_key_bin = other_point.encode(m_params.compression_type());
172     // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because
173    // "Raw" is passed to the `PK_Key_Agreement` if the implementation of botan is used.
174    const SymmetricKey peh = m_ka.derive_key(m_params.domain().get_order().bytes(), other_public_key_bin.data(), other_public_key_bin.size());
175    derivation_input.insert(derivation_input.end(), peh.begin(), peh.end());
176 
177    // ISO 18033: encryption step g / decryption step i
178    return kdf->derive_key(m_params.secret_length(), derivation_input);
179    }
180 
181 
ECIES_KA_Params(const EC_Group & domain,const std::string & kdf_spec,size_t length,PointGFp::Compression_Type compression_type,ECIES_Flags flags)182 ECIES_KA_Params::ECIES_KA_Params(const EC_Group& domain, const std::string& kdf_spec, size_t length,
183                                  PointGFp::Compression_Type compression_type, ECIES_Flags flags) :
184    m_domain(domain),
185    m_kdf_spec(kdf_spec),
186    m_length(length),
187    m_compression_mode(compression_type),
188    m_flags(flags)
189    {
190    }
191 
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,PointGFp::Compression_Type compression_type,ECIES_Flags flags)192 ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,
193                                          const std::string& dem_algo_spec, size_t dem_key_len,
194                                          const std::string& mac_spec, size_t mac_key_len,
195                                          PointGFp::Compression_Type compression_type, ECIES_Flags flags) :
196    ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags),
197    m_dem_spec(dem_algo_spec),
198    m_dem_keylen(dem_key_len),
199    m_mac_spec(mac_spec),
200    m_mac_keylen(mac_key_len)
201    {
202    // ISO 18033: "At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1."
203    if(size_t(cofactor_mode()) + size_t(old_cofactor_mode()) + size_t(check_mode()) > 1)
204       {
205       throw Invalid_Argument("ECIES: only one of cofactor_mode, old_cofactor_mode and check_mode can be set");
206       }
207    }
208 
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)209 ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,
210                                          const std::string& dem_algo_spec, size_t dem_key_len,
211                                          const std::string& mac_spec, size_t mac_key_len) :
212    ECIES_System_Params(domain, kdf_spec, dem_algo_spec, dem_key_len, mac_spec, mac_key_len, PointGFp::UNCOMPRESSED,
213                          ECIES_Flags::NONE)
214    {
215    }
216 
create_mac() const217 std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const
218    {
219    return Botan::MessageAuthenticationCode::create_or_throw(m_mac_spec);
220    }
221 
create_cipher(Botan::Cipher_Dir direction) const222 std::unique_ptr<Cipher_Mode> ECIES_System_Params::create_cipher(Botan::Cipher_Dir direction) const
223    {
224    return Cipher_Mode::create_or_throw(m_dem_spec, direction);
225    }
226 
227 
228 /*
229 * ECIES_Encryptor Constructor
230 */
ECIES_Encryptor(const PK_Key_Agreement_Key & private_key,const ECIES_System_Params & ecies_params,RandomNumberGenerator & rng)231 ECIES_Encryptor::ECIES_Encryptor(const PK_Key_Agreement_Key& private_key,
232                                  const ECIES_System_Params& ecies_params,
233                                  RandomNumberGenerator& rng) :
234    m_ka(private_key, ecies_params, true, rng),
235    m_params(ecies_params),
236    m_eph_public_key_bin(private_key.public_value()),    // returns the uncompressed public key, see conversion below
237    m_iv(),
238    m_other_point(),
239    m_label()
240    {
241    if(ecies_params.compression_type() != PointGFp::UNCOMPRESSED)
242       {
243       // ISO 18033: step d
244       // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format
245       m_eph_public_key_bin = m_params.domain().OS2ECP(m_eph_public_key_bin).encode(ecies_params.compression_type());
246       }
247    m_mac = m_params.create_mac();
248    m_cipher = m_params.create_cipher(ENCRYPTION);
249    }
250 
251 /*
252 * ECIES_Encryptor Constructor
253 */
ECIES_Encryptor(RandomNumberGenerator & rng,const ECIES_System_Params & ecies_params)254 ECIES_Encryptor::ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_Params& ecies_params) :
255    ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.domain()), ecies_params, rng)
256    {
257    }
258 
maximum_input_size() const259 size_t ECIES_Encryptor::maximum_input_size() const
260    {
261    /*
262    ECIES should just be used for key transport so this (arbitrary) limit
263    seems sufficient
264    */
265    return 64;
266    }
267 
ciphertext_length(size_t ptext_len) const268 size_t ECIES_Encryptor::ciphertext_length(size_t ptext_len) const
269    {
270    return m_eph_public_key_bin.size() +
271           m_mac->output_length() +
272           m_cipher->output_length(ptext_len);
273    }
274 
275 /*
276 * ECIES Encryption according to ISO 18033-2
277 */
enc(const uint8_t data[],size_t length,RandomNumberGenerator &) const278 std::vector<uint8_t> ECIES_Encryptor::enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const
279    {
280    if(m_other_point.is_zero())
281       {
282       throw Invalid_State("ECIES: the other key is zero");
283       }
284 
285    const SymmetricKey secret_key = m_ka.derive_secret(m_eph_public_key_bin, m_other_point);
286 
287    // encryption
288 
289    m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
290    if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
291       throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
292 
293    m_cipher->start(m_iv.bits_of());
294 
295    secure_vector<uint8_t> encrypted_data(data, data + length);
296    m_cipher->finish(encrypted_data);
297 
298    // concat elements
299 
300    std::vector<uint8_t> out(m_eph_public_key_bin.size() + encrypted_data.size() + m_mac->output_length());
301    buffer_insert(out, 0, m_eph_public_key_bin);
302    buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data);
303 
304    // mac
305    m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
306    m_mac->update(encrypted_data);
307    if(!m_label.empty())
308       {
309       m_mac->update(m_label);
310       }
311    m_mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size());
312 
313    return out;
314    }
315 
316 
ECIES_Decryptor(const PK_Key_Agreement_Key & key,const ECIES_System_Params & ecies_params,RandomNumberGenerator & rng)317 ECIES_Decryptor::ECIES_Decryptor(const PK_Key_Agreement_Key& key,
318                                  const ECIES_System_Params& ecies_params,
319                                  RandomNumberGenerator& rng) :
320    m_ka(key, ecies_params, false, rng),
321    m_params(ecies_params),
322    m_iv(),
323    m_label()
324    {
325    // ISO 18033: "If v > 1 and CheckMode = 0, then we must have gcd(u, v) = 1." (v = index, u= order)
326    if(!ecies_params.check_mode())
327       {
328       const Botan::BigInt& cofactor = m_params.domain().get_cofactor();
329       if(cofactor > 1 && Botan::gcd(cofactor, m_params.domain().get_order()) != 1)
330          {
331          throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0");
332          }
333       }
334 
335    m_mac = m_params.create_mac();
336    m_cipher = m_params.create_cipher(DECRYPTION);
337    }
338 
plaintext_length(size_t ctext_len) const339 size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const
340    {
341    const size_t point_size = m_params.domain().point_size(m_params.compression_type());
342    const size_t overhead = point_size + m_mac->output_length();
343 
344    if(ctext_len < overhead)
345       return 0;
346 
347    return m_cipher->output_length(ctext_len - overhead);
348    }
349 
350 /**
351 * ECIES Decryption according to ISO 18033-2
352 */
do_decrypt(uint8_t & valid_mask,const uint8_t in[],size_t in_len) const353 secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const
354    {
355    const size_t point_size = m_params.domain().point_size(m_params.compression_type());
356 
357    if(in_len < point_size + m_mac->output_length())
358       {
359       throw Decoding_Error("ECIES decryption: ciphertext is too short");
360       }
361 
362    // extract data
363    const std::vector<uint8_t> other_public_key_bin(in, in + point_size);    // the received (ephemeral) public key
364    const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length());
365    const std::vector<uint8_t> mac_data(in + in_len - m_mac->output_length(), in + in_len);
366 
367    // ISO 18033: step a
368    PointGFp other_public_key = m_params.domain().OS2ECP(other_public_key_bin);
369 
370    // ISO 18033: step b
371    if(m_params.check_mode() && !other_public_key.on_the_curve())
372       {
373       throw Decoding_Error("ECIES decryption: received public key is not on the curve");
374       }
375 
376    // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree)
377    // throws Illegal_Transformation if the point is zero)
378    const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);
379 
380    // validate mac
381    m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
382    m_mac->update(encrypted_data);
383    if(!m_label.empty())
384       {
385       m_mac->update(m_label);
386       }
387    const secure_vector<uint8_t> calculated_mac = m_mac->final();
388    valid_mask = ct_compare_u8(mac_data.data(), calculated_mac.data(), mac_data.size());
389 
390    if(valid_mask)
391       {
392       // decrypt data
393 
394       m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
395       if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
396          throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
397       m_cipher->start(m_iv.bits_of());
398 
399       try
400          {
401          // the decryption can fail:
402          // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
403          secure_vector<uint8_t> decrypted_data(encrypted_data.begin(), encrypted_data.end());
404          m_cipher->finish(decrypted_data);
405          return decrypted_data;
406          }
407       catch(...)
408          {
409          valid_mask = 0;
410          }
411       }
412    return secure_vector<uint8_t>();
413    }
414 
415 }
416