From 87e4cd8c71511837b38406802f6e92e32b1890e9 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Fri, 10 Aug 2018 12:44:55 -0400 Subject: Add functions to get size of PK ciphertext,plaintext Needed for https://github.com/strongswan/strongswan/pull/109 --- src/lib/pubkey/dlies/dlies.cpp | 27 +++++++----- src/lib/pubkey/dlies/dlies.h | 4 ++ src/lib/pubkey/ec_group/ec_group.cpp | 9 ++++ src/lib/pubkey/ec_group/ec_group.h | 2 + src/lib/pubkey/ecies/ecies.cpp | 85 +++++++++++++++++++++--------------- src/lib/pubkey/ecies/ecies.h | 16 ++++--- src/lib/pubkey/elgamal/elgamal.cpp | 4 ++ src/lib/pubkey/pk_ops.h | 4 ++ src/lib/pubkey/pubkey.cpp | 10 +++++ src/lib/pubkey/pubkey.h | 19 ++++++++ src/lib/pubkey/rsa/rsa.cpp | 4 ++ src/lib/pubkey/sm2/sm2_enc.cpp | 34 ++++++++++++++- 12 files changed, 165 insertions(+), 53 deletions(-) (limited to 'src/lib/pubkey') diff --git a/src/lib/pubkey/dlies/dlies.cpp b/src/lib/pubkey/dlies/dlies.cpp index a4603f0d7..5465401d1 100644 --- a/src/lib/pubkey/dlies/dlies.cpp +++ b/src/lib/pubkey/dlies/dlies.cpp @@ -97,20 +97,17 @@ std::vector DLIES_Encryptor::enc(const uint8_t in[], size_t length, /** * Return the max size, in bytes, of a message -* Not_Implemented if DLIES is used in XOR encryption mode +* We assume DLIES is only used for key transport and limit the maximum size +* to 512 bits */ size_t DLIES_Encryptor::maximum_input_size() const { - if(m_cipher) - { - // no limit in block cipher mode - return std::numeric_limits::max(); - } - else - { - // No way to determine if the KDF will output enough bits for XORing with the plaintext?! - throw Not_Implemented("Not implemented for XOR encryption mode"); - } + return 64; + } + +size_t DLIES_Encryptor::ciphertext_length(size_t ptext_len) const + { + return m_own_pub_key.size() + m_mac->output_length() + m_cipher->output_length(ptext_len); } DLIES_Decryptor::DLIES_Decryptor(const DH_PrivateKey& own_priv_key, @@ -141,6 +138,14 @@ DLIES_Decryptor::DLIES_Decryptor(const DH_PrivateKey& own_priv_key, DLIES_Decryptor(own_priv_key, rng, kdf, nullptr, 0, mac, mac_key_length) {} +size_t DLIES_Decryptor::plaintext_length(size_t ctext_len) const + { + if(ctext_len < m_pub_key_size + m_mac->output_length()) + return 0; // will throw if attempted + + return ctext_len - (m_pub_key_size + m_mac->output_length()); + } + secure_vector DLIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t msg[], size_t length) const { diff --git a/src/lib/pubkey/dlies/dlies.h b/src/lib/pubkey/dlies/dlies.h index 8fa673491..640a8655e 100644 --- a/src/lib/pubkey/dlies/dlies.h +++ b/src/lib/pubkey/dlies/dlies.h @@ -79,6 +79,8 @@ class BOTAN_PUBLIC_API(2,0) DLIES_Encryptor final : public PK_Encryptor size_t maximum_input_size() const override; + size_t ciphertext_length(size_t ptext_len) const override; + std::vector m_other_pub_key; std::vector m_own_pub_key; PK_Key_Agreement m_ka; @@ -144,6 +146,8 @@ class BOTAN_PUBLIC_API(2,0) DLIES_Decryptor final : public PK_Decryptor secure_vector do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const override; + size_t plaintext_length(size_t ctext_len) const override; + const size_t m_pub_key_size; PK_Key_Agreement m_ka; std::unique_ptr m_kdf; diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp index 30a0bb141..f4419c7f0 100644 --- a/src/lib/pubkey/ec_group/ec_group.cpp +++ b/src/lib/pubkey/ec_group/ec_group.cpp @@ -526,6 +526,15 @@ const OID& EC_Group::get_curve_oid() const return data().oid(); } +size_t EC_Group::point_size(PointGFp::Compression_Type format) const + { + // Hybrid and standard format are (x,y), compressed is y, +1 format byte + if(format == PointGFp::COMPRESSED) + return (1 + get_p_bytes()); + else + return (1 + 2*get_p_bytes()); + } + PointGFp EC_Group::OS2ECP(const uint8_t bits[], size_t len) const { return Botan::OS2ECP(bits, len, data().curve()); diff --git a/src/lib/pubkey/ec_group/ec_group.h b/src/lib/pubkey/ec_group/ec_group.h index f8c1c1a12..8a22cebce 100644 --- a/src/lib/pubkey/ec_group/ec_group.h +++ b/src/lib/pubkey/ec_group/ec_group.h @@ -302,6 +302,8 @@ class BOTAN_PUBLIC_API(2,0) EC_Group final */ PointGFp zero_point() const; + size_t point_size(PointGFp::Compression_Type format) const; + PointGFp OS2ECP(const uint8_t bits[], size_t len) const; template diff --git a/src/lib/pubkey/ecies/ecies.cpp b/src/lib/pubkey/ecies/ecies.cpp index 793cca225..8f5c445ed 100644 --- a/src/lib/pubkey/ecies/ecies.cpp +++ b/src/lib/pubkey/ecies/ecies.cpp @@ -244,6 +244,8 @@ ECIES_Encryptor::ECIES_Encryptor(const PK_Key_Agreement_Key& private_key, // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format m_eph_public_key_bin = m_params.domain().OS2ECP(m_eph_public_key_bin).encode(ecies_params.compression_type()); } + m_mac = m_params.create_mac(); + m_cipher = m_params.create_cipher(ENCRYPTION); } /* @@ -254,6 +256,21 @@ ECIES_Encryptor::ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_ { } +size_t ECIES_Encryptor::maximum_input_size() const + { + /* + ECIES should just be used for key transport so this (arbitrary) limit + seems sufficient + */ + return 64; + } + +size_t ECIES_Encryptor::ciphertext_length(size_t ptext_len) const + { + return m_eph_public_key_bin.size() + + m_mac->output_length() + + m_cipher->output_length(ptext_len); + } /* * ECIES Encryption according to ISO 18033-2 @@ -268,35 +285,31 @@ std::vector ECIES_Encryptor::enc(const uint8_t data[], size_t length, R const SymmetricKey secret_key = m_ka.derive_secret(m_eph_public_key_bin, m_other_point); // encryption - std::unique_ptr cipher = m_params.create_cipher(ENCRYPTION); - BOTAN_ASSERT(cipher != nullptr, "Cipher is found"); - cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen())); + m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen())); if(m_iv.size() != 0) { - cipher->start(m_iv.bits_of()); + m_cipher->start(m_iv.bits_of()); } secure_vector encrypted_data(data, data + length); - cipher->finish(encrypted_data); + m_cipher->finish(encrypted_data); // concat elements - std::unique_ptr mac = m_params.create_mac(); - BOTAN_ASSERT(mac != nullptr, "MAC is found"); - secure_vector out(m_eph_public_key_bin.size() + encrypted_data.size() + mac->output_length()); + std::vector out(m_eph_public_key_bin.size() + encrypted_data.size() + m_mac->output_length()); buffer_insert(out, 0, m_eph_public_key_bin); buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data); // mac - mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen()); - mac->update(encrypted_data); + m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen()); + m_mac->update(encrypted_data); if(!m_label.empty()) { - mac->update(m_label); + m_mac->update(m_label); } - mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size()); + m_mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size()); - return unlock(out); + return out; } @@ -317,6 +330,20 @@ ECIES_Decryptor::ECIES_Decryptor(const PK_Key_Agreement_Key& key, throw Invalid_Argument("ECIES: gcd of cofactor and order must be 1 if check_mode is 0"); } } + + m_mac = m_params.create_mac(); + m_cipher = m_params.create_cipher(DECRYPTION); + } + +size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const + { + const size_t point_size = m_params.domain().point_size(m_params.compression_type()); + const size_t overhead = point_size + m_mac->output_length(); + + if(ctext_len < overhead) + return 0; + + return m_cipher->output_length(ctext_len - overhead); } /** @@ -324,25 +351,17 @@ ECIES_Decryptor::ECIES_Decryptor(const PK_Key_Agreement_Key& key, */ secure_vector ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const { - size_t point_size = m_params.domain().get_p_bytes(); - if(m_params.compression_type() != PointGFp::COMPRESSED) - { - point_size *= 2; // uncompressed and hybrid contains x AND y - } - point_size += 1; // format byte - - std::unique_ptr mac = m_params.create_mac(); - BOTAN_ASSERT(mac != nullptr, "MAC is found"); + const size_t point_size = m_params.domain().point_size(m_params.compression_type()); - if(in_len < point_size + mac->output_length()) + if(in_len < point_size + m_mac->output_length()) { throw Decoding_Error("ECIES decryption: ciphertext is too short"); } // extract data const std::vector other_public_key_bin(in, in + point_size); // the received (ephemeral) public key - const std::vector encrypted_data(in + point_size, in + in_len - mac->output_length()); - const std::vector mac_data(in + in_len - mac->output_length(), in + in_len); + const std::vector encrypted_data(in + point_size, in + in_len - m_mac->output_length()); + const std::vector mac_data(in + in_len - m_mac->output_length(), in + in_len); // ISO 18033: step a PointGFp other_public_key = m_params.domain().OS2ECP(other_public_key_bin); @@ -358,25 +377,23 @@ secure_vector ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const ui const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key); // validate mac - mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen()); - mac->update(encrypted_data); + m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen()); + m_mac->update(encrypted_data); if(!m_label.empty()) { - mac->update(m_label); + m_mac->update(m_label); } - const secure_vector calculated_mac = mac->final(); + const secure_vector calculated_mac = m_mac->final(); valid_mask = CT::expand_mask(constant_time_compare(mac_data.data(), calculated_mac.data(), mac_data.size())); if(valid_mask) { // decrypt data - std::unique_ptr cipher = m_params.create_cipher(DECRYPTION); - BOTAN_ASSERT(cipher != nullptr, "Cipher is found"); - cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen())); + m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen())); if(m_iv.size() != 0) { - cipher->start(m_iv.bits_of()); + m_cipher->start(m_iv.bits_of()); } try @@ -384,7 +401,7 @@ secure_vector ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const ui // the decryption can fail: // e.g. Integrity_Failure is thrown if GCM is used and the message does not have a valid tag secure_vector decrypted_data(encrypted_data.begin(), encrypted_data.end()); - cipher->finish(decrypted_data); + m_cipher->finish(decrypted_data); return decrypted_data; } catch(...) diff --git a/src/lib/pubkey/ecies/ecies.h b/src/lib/pubkey/ecies/ecies.h index f7d055dea..e36f94568 100644 --- a/src/lib/pubkey/ecies/ecies.h +++ b/src/lib/pubkey/ecies/ecies.h @@ -15,14 +15,13 @@ #include #include #include +#include #include #include #include -#include namespace Botan { -class MessageAuthenticationCode; class RandomNumberGenerator; enum class ECIES_Flags : uint32_t @@ -254,13 +253,14 @@ class BOTAN_PUBLIC_API(2,0) ECIES_Encryptor final : public PK_Encryptor private: std::vector enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const override; - inline size_t maximum_input_size() const override - { - return std::numeric_limits::max(); - } + size_t maximum_input_size() const override; + + size_t ciphertext_length(size_t ptext_len) const override; const ECIES_KA_Operation m_ka; const ECIES_System_Params m_params; + std::unique_ptr m_mac; + std::unique_ptr m_cipher; std::vector m_eph_public_key_bin; InitializationVector m_iv; PointGFp m_other_point; @@ -298,8 +298,12 @@ class BOTAN_PUBLIC_API(2,0) ECIES_Decryptor final : public PK_Decryptor private: secure_vector do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const override; + size_t plaintext_length(size_t ctext_len) const override; + const ECIES_KA_Operation m_ka; const ECIES_System_Params m_params; + std::unique_ptr m_mac; + std::unique_ptr m_cipher; InitializationVector m_iv; std::vector m_label; }; diff --git a/src/lib/pubkey/elgamal/elgamal.cpp b/src/lib/pubkey/elgamal/elgamal.cpp index 1f62c2b1d..6c2d6bccc 100644 --- a/src/lib/pubkey/elgamal/elgamal.cpp +++ b/src/lib/pubkey/elgamal/elgamal.cpp @@ -75,6 +75,8 @@ class ElGamal_Encryption_Operation final : public PK_Ops::Encryption_with_EME { public: + size_t ciphertext_length(size_t) const override { return 2*m_group.p_bytes(); } + size_t max_raw_input_bits() const override { return m_group.p_bits() - 1; } ElGamal_Encryption_Operation(const ElGamal_PublicKey& key, const std::string& eme); @@ -124,6 +126,8 @@ class ElGamal_Decryption_Operation final : public PK_Ops::Decryption_with_EME const std::string& eme, RandomNumberGenerator& rng); + size_t plaintext_length(size_t) const override { return m_group.p_bytes(); } + secure_vector raw_decrypt(const uint8_t msg[], size_t msg_len) override; private: const DL_Group m_group; diff --git a/src/lib/pubkey/pk_ops.h b/src/lib/pubkey/pk_ops.h index 3e38cc8ca..3f794f00d 100644 --- a/src/lib/pubkey/pk_ops.h +++ b/src/lib/pubkey/pk_ops.h @@ -42,6 +42,8 @@ class BOTAN_PUBLIC_API(2,0) Encryption virtual size_t max_input_bits() const = 0; + virtual size_t ciphertext_length(size_t ptext_len) const = 0; + virtual ~Encryption() = default; }; @@ -55,6 +57,8 @@ class BOTAN_PUBLIC_API(2,0) Decryption const uint8_t ciphertext[], size_t ciphertext_len) = 0; + virtual size_t plaintext_length(size_t ctext_len) const = 0; + virtual ~Decryption() = default; }; diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index 6296adbb2..fef81a620 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -96,6 +96,11 @@ PK_Encryptor_EME::PK_Encryptor_EME(const Public_Key& key, PK_Encryptor_EME::~PK_Encryptor_EME() { /* for unique_ptr */ } +size_t PK_Encryptor_EME::ciphertext_length(size_t ptext_len) const + { + return m_op->ciphertext_length(ptext_len); + } + std::vector PK_Encryptor_EME::enc(const uint8_t in[], size_t length, RandomNumberGenerator& rng) const { @@ -119,6 +124,11 @@ PK_Decryptor_EME::PK_Decryptor_EME(const Private_Key& key, PK_Decryptor_EME::~PK_Decryptor_EME() { /* for unique_ptr */ } +size_t PK_Decryptor_EME::plaintext_length(size_t ctext_len) const + { + return m_op->plaintext_length(ctext_len); + } + secure_vector PK_Decryptor_EME::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const { diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index 8328861fd..a1404135d 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -66,6 +66,11 @@ class BOTAN_PUBLIC_API(2,0) PK_Encryptor */ virtual size_t maximum_input_size() const = 0; + /** + * Return an upper bound on the ciphertext length + */ + virtual size_t ciphertext_length(size_t ctext_len) const = 0; + PK_Encryptor() = default; virtual ~PK_Encryptor() = default; @@ -140,6 +145,12 @@ class BOTAN_PUBLIC_API(2,0) PK_Decryptor const uint8_t required_content_offsets[], size_t required_contents) const; + /** + * Return an upper bound on the plaintext length for a particular + * ciphertext input length + */ + virtual size_t plaintext_length(size_t ctext_len) const = 0; + PK_Decryptor() = default; virtual ~PK_Decryptor() = default; @@ -547,6 +558,12 @@ class BOTAN_PUBLIC_API(2,0) PK_Encryptor_EME final : public PK_Encryptor PK_Encryptor_EME& operator=(const PK_Encryptor_EME&) = delete; PK_Encryptor_EME(const PK_Encryptor_EME&) = delete; + + /** + * Return an upper bound on the ciphertext length for a particular + * plaintext input length + */ + size_t ciphertext_length(size_t ptext_len) const override; private: std::vector enc(const uint8_t[], size_t, RandomNumberGenerator& rng) const override; @@ -586,6 +603,8 @@ class BOTAN_PUBLIC_API(2,0) PK_Decryptor_EME final : public PK_Decryptor PK_Decryptor_EME(key, system_rng(), eme, provider) {} #endif + size_t plaintext_length(size_t ptext_len) const override; + ~PK_Decryptor_EME(); PK_Decryptor_EME& operator=(const PK_Decryptor_EME&) = delete; PK_Decryptor_EME(const PK_Decryptor_EME&) = delete; diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp index eefbea869..ec3c35ef1 100644 --- a/src/lib/pubkey/rsa/rsa.cpp +++ b/src/lib/pubkey/rsa/rsa.cpp @@ -328,6 +328,8 @@ class RSA_Decryption_Operation final : public PK_Ops::Decryption_with_EME, { } + size_t plaintext_length(size_t) const override { return m_mod_bytes; } + secure_vector raw_decrypt(const uint8_t msg[], size_t msg_len) override { const BigInt m(msg, msg_len); @@ -405,6 +407,8 @@ class RSA_Encryption_Operation final : public PK_Ops::Encryption_with_EME, { } + size_t ciphertext_length(size_t) const override { return m_n.bytes(); } + size_t max_raw_input_bits() const override { return get_max_input_bits(); } secure_vector raw_encrypt(const uint8_t msg[], size_t msg_len, diff --git a/src/lib/pubkey/sm2/sm2_enc.cpp b/src/lib/pubkey/sm2/sm2_enc.cpp index 3da4275b3..856cb5cbc 100644 --- a/src/lib/pubkey/sm2/sm2_enc.cpp +++ b/src/lib/pubkey/sm2/sm2_enc.cpp @@ -27,7 +27,10 @@ class SM2_Encryption_Operation final : public PK_Ops::Encryption m_kdf_hash(kdf_hash), m_ws(PointGFp::WORKSPACE_SIZE), m_mul_public_point(key.public_point(), rng, m_ws) - {} + { + std::unique_ptr hash = HashFunction::create_or_throw(m_kdf_hash); + m_hash_size = hash->output_length(); + } size_t max_input_bits() const override { @@ -35,6 +38,14 @@ class SM2_Encryption_Operation final : public PK_Ops::Encryption return 512; } + size_t ciphertext_length(size_t ptext_len) const override + { + const size_t elem_size = m_group.get_order_bytes(); + const size_t der_overhead = 12; + + return der_overhead + 2*elem_size + m_hash_size + ptext_len; + } + secure_vector encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override @@ -95,6 +106,7 @@ class SM2_Encryption_Operation final : public PK_Ops::Encryption std::vector m_ws; PointGFp_Var_Point_Precompute m_mul_public_point; + size_t m_hash_size; }; class SM2_Decryption_Operation final : public PK_Ops::Decryption @@ -106,7 +118,24 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption m_key(key), m_rng(rng), m_kdf_hash(kdf_hash) - {} + { + std::unique_ptr hash = HashFunction::create_or_throw(m_kdf_hash); + m_hash_size = hash->output_length(); + } + + size_t plaintext_length(size_t ptext_len) const override + { + /* + * This ignores the DER encoding and so overestimates the + * plaintext length by 12 bytes or so + */ + const size_t elem_size = m_key.domain().get_order_bytes(); + + if(ptext_len < 2*elem_size + m_hash_size) + return 0; + + return ptext_len - (2*elem_size + m_hash_size); + } secure_vector decrypt(uint8_t& valid_mask, const uint8_t ciphertext[], @@ -202,6 +231,7 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption RandomNumberGenerator& m_rng; const std::string m_kdf_hash; std::vector m_ws; + size_t m_hash_size; }; } -- cgit v1.2.3