diff options
author | Jack Lloyd <[email protected]> | 2018-08-10 12:44:55 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2018-08-10 12:44:55 -0400 |
commit | 87e4cd8c71511837b38406802f6e92e32b1890e9 (patch) | |
tree | 000aed5da6cfabd3009937a88cafb959f3988ea1 /src | |
parent | 256cfa3be098fb45d5c2de047f88786e88051e73 (diff) |
Add functions to get size of PK ciphertext,plaintext
Needed for https://github.com/strongswan/strongswan/pull/109
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/ffi/ffi.h | 16 | ||||
-rw-r--r-- | src/lib/ffi/ffi_pk_op.cpp | 10 | ||||
-rw-r--r-- | src/lib/prov/openssl/openssl_ec.cpp | 2 | ||||
-rw-r--r-- | src/lib/prov/openssl/openssl_rsa.cpp | 4 | ||||
-rw-r--r-- | src/lib/prov/pkcs11/p11_rsa.cpp | 4 | ||||
-rw-r--r-- | src/lib/pubkey/dlies/dlies.cpp | 27 | ||||
-rw-r--r-- | src/lib/pubkey/dlies/dlies.h | 4 | ||||
-rw-r--r-- | src/lib/pubkey/ec_group/ec_group.cpp | 9 | ||||
-rw-r--r-- | src/lib/pubkey/ec_group/ec_group.h | 2 | ||||
-rw-r--r-- | src/lib/pubkey/ecies/ecies.cpp | 85 | ||||
-rw-r--r-- | src/lib/pubkey/ecies/ecies.h | 16 | ||||
-rw-r--r-- | src/lib/pubkey/elgamal/elgamal.cpp | 4 | ||||
-rw-r--r-- | src/lib/pubkey/pk_ops.h | 4 | ||||
-rw-r--r-- | src/lib/pubkey/pubkey.cpp | 10 | ||||
-rw-r--r-- | src/lib/pubkey/pubkey.h | 19 | ||||
-rw-r--r-- | src/lib/pubkey/rsa/rsa.cpp | 4 | ||||
-rw-r--r-- | src/lib/pubkey/sm2/sm2_enc.cpp | 34 | ||||
-rw-r--r-- | src/tests/test_ecies.cpp | 15 | ||||
-rw-r--r-- | src/tests/test_ffi.cpp | 38 | ||||
-rw-r--r-- | src/tests/test_pubkey.cpp | 8 |
20 files changed, 236 insertions, 79 deletions
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 210c41eeb..19a65f946 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1090,10 +1090,16 @@ BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op, BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op); +BOTAN_PUBLIC_API(2,8) int botan_pk_op_encrypt_output_length(botan_pk_op_encrypt_t op, + size_t ptext_len, + size_t* ctext_len); + BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt(botan_pk_op_encrypt_t op, - botan_rng_t rng, - uint8_t out[], size_t* out_len, - const uint8_t plaintext[], size_t plaintext_len); + botan_rng_t rng, + uint8_t out[], + size_t* out_len, + const uint8_t plaintext[], + size_t plaintext_len); /* * Public Key Decryption @@ -1106,6 +1112,10 @@ BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op, uint32_t flags); BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op); +BOTAN_PUBLIC_API(2,8) int botan_pk_op_decrypt_output_length(botan_pk_op_decrypt_t op, + size_t ctext_len, + size_t* ptext_len); + BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt(botan_pk_op_decrypt_t op, uint8_t out[], size_t* out_len, const uint8_t ciphertext[], size_t ciphertext_len); diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp index c714f7158..0f18fca3e 100644 --- a/src/lib/ffi/ffi_pk_op.cpp +++ b/src/lib/ffi/ffi_pk_op.cpp @@ -44,6 +44,11 @@ int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op) return BOTAN_FFI_CHECKED_DELETE(op); } +int botan_pk_op_encrypt_output_length(botan_pk_op_encrypt_t op, size_t ptext_len, size_t* ctext_len) + { + return BOTAN_FFI_DO(Botan::PK_Encryptor, op, o, { *ctext_len = o.ciphertext_length(ptext_len); }); + } + int botan_pk_op_encrypt(botan_pk_op_encrypt_t op, botan_rng_t rng_obj, uint8_t out[], size_t* out_len, @@ -81,6 +86,11 @@ int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op) return BOTAN_FFI_CHECKED_DELETE(op); } +int botan_pk_op_decrypt_output_length(botan_pk_op_decrypt_t op, size_t ctext_len, size_t* ptext_len) + { + return BOTAN_FFI_DO(Botan::PK_Decryptor, op, o, { *ptext_len = o.plaintext_length(ctext_len); }); + } + int botan_pk_op_decrypt(botan_pk_op_decrypt_t op, uint8_t out[], size_t* out_len, const uint8_t ciphertext[], size_t ciphertext_len) diff --git a/src/lib/prov/openssl/openssl_ec.cpp b/src/lib/prov/openssl/openssl_ec.cpp index 33f277d5a..5da466c0c 100644 --- a/src/lib/prov/openssl/openssl_ec.cpp +++ b/src/lib/prov/openssl/openssl_ec.cpp @@ -232,7 +232,7 @@ class OpenSSL_ECDSA_Signing_Operation final : public PK_Ops::Signature_with_EMSA m_order_bytes = (m_order_bits + 7) / 8; } - size_t signature_length() const { return 2*m_order_bytes; } + size_t signature_length() const override { return 2*m_order_bytes; } secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator&) override diff --git a/src/lib/prov/openssl/openssl_rsa.cpp b/src/lib/prov/openssl/openssl_rsa.cpp index cf9c2b52b..8108526a1 100644 --- a/src/lib/prov/openssl/openssl_rsa.cpp +++ b/src/lib/prov/openssl/openssl_rsa.cpp @@ -57,6 +57,8 @@ class OpenSSL_RSA_Encryption_Operation final : public PK_Ops::Encryption m_bits = 8 * (n_size() - pad_overhead) - 1; } + size_t ciphertext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); } + size_t max_input_bits() const override { return m_bits; }; secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len, @@ -110,6 +112,8 @@ class OpenSSL_RSA_Decryption_Operation final : public PK_Ops::Decryption throw OpenSSL_Error("d2i_RSAPrivateKey"); } + size_t plaintext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); } + secure_vector<uint8_t> decrypt(uint8_t& valid_mask, const uint8_t msg[], size_t msg_len) override { diff --git a/src/lib/prov/pkcs11/p11_rsa.cpp b/src/lib/prov/pkcs11/p11_rsa.cpp index 65bcd2d22..225e49bb2 100644 --- a/src/lib/prov/pkcs11/p11_rsa.cpp +++ b/src/lib/prov/pkcs11/p11_rsa.cpp @@ -131,6 +131,8 @@ class PKCS11_RSA_Decryption_Operation final : public PK_Ops::Decryption m_bits = m_key.get_n().bits() - 1; } + size_t plaintext_length(size_t) const override { return m_key.get_n().bytes(); } + secure_vector<uint8_t> decrypt(uint8_t& valid_mask, const uint8_t ciphertext[], size_t ciphertext_len) override { valid_mask = 0; @@ -177,6 +179,8 @@ class PKCS11_RSA_Encryption_Operation final : public PK_Ops::Encryption m_bits = 8 * (key.get_n().bytes() - m_mechanism.padding_size()) - 1; } + size_t ciphertext_length(size_t) const override { return m_key.get_n().bytes(); } + size_t max_input_bits() const override { return m_bits; 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<uint8_t> 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<size_t>::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<uint8_t> 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<uint8_t> m_other_pub_key; std::vector<uint8_t> 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<uint8_t> 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<KDF> 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<typename Alloc> 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<uint8_t> 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_Mode> 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<uint8_t> encrypted_data(data, data + length); - cipher->finish(encrypted_data); + m_cipher->finish(encrypted_data); // concat elements - std::unique_ptr<MessageAuthenticationCode> mac = m_params.create_mac(); - BOTAN_ASSERT(mac != nullptr, "MAC is found"); - secure_vector<uint8_t> out(m_eph_public_key_bin.size() + encrypted_data.size() + mac->output_length()); + std::vector<uint8_t> 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<uint8_t> 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<MessageAuthenticationCode> 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<uint8_t> other_public_key_bin(in, in + point_size); // the received (ephemeral) public key - const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - mac->output_length()); - const std::vector<uint8_t> mac_data(in + in_len - mac->output_length(), in + in_len); + const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length()); + const std::vector<uint8_t> 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<uint8_t> 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<uint8_t> calculated_mac = mac->final(); + const secure_vector<uint8_t> calculated_mac = m_mac->final(); valid_mask = CT::expand_mask<uint8_t>(constant_time_compare(mac_data.data(), calculated_mac.data(), mac_data.size())); if(valid_mask) { // decrypt data - std::unique_ptr<Cipher_Mode> 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<uint8_t> 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<uint8_t> 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 <botan/pubkey.h> #include <botan/secmem.h> #include <botan/symkey.h> +#include <botan/mac.h> #include <memory> #include <string> #include <vector> -#include <limits> 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<uint8_t> enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const override; - inline size_t maximum_input_size() const override - { - return std::numeric_limits<size_t>::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<MessageAuthenticationCode> m_mac; + std::unique_ptr<Cipher_Mode> m_cipher; std::vector<uint8_t> 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<uint8_t> 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<MessageAuthenticationCode> m_mac; + std::unique_ptr<Cipher_Mode> m_cipher; InitializationVector m_iv; std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<HashFunction> 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<uint8_t> 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<BigInt> 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<HashFunction> 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<uint8_t> 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<BigInt> m_ws; + size_t m_hash_size; }; } diff --git a/src/tests/test_ecies.cpp b/src/tests/test_ecies.cpp index 63619a8a4..8e167f5bb 100644 --- a/src/tests/test_ecies.cpp +++ b/src/tests/test_ecies.cpp @@ -279,10 +279,9 @@ Test::Result test_kdf_not_found() "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED, flags); - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); - - result.test_throws("kdf not found", [ &ecies_enc ]() + result.test_throws("kdf not found", [&]() { + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng()); }); @@ -304,10 +303,9 @@ Test::Result test_mac_not_found() "XYZMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED, flags); - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); - - result.test_throws("mac not found", [ &ecies_enc ]() + result.test_throws("mac not found", [&]() { + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng()); }); @@ -329,10 +327,9 @@ Test::Result test_cipher_not_found() "HMAC(SHA-512)", 20, Botan::PointGFp::Compression_Type::COMPRESSED, flags); - Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); - - result.test_throws("cipher not found", [ &ecies_enc ]() + result.test_throws("cipher not found", [&]() { + Botan::ECIES_Encryptor ecies_enc(private_key, ecies_params, Test::rng()); ecies_enc.encrypt(std::vector<uint8_t>(8), Test::rng()); }); diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 010b1b4eb..76f8635f1 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -1474,8 +1474,9 @@ class FFI_Unit_Tests final : public Test std::vector<uint8_t> plaintext(32); TEST_FFI_OK(botan_rng_get, (rng, plaintext.data(), plaintext.size())); - std::vector<uint8_t> ciphertext(256); // TODO: no way to know this size from API - size_t ctext_len = ciphertext.size(); + size_t ctext_len; + TEST_FFI_OK(botan_pk_op_encrypt_output_length, (encrypt, plaintext.size(), &ctext_len)); + std::vector<uint8_t> ciphertext(ctext_len); if(TEST_FFI_OK(botan_pk_op_encrypt, (encrypt, rng, ciphertext.data(), &ctext_len, @@ -1486,8 +1487,9 @@ class FFI_Unit_Tests final : public Test botan_pk_op_decrypt_t decrypt; if(TEST_FFI_OK(botan_pk_op_decrypt_create, (&decrypt, priv, "OAEP(SHA-256)", 0))) { - std::vector<uint8_t> decrypted(256); // TODO as with above - size_t decrypted_len = decrypted.size(); + size_t decrypted_len; + TEST_FFI_OK(botan_pk_op_decrypt_output_length, (decrypt, ciphertext.size(), &decrypted_len)); + std::vector<uint8_t> decrypted(decrypted_len); TEST_FFI_OK(botan_pk_op_decrypt, (decrypt, decrypted.data(), &decrypted_len, ciphertext.data(), ciphertext.size())); decrypted.resize(decrypted_len); @@ -1854,13 +1856,16 @@ class FFI_Unit_Tests final : public Test std::vector<uint8_t> message(32); - std::vector<uint8_t> ciphertext(4096); + std::vector<uint8_t> ciphertext; TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size())); botan_pk_op_encrypt_t enc; if(TEST_FFI_OK(botan_pk_op_encrypt_create, (&enc, loaded_pubkey, "", 0))) { - size_t ctext_len = ciphertext.size(); + size_t ctext_len; + TEST_FFI_OK(botan_pk_op_encrypt_output_length, (enc, message.size(), &ctext_len)); + + ciphertext.resize(ctext_len); TEST_FFI_OK(botan_pk_op_encrypt, (enc, rng, ciphertext.data(), &ctext_len, message.data(), message.size())); ciphertext.resize(ctext_len); @@ -2140,29 +2145,36 @@ class FFI_Unit_Tests final : public Test botan_mp_destroy(y); botan_mp_destroy(x); - std::vector<uint8_t> plaintext(16, 0xFF); - std::vector<uint8_t> ciphertext(p_len*2, 0); - std::vector<uint8_t> decryption(16, 0); + std::vector<uint8_t> ciphertext; + std::vector<uint8_t> decryption; // Test encryption botan_pk_op_encrypt_t op_enc; - size_t ct_len = ciphertext.size(); if (TEST_FFI_OK(botan_pk_op_encrypt_create, (&op_enc, loaded_pubkey, "Raw", 0))) { - TEST_FFI_OK(botan_pk_op_encrypt, (op_enc, rng, ciphertext.data(), &ct_len, plaintext.data(), plaintext.size())); + size_t ctext_len; + TEST_FFI_OK(botan_pk_op_encrypt_output_length, (op_enc, plaintext.size(), &ctext_len)); + ciphertext.resize(ctext_len); + TEST_FFI_OK(botan_pk_op_encrypt, (op_enc, rng, ciphertext.data(), &ctext_len, plaintext.data(), plaintext.size())); + ciphertext.resize(ctext_len); TEST_FFI_OK(botan_pk_op_encrypt_destroy, (op_enc)); } // Test decryption botan_pk_op_decrypt_t op_dec; - size_t pt_len = decryption.size(); if (TEST_FFI_OK(botan_pk_op_decrypt_create, (&op_dec, loaded_privkey, "Raw", 0))) { - TEST_FFI_OK(botan_pk_op_decrypt, (op_dec, decryption.data(), &pt_len, ciphertext.data(), ct_len)); + size_t ptext_len; + TEST_FFI_OK(botan_pk_op_decrypt_output_length, (op_dec, ciphertext.size(), &ptext_len)); + decryption.resize(ptext_len); + TEST_FFI_OK(botan_pk_op_decrypt, (op_dec, decryption.data(), &ptext_len, ciphertext.data(), ciphertext.size())); + decryption.resize(ptext_len); TEST_FFI_OK(botan_pk_op_decrypt_destroy, (op_dec)); } + result.test_eq("decryption worked", decryption, plaintext); + TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey)); TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey)); diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index 6b40e8d3c..bc2d19bb7 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -310,6 +310,10 @@ PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_hdr, const Va try { decrypted = decryptor->decrypt(ciphertext); + + result.test_lte("Plaintext within length", + decrypted.size(), + decryptor->plaintext_length(ciphertext.size())); } catch(Botan::Exception& e) { @@ -358,6 +362,10 @@ PK_Encryption_Decryption_Test::run_one_test(const std::string& pad_hdr, const Va const std::vector<uint8_t> generated_ciphertext = encryptor->encrypt(plaintext, kat_rng ? *kat_rng : Test::rng()); + result.test_lte("Ciphertext within length", + generated_ciphertext.size(), + encryptor->ciphertext_length(plaintext.size())); + if(enc_provider == "base") { result.test_eq(enc_provider, "generated ciphertext matches KAT", |