diff options
-rw-r--r-- | include/amalgamation-amd64/botan_all.cpp | 2935 | ||||
-rw-r--r-- | include/amalgamation-amd64/botan_all.h | 773 | ||||
-rw-r--r-- | include/amalgamation-arm64/botan_all.cpp | 2935 | ||||
-rw-r--r-- | include/amalgamation-arm64/botan_all.h | 773 | ||||
-rw-r--r-- | include/amalgamation-armhf/botan_all.cpp | 2935 | ||||
-rw-r--r-- | include/amalgamation-armhf/botan_all.h | 773 | ||||
-rw-r--r-- | scripts/build-botan.sh | 4 |
7 files changed, 11117 insertions, 11 deletions
diff --git a/include/amalgamation-amd64/botan_all.cpp b/include/amalgamation-amd64/botan_all.cpp index 090da12..be17b58 100644 --- a/include/amalgamation-amd64/botan_all.cpp +++ b/include/amalgamation-amd64/botan_all.cpp @@ -400,6 +400,297 @@ class BOTAN_TEST_API calendar_point namespace Botan { /** +* Block Cipher Mode Padding Method +* This class is pretty limited, it cannot deal well with +* randomized padding methods, or any padding method that +* wants to add more than one block. For instance, it should +* be possible to define cipher text stealing mode as simply +* a padding mode for CBC, which happens to consume the last +* two block (and requires use of the block cipher). +*/ +class BOTAN_TEST_API BlockCipherModePaddingMethod + { + public: + /** + * Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7") + * @param algo_spec block cipher padding mode name + */ + static std::unique_ptr<BlockCipherModePaddingMethod> create(const std::string& algo_spec); + + /** + * Add padding bytes to buffer. + * @param buffer data to pad + * @param final_block_bytes size of the final block in bytes + * @param block_size size of each block in bytes + */ + virtual void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const = 0; + + /** + * Remove padding bytes from block + * @param block the last block + * @param len the size of the block in bytes + * @return number of data bytes, or if the padding is invalid returns len + */ + virtual size_t unpad(const uint8_t block[], size_t len) const = 0; + + /** + * @param block_size of the cipher + * @return valid block size for this padding mode + */ + virtual bool valid_blocksize(size_t block_size) const = 0; + + /** + * @return name of the mode + */ + virtual std::string name() const = 0; + + /** + * virtual destructor + */ + virtual ~BlockCipherModePaddingMethod() = default; + }; + +/** +* PKCS#7 Padding +*/ +class BOTAN_TEST_API PKCS7_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "PKCS7"; } + }; + +/** +* ANSI X9.23 Padding +*/ +class BOTAN_TEST_API ANSI_X923_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "X9.23"; } + }; + +/** +* One And Zeros Padding (ISO/IEC 9797-1, padding method 2) +*/ +class BOTAN_TEST_API OneAndZeros_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2); } + + std::string name() const override { return "OneAndZeros"; } + }; + +/** +* ESP Padding (RFC 4304) +*/ +class BOTAN_TEST_API ESP_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "ESP"; } + }; + +/** +* Null Padding +*/ +class Null_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>&, size_t, size_t) const override + { + /* no padding */ + } + + size_t unpad(const uint8_t[], size_t size) const override { return size; } + + bool valid_blocksize(size_t) const override { return true; } + + std::string name() const override { return "NoPadding"; } + }; + +} + +namespace Botan { + +/** +* CBC Mode +*/ +class CBC_Mode : public Cipher_Mode + { + public: + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + size_t default_nonce_length() const override; + + bool valid_nonce_length(size_t n) const override; + + void clear() override; + + void reset() override; + + protected: + CBC_Mode(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding); + + const BlockCipher& cipher() const { return *m_cipher; } + + const BlockCipherModePaddingMethod& padding() const + { + BOTAN_ASSERT_NONNULL(m_padding); + return *m_padding; + } + + size_t block_size() const { return m_block_size; } + + secure_vector<uint8_t>& state() { return m_state; } + + uint8_t* state_ptr() { return m_state.data(); } + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + std::unique_ptr<BlockCipher> m_cipher; + std::unique_ptr<BlockCipherModePaddingMethod> m_padding; + secure_vector<uint8_t> m_state; + size_t m_block_size; + }; + +/** +* CBC Encryption +*/ +class CBC_Encryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Encryption(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + CBC_Mode(std::move(cipher), std::move(padding)) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + }; + +/** +* CBC Encryption with ciphertext stealing (CBC-CS3 variant) +*/ +class CTS_Encryption final : public CBC_Encryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Encryption(std::unique_ptr<BlockCipher> cipher) : + CBC_Encryption(std::move(cipher), nullptr) + {} + + size_t output_length(size_t input_length) const override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +/** +* CBC Decryption +*/ +class CBC_Decryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Decryption(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + CBC_Mode(std::move(cipher), std::move(padding)), + m_tempbuf(update_granularity()) + {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + + void reset() override; + + private: + secure_vector<uint8_t> m_tempbuf; + }; + +/** +* CBC Decryption with ciphertext stealing (CBC-CS3 variant) +*/ +class CTS_Decryption final : public CBC_Decryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Decryption(std::unique_ptr<BlockCipher> cipher) : + CBC_Decryption(std::move(cipher), nullptr) + {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +} + +namespace Botan { + +/** * DJB's ChaCha (https://cr.yp.to/chacha.html) */ class ChaCha final : public StreamCipher @@ -2088,6 +2379,26 @@ class EME namespace Botan { +/** +* EME from PKCS #1 v1.5 +*/ +class BOTAN_TEST_API EME_PKCS1v15 final : public EME + { + public: + size_t maximum_input_size(size_t) const override; + + secure_vector<uint8_t> pad(const uint8_t[], size_t, size_t, + RandomNumberGenerator&) const override; + + secure_vector<uint8_t> unpad(uint8_t& valid_mask, + const uint8_t in[], + size_t in_len) const override; + }; + +} + +namespace Botan { + class EME_Raw final : public EME { public: @@ -2261,6 +2572,126 @@ class EMSA1 final : public EMSA namespace Botan { /** +* PKCS #1 v1.5 signature padding +* aka PKCS #1 block type 1 +* aka EMSA3 from IEEE 1363 +*/ +class EMSA_PKCS1v15 final : public EMSA + { + public: + /** + * @param hash the hash function to use + */ + explicit EMSA_PKCS1v15(std::unique_ptr<HashFunction> hash); + + std::unique_ptr<EMSA> new_object() override + { + return std::make_unique<EMSA_PKCS1v15>(m_hash->new_object()); + } + + void update(const uint8_t[], size_t) override; + + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator& rng) override; + + bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&, + size_t) override; + + std::string name() const override + { return "EMSA3(" + m_hash->name() + ")"; } + + AlgorithmIdentifier config_for_x509(const Private_Key& key, + const std::string& cert_hash_name) const override; + + bool requires_message_recovery() const override { return true; } + private: + std::unique_ptr<HashFunction> m_hash; + std::vector<uint8_t> m_hash_id; + }; + +/** +* EMSA_PKCS1v15_Raw which is EMSA_PKCS1v15 without a hash or digest id +* (which according to QCA docs is "identical to PKCS#11's CKM_RSA_PKCS +* mechanism", something I have not confirmed) +*/ +class EMSA_PKCS1v15_Raw final : public EMSA + { + public: + std::unique_ptr<EMSA> new_object() override { return std::make_unique<EMSA_PKCS1v15_Raw>(); } + + void update(const uint8_t[], size_t) override; + + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator& rng) override; + + bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&, + size_t) override; + + EMSA_PKCS1v15_Raw(); + + /** + * @param hash_algo t he digest id for that hash is included in + * the signature. + */ + EMSA_PKCS1v15_Raw(const std::string& hash_algo); + + std::string name() const override + { + if(m_hash_name.empty()) return "EMSA3(Raw)"; + else return "EMSA3(Raw," + m_hash_name + ")"; + } + + bool requires_message_recovery() const override { return true; } + private: + size_t m_hash_output_len = 0; + std::string m_hash_name; + std::vector<uint8_t> m_hash_id; + secure_vector<uint8_t> m_message; + }; + +} + +namespace Botan { + +/** +* EMSA-Raw - sign inputs directly +* Don't use this unless you know what you are doing. +*/ +class EMSA_Raw final : public EMSA + { + public: + std::unique_ptr<EMSA> new_object() override { return std::make_unique<EMSA_Raw>(); } + + explicit EMSA_Raw(size_t expected_hash_size = 0) : + m_expected_size(expected_hash_size) {} + + std::string name() const override; + + bool requires_message_recovery() const override { return false; } + private: + void update(const uint8_t[], size_t) override; + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator&) override; + + bool verify(const secure_vector<uint8_t>&, + const secure_vector<uint8_t>&, + size_t) override; + + const size_t m_expected_size; + secure_vector<uint8_t> m_message; + }; + +} + +namespace Botan { + +/** * No_Filesystem_Access Exception */ class No_Filesystem_Access final : public Exception @@ -2278,6 +2709,62 @@ BOTAN_TEST_API std::vector<std::string> get_files_recursive(const std::string& d namespace Botan { +/** +* Return the PKCS #1 hash identifier +* @see RFC 3447 section 9.2 +* @param hash_name the name of the hash function +* @return uint8_t sequence identifying the hash +* @throw Invalid_Argument if the hash has no known PKCS #1 hash id +*/ +std::vector<uint8_t> BOTAN_TEST_API pkcs_hash_id(const std::string& hash_name); + +/** +* Return the IEEE 1363 hash identifier +* @param hash_name the name of the hash function +* @return uint8_t code identifying the hash, or 0 if not known +*/ +uint8_t ieee1363_hash_id(const std::string& hash_name); + +} + +namespace Botan { + +/** +* HMAC +*/ +class HMAC final : public MessageAuthenticationCode + { + public: + void clear() override; + std::string name() const override; + std::unique_ptr<MessageAuthenticationCode> new_object() const override; + + size_t output_length() const override; + + Key_Length_Specification key_spec() const override; + + /** + * @param hash the hash to use for HMACing + */ + explicit HMAC(std::unique_ptr<HashFunction> hash); + + HMAC(const HMAC&) = delete; + HMAC& operator=(const HMAC&) = delete; + private: + void add_data(const uint8_t[], size_t) override; + void final_result(uint8_t[]) override; + void key_schedule(const uint8_t[], size_t) override; + + std::unique_ptr<HashFunction> m_hash; + secure_vector<uint8_t> m_ikey, m_okey; + size_t m_hash_output_length; + size_t m_hash_block_size; + }; + +} + +namespace Botan { + namespace KeyPair { /** @@ -5254,6 +5741,77 @@ bool host_wildcard_match(const std::string& wildcard, } + +namespace Botan { + +class RandomNumberGenerator; + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds to run PBKDF2 +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds to run PBKDF2 +* @param out_iterations_if_nonnull if not null, set to the number +* of PBKDF iterations used +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param iterations how many iterations to run PBKDF2 +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t iterations, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Decrypt a PKCS #5 v2.0 encrypted stream +* @param key_bits the input +* @param passphrase the passphrase to use for decryption +* @param params the PBES2 parameters +*/ +secure_vector<uint8_t> +pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params); + +} /** * Ordinary applications should never need to include or use this * header. It is exposed only for specialized applications which want @@ -12676,6 +13234,604 @@ void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& } /* +* Block Ciphers +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_AES) +#endif + +#if defined(BOTAN_HAS_ARIA) +#endif + +#if defined(BOTAN_HAS_BLOWFISH) +#endif + +#if defined(BOTAN_HAS_CAMELLIA) +#endif + +#if defined(BOTAN_HAS_CAST_128) +#endif + +#if defined(BOTAN_HAS_CASCADE) +#endif + +#if defined(BOTAN_HAS_DES) +#endif + +#if defined(BOTAN_HAS_GOST_28147_89) +#endif + +#if defined(BOTAN_HAS_IDEA) +#endif + +#if defined(BOTAN_HAS_LION) +#endif + +#if defined(BOTAN_HAS_NOEKEON) +#endif + +#if defined(BOTAN_HAS_SEED) +#endif + +#if defined(BOTAN_HAS_SERPENT) +#endif + +#if defined(BOTAN_HAS_SHACAL2) +#endif + +#if defined(BOTAN_HAS_SM4) +#endif + +#if defined(BOTAN_HAS_TWOFISH) +#endif + +#if defined(BOTAN_HAS_THREEFISH_512) +#endif + +#if defined(BOTAN_HAS_COMMONCRYPTO) +#endif + +namespace Botan { + +std::unique_ptr<BlockCipher> +BlockCipher::create(const std::string& algo, + const std::string& provider) + { +#if defined(BOTAN_HAS_COMMONCRYPTO) + if(provider.empty() || provider == "commoncrypto") + { + if(auto bc = make_commoncrypto_block_cipher(algo)) + return bc; + + if(!provider.empty()) + return nullptr; + } +#endif + + // TODO: CryptoAPI + // TODO: /dev/crypto + + // Only base providers from here on out + if(provider.empty() == false && provider != "base") + return nullptr; + +#if defined(BOTAN_HAS_AES) + if(algo == "AES-128") + { + return std::make_unique<AES_128>(); + } + + if(algo == "AES-192") + { + return std::make_unique<AES_192>(); + } + + if(algo == "AES-256") + { + return std::make_unique<AES_256>(); + } +#endif + +#if defined(BOTAN_HAS_ARIA) + if(algo == "ARIA-128") + { + return std::make_unique<ARIA_128>(); + } + + if(algo == "ARIA-192") + { + return std::make_unique<ARIA_192>(); + } + + if(algo == "ARIA-256") + { + return std::make_unique<ARIA_256>(); + } +#endif + +#if defined(BOTAN_HAS_SERPENT) + if(algo == "Serpent") + { + return std::make_unique<Serpent>(); + } +#endif + +#if defined(BOTAN_HAS_SHACAL2) + if(algo == "SHACAL2") + { + return std::make_unique<SHACAL2>(); + } +#endif + +#if defined(BOTAN_HAS_TWOFISH) + if(algo == "Twofish") + { + return std::make_unique<Twofish>(); + } +#endif + +#if defined(BOTAN_HAS_THREEFISH_512) + if(algo == "Threefish-512") + { + return std::make_unique<Threefish_512>(); + } +#endif + +#if defined(BOTAN_HAS_BLOWFISH) + if(algo == "Blowfish") + { + return std::make_unique<Blowfish>(); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA) + if(algo == "Camellia-128") + { + return std::make_unique<Camellia_128>(); + } + + if(algo == "Camellia-192") + { + return std::make_unique<Camellia_192>(); + } + + if(algo == "Camellia-256") + { + return std::make_unique<Camellia_256>(); + } +#endif + +#if defined(BOTAN_HAS_DES) + if(algo == "DES") + { + return std::make_unique<DES>(); + } + + if(algo == "TripleDES" || algo == "3DES" || algo == "DES-EDE") + { + return std::make_unique<TripleDES>(); + } +#endif + +#if defined(BOTAN_HAS_NOEKEON) + if(algo == "Noekeon") + { + return std::make_unique<Noekeon>(); + } +#endif + +#if defined(BOTAN_HAS_CAST_128) + if(algo == "CAST-128" || algo == "CAST5") + { + return std::make_unique<CAST_128>(); + } +#endif + +#if defined(BOTAN_HAS_IDEA) + if(algo == "IDEA") + { + return std::make_unique<IDEA>(); + } +#endif + +#if defined(BOTAN_HAS_SEED) + if(algo == "SEED") + { + return std::make_unique<SEED>(); + } +#endif + +#if defined(BOTAN_HAS_SM4) + if(algo == "SM4") + { + return std::make_unique<SM4>(); + } +#endif + + const SCAN_Name req(algo); + +#if defined(BOTAN_HAS_GOST_28147_89) + if(req.algo_name() == "GOST-28147-89") + { + return std::make_unique<GOST_28147_89>(req.arg(0, "R3411_94_TestParam")); + } +#endif + +#if defined(BOTAN_HAS_CASCADE) + if(req.algo_name() == "Cascade" && req.arg_count() == 2) + { + std::unique_ptr<BlockCipher> c1 = BlockCipher::create(req.arg(0)); + std::unique_ptr<BlockCipher> c2 = BlockCipher::create(req.arg(1)); + + if(c1 && c2) + return std::make_unique<Cascade_Cipher>(std::move(c1), std::move(c2)); + } +#endif + +#if defined(BOTAN_HAS_LION) + if(req.algo_name() == "Lion" && req.arg_count_between(2, 3)) + { + std::unique_ptr<HashFunction> hash = HashFunction::create(req.arg(0)); + std::unique_ptr<StreamCipher> stream = StreamCipher::create(req.arg(1)); + + if(hash && stream) + { + const size_t block_size = req.arg_as_integer(2, 1024); + return std::make_unique<Lion>(std::move(hash), std::move(stream), block_size); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<BlockCipher> +BlockCipher::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto bc = BlockCipher::create(algo, provider)) + { + return bc; + } + throw Lookup_Error("Block cipher", algo, provider); + } + +std::vector<std::string> BlockCipher::providers(const std::string& algo) + { + return probe_providers_of<BlockCipher>(algo, { "base", "commoncrypto" }); + } + +} +/* +* CBC Mode +* (C) 1999-2007,2013,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +CBC_Mode::CBC_Mode(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + m_cipher(std::move(cipher)), + m_padding(std::move(padding)), + m_block_size(m_cipher->block_size()) + { + if(m_padding && !m_padding->valid_blocksize(m_block_size)) + throw Invalid_Argument("Padding " + m_padding->name() + + " cannot be used with " + + m_cipher->name() + "/CBC"); + } + +void CBC_Mode::clear() + { + m_cipher->clear(); + reset(); + } + +void CBC_Mode::reset() + { + m_state.clear(); + } + +std::string CBC_Mode::name() const + { + if(m_padding) + return cipher().name() + "/CBC/" + padding().name(); + else + return cipher().name() + "/CBC/CTS"; + } + +size_t CBC_Mode::update_granularity() const + { + return cipher().parallel_bytes(); + } + +Key_Length_Specification CBC_Mode::key_spec() const + { + return cipher().key_spec(); + } + +size_t CBC_Mode::default_nonce_length() const + { + return block_size(); + } + +bool CBC_Mode::valid_nonce_length(size_t n) const + { + return (n == 0 || n == block_size()); + } + +void CBC_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_cipher->set_key(key, length); + m_state.clear(); + } + +void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + /* + * A nonce of zero length means carry the last ciphertext value over + * as the new IV, as unfortunately some protocols require this. If + * this is the first message then we use an IV of all zeros. + */ + if(nonce_len) + m_state.assign(nonce, nonce + nonce_len); + else if(m_state.empty()) + m_state.resize(m_cipher->block_size()); + // else leave the state alone + } + +size_t CBC_Encryption::minimum_final_size() const + { + return 0; + } + +size_t CBC_Encryption::output_length(size_t input_length) const + { + if(input_length == 0) + return block_size(); + else + return round_up(input_length, block_size()); + } + +size_t CBC_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "CBC input is full blocks"); + const size_t blocks = sz / BS; + + if(blocks > 0) + { + xor_buf(&buf[0], state_ptr(), BS); + cipher().encrypt(&buf[0]); + + for(size_t i = 1; i != blocks; ++i) + { + xor_buf(&buf[BS*i], &buf[BS*(i-1)], BS); + cipher().encrypt(&buf[BS*i]); + } + + state().assign(&buf[BS*(blocks-1)], &buf[BS*blocks]); + } + + return sz; + } + +void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + + const size_t BS = block_size(); + + const size_t bytes_in_final_block = (buffer.size()-offset) % BS; + + padding().add_padding(buffer, bytes_in_final_block, BS); + + BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary"); + + update(buffer, offset); + } + +bool CTS_Encryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Encryption::minimum_final_size() const + { + return block_size() + 1; + } + +size_t CTS_Encryption::output_length(size_t input_length) const + { + return input_length; // no ciphertext expansion in CTS + } + +void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + uint8_t* buf = buffer.data() + offset; + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to encrypt"); + + if(sz % BS == 0) + { + update(buffer, offset); + + // swap last two blocks + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + xor_buf(last.data(), state_ptr(), BS); + cipher().encrypt(last.data()); + + for(size_t i = 0; i != final_bytes - BS; ++i) + { + last[i] ^= last[i + BS]; + last[i + BS] ^= last[i]; + } + + cipher().encrypt(last.data()); + + buffer += last; + } + } + +size_t CBC_Decryption::output_length(size_t input_length) const + { + return input_length; // precise for CTS, worst case otherwise + } + +size_t CBC_Decryption::minimum_final_size() const + { + return block_size(); + } + +size_t CBC_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "Input is full blocks"); + size_t blocks = sz / BS; + + while(blocks) + { + const size_t to_proc = std::min(BS * blocks, m_tempbuf.size()); + + cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS); + + xor_buf(m_tempbuf.data(), state_ptr(), BS); + xor_buf(&m_tempbuf[BS], buf, to_proc - BS); + copy_mem(state_ptr(), buf + (to_proc - BS), BS); + + copy_mem(buf, m_tempbuf.data(), to_proc); + + buf += to_proc; + blocks -= to_proc / BS; + } + + return sz; + } + +void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz == 0 || sz % BS) + throw Decoding_Error(name() + ": Ciphertext not a multiple of block size"); + + update(buffer, offset); + + const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.size()-BS], BS); + buffer.resize(buffer.size() - pad_bytes); // remove padding + if(pad_bytes == 0 && padding().name() != "NoPadding") + { + throw Decoding_Error("Invalid CBC padding"); + } + } + +void CBC_Decryption::reset() + { + CBC_Mode::reset(); + zeroise(m_tempbuf); + } + +bool CTS_Decryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Decryption::minimum_final_size() const + { + return block_size() + 1; + } + +void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to decrypt"); + + if(sz % BS == 0) + { + // swap last two blocks + + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + + update(buffer, offset); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + cipher().decrypt(last.data()); + + xor_buf(last.data(), &last[BS], final_bytes - BS); + + for(size_t i = 0; i != final_bytes - BS; ++i) + std::swap(last[i], last[i + BS]); + + cipher().decrypt(last.data()); + xor_buf(last.data(), state_ptr(), BS); + + buffer += last; + } + } + +} +/* * ChaCha * (C) 2014,2018 Jack Lloyd * @@ -14541,6 +15697,111 @@ OAEP::OAEP(std::unique_ptr<HashFunction> hash, } /* +* PKCS #1 v1.5 Type 2 (encryption) padding +* (C) 1999-2007,2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/* +* PKCS1 Pad Operation +*/ +secure_vector<uint8_t> EME_PKCS1v15::pad(const uint8_t in[], size_t inlen, + size_t key_length, + RandomNumberGenerator& rng) const + { + key_length /= 8; + + if(inlen > maximum_input_size(key_length * 8)) + { + throw Invalid_Argument("PKCS1: Input is too large"); + } + + secure_vector<uint8_t> out(key_length); + + out[0] = 0x02; + rng.randomize(out.data() + 1, (key_length - inlen - 2)); + + for(size_t j = 1; j != key_length - inlen - 1; ++j) + { + if(out[j] == 0) + { + out[j] = rng.next_nonzero_byte(); + } + } + + buffer_insert(out, key_length - inlen, in, inlen); + + return out; + } + +/* +* PKCS1 Unpad Operation +*/ +secure_vector<uint8_t> EME_PKCS1v15::unpad(uint8_t& valid_mask, + const uint8_t in[], size_t inlen) const + { + /* + * RSA decryption pads the ciphertext up to the modulus size, so this only + * occurs with very (!) small keys, or when fuzzing. + * + * 11 bytes == 00,02 + 8 bytes mandatory padding + 00 + */ + if(inlen < 11) + { + valid_mask = false; + return secure_vector<uint8_t>(); + } + + CT::poison(in, inlen); + + CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared(); + CT::Mask<uint8_t> seen_zero_m = CT::Mask<uint8_t>::cleared(); + size_t delim_idx = 2; // initial 0002 + + bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[0], 0); + bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[1], 2); + + for(size_t i = 2; i < inlen; ++i) + { + const auto is_zero_m = CT::Mask<uint8_t>::is_zero(in[i]); + delim_idx += seen_zero_m.if_not_set_return(1); + seen_zero_m |= is_zero_m; + } + + // no zero delim -> bad padding + bad_input_m |= ~seen_zero_m; + /* + delim indicates < 8 bytes padding -> bad padding + + We require 11 here because we are counting also the 00 delim byte + */ + bad_input_m |= CT::Mask<uint8_t>(CT::Mask<size_t>::is_lt(delim_idx, 11)); + + valid_mask = (~bad_input_m).unpoisoned_value(); + auto output = CT::copy_output(bad_input_m, in, inlen, delim_idx); + + CT::unpoison(in, inlen); + + return output; + } + +/* +* Return the max input size for a given key size +*/ +size_t EME_PKCS1v15::maximum_input_size(size_t keybits) const + { + if(keybits / 8 > 10) + return ((keybits / 8) - 10); + else + return 0; + } + +} +/* * (C) 2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) @@ -14700,6 +15961,167 @@ AlgorithmIdentifier EMSA1::config_for_x509(const Private_Key& key, } /* +* PKCS #1 v1.5 signature padding +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg, + size_t output_bits, + const uint8_t hash_id[], + size_t hash_id_length) + { + size_t output_length = output_bits / 8; + if(output_length < hash_id_length + msg.size() + 10) + throw Encoding_Error("emsa3_encoding: Output length is too small"); + + secure_vector<uint8_t> T(output_length); + const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2; + + T[0] = 0x01; + set_mem(&T[1], P_LENGTH, 0xFF); + T[P_LENGTH+1] = 0x00; + + if(hash_id_length > 0) + { + BOTAN_ASSERT_NONNULL(hash_id); + buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length); + } + + buffer_insert(T, output_length-msg.size(), msg.data(), msg.size()); + return T; + } + +} + +void EMSA_PKCS1v15::update(const uint8_t input[], size_t length) + { + m_hash->update(input, length); + } + +secure_vector<uint8_t> EMSA_PKCS1v15::raw_data() + { + return m_hash->final(); + } + +secure_vector<uint8_t> +EMSA_PKCS1v15::encoding_of(const secure_vector<uint8_t>& msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) + { + if(msg.size() != m_hash->output_length()) + throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length"); + + return emsa3_encoding(msg, output_bits, + m_hash_id.data(), m_hash_id.size()); + } + +bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t key_bits) + { + if(raw.size() != m_hash->output_length()) + return false; + + try + { + return (coded == emsa3_encoding(raw, key_bits, + m_hash_id.data(), m_hash_id.size())); + } + catch(...) + { + return false; + } + } + +AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key, + const std::string& cert_hash_name) const + { + if(cert_hash_name != m_hash->name()) + throw Invalid_Argument("Hash function from opts and hash_fn argument" + " need to be identical"); + // check that the signature algorithm and the padding scheme fit + if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3")) + { + throw Invalid_Argument("Encoding scheme with canonical name EMSA3" + " not supported for signature algorithm " + key.algo_name()); + } + + // for RSA PKCSv1.5 parameters "SHALL" be NULL + + const OID oid = OID::from_string(key.algo_name() + "/" + name()); + return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_NULL_PARAM); + } + +EMSA_PKCS1v15::EMSA_PKCS1v15(std::unique_ptr<HashFunction> hash) : + m_hash(std::move(hash)) + { + m_hash_id = pkcs_hash_id(m_hash->name()); + } + +EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw() + { + m_hash_output_len = 0; + // m_hash_id, m_hash_name left empty + } + +EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo) + { + std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo)); + m_hash_id = pkcs_hash_id(hash_algo); + m_hash_name = hash->name(); + m_hash_output_len = hash->output_length(); + } + +void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length) + { + m_message += std::make_pair(input, length); + } + +secure_vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data() + { + secure_vector<uint8_t> ret; + std::swap(ret, m_message); + + if(m_hash_output_len > 0 && ret.size() != m_hash_output_len) + throw Encoding_Error("EMSA_PKCS1v15_Raw::encoding_of: Bad input length"); + + return ret; + } + +secure_vector<uint8_t> +EMSA_PKCS1v15_Raw::encoding_of(const secure_vector<uint8_t>& msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) + { + return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size()); + } + +bool EMSA_PKCS1v15_Raw::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t key_bits) + { + if(m_hash_output_len > 0 && raw.size() != m_hash_output_len) + return false; + + try + { + return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size())); + } + catch(...) + { + return false; + } + } + +} +/* * PSSR * (C) 1999-2007,2017 Jack Lloyd * @@ -14983,6 +16405,96 @@ std::string PSSR_Raw::name() const } /* +* EMSA-Raw +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +std::string EMSA_Raw::name() const + { + if(m_expected_size > 0) + return "Raw(" + std::to_string(m_expected_size) + ")"; + return "Raw"; + } + +/* +* EMSA-Raw Encode Operation +*/ +void EMSA_Raw::update(const uint8_t input[], size_t length) + { + m_message += std::make_pair(input, length); + } + +/* +* Return the raw (unencoded) data +*/ +secure_vector<uint8_t> EMSA_Raw::raw_data() + { + if(m_expected_size && m_message.size() != m_expected_size) + throw Invalid_Argument("EMSA_Raw was configured to use a " + + std::to_string(m_expected_size) + + " byte hash but instead was used for a " + + std::to_string(m_message.size()) + " hash"); + + secure_vector<uint8_t> output; + std::swap(m_message, output); + return output; + } + +/* +* EMSA-Raw Encode Operation +*/ +secure_vector<uint8_t> +EMSA_Raw::encoding_of(const secure_vector<uint8_t>& msg, + size_t /*output_bits*/, + RandomNumberGenerator& /*rng*/) + { + if(m_expected_size && msg.size() != m_expected_size) + throw Invalid_Argument("EMSA_Raw was configured to use a " + + std::to_string(m_expected_size) + + " byte hash but instead was used for a " + + std::to_string(msg.size()) + " hash"); + + return msg; + } + +/* +* EMSA-Raw Verify Operation +*/ +bool EMSA_Raw::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t /*key_bits*/) + { + if(m_expected_size && raw.size() != m_expected_size) + return false; + + if(coded.size() == raw.size()) + return (coded == raw); + + if(coded.size() > raw.size()) + return false; + + // handle zero padding differences + const size_t leading_zeros_expected = raw.size() - coded.size(); + + bool same_modulo_leading_zeros = true; + + for(size_t i = 0; i != leading_zeros_expected; ++i) + if(raw[i]) + same_modulo_leading_zeros = false; + + if(!constant_time_compare(coded.data(), raw.data() + leading_zeros_expected, coded.size())) + same_modulo_leading_zeros = false; + + return same_modulo_leading_zeros; + } + +} +/* * Entropy Source Polling * (C) 2008-2010,2015 Jack Lloyd * @@ -15481,6 +16993,159 @@ std::vector<std::string> HashFunction::providers(const std::string& algo_spec) } /* +* Hash Function Identification +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +const uint8_t MD5_PKCS_ID[] = { +0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, +0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; + +const uint8_t RIPEMD_160_PKCS_ID[] = { +0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, +0x01, 0x05, 0x00, 0x04, 0x14 }; + +const uint8_t SHA_160_PKCS_ID[] = { +0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, +0x1A, 0x05, 0x00, 0x04, 0x14 }; + +const uint8_t SHA_224_PKCS_ID[] = { +0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C }; + +const uint8_t SHA_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA_384_PKCS_ID[] = { +0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; + +const uint8_t SHA_512_PKCS_ID[] = { +0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; + +const uint8_t SHA_512_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA3_224_PKCS_ID[] = { +0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x07, 0x05, 0x00, 0x04, 0x1C }; + +const uint8_t SHA3_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA3_384_PKCS_ID[] = { +0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x09, 0x05, 0x00, 0x04, 0x30 }; + +const uint8_t SHA3_512_PKCS_ID[] = { +0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x0A, 0x05, 0x00, 0x04, 0x40 }; + +const uint8_t SM3_PKCS_ID[] = { +0x30, 0x30, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x81, 0x1C, 0xCF, +0x55, 0x01, 0x83, 0x11, 0x05, 0x00, 0x04, 0x20, +}; + +} + +/* +* HashID as specified by PKCS +*/ +std::vector<uint8_t> pkcs_hash_id(const std::string& name) + { + // Special case for SSL/TLS RSA signatures + if(name == "Parallel(MD5,SHA-160)") + return std::vector<uint8_t>(); + + // If you add a value to this function, also update test_hash_id.cpp + + if(name == "MD5") + return std::vector<uint8_t>(MD5_PKCS_ID, + MD5_PKCS_ID + sizeof(MD5_PKCS_ID)); + + if(name == "RIPEMD-160") + return std::vector<uint8_t>(RIPEMD_160_PKCS_ID, + RIPEMD_160_PKCS_ID + sizeof(RIPEMD_160_PKCS_ID)); + + if(name == "SHA-160" || name == "SHA-1" || name == "SHA1") + return std::vector<uint8_t>(SHA_160_PKCS_ID, + SHA_160_PKCS_ID + sizeof(SHA_160_PKCS_ID)); + + if(name == "SHA-224") + return std::vector<uint8_t>(SHA_224_PKCS_ID, + SHA_224_PKCS_ID + sizeof(SHA_224_PKCS_ID)); + + if(name == "SHA-256") + return std::vector<uint8_t>(SHA_256_PKCS_ID, + SHA_256_PKCS_ID + sizeof(SHA_256_PKCS_ID)); + + if(name == "SHA-384") + return std::vector<uint8_t>(SHA_384_PKCS_ID, + SHA_384_PKCS_ID + sizeof(SHA_384_PKCS_ID)); + + if(name == "SHA-512") + return std::vector<uint8_t>(SHA_512_PKCS_ID, + SHA_512_PKCS_ID + sizeof(SHA_512_PKCS_ID)); + + if(name == "SHA-512-256") + return std::vector<uint8_t>(SHA_512_256_PKCS_ID, + SHA_512_256_PKCS_ID + sizeof(SHA_512_256_PKCS_ID)); + + if(name == "SHA-3(224)") + return std::vector<uint8_t>(SHA3_224_PKCS_ID, + SHA3_224_PKCS_ID + sizeof(SHA3_224_PKCS_ID)); + + if(name == "SHA-3(256)") + return std::vector<uint8_t>(SHA3_256_PKCS_ID, + SHA3_256_PKCS_ID + sizeof(SHA3_256_PKCS_ID)); + + if(name == "SHA-3(384)") + return std::vector<uint8_t>(SHA3_384_PKCS_ID, + SHA3_384_PKCS_ID + sizeof(SHA3_384_PKCS_ID)); + + if(name == "SHA-3(512)") + return std::vector<uint8_t>(SHA3_512_PKCS_ID, + SHA3_512_PKCS_ID + sizeof(SHA3_512_PKCS_ID)); + + if(name == "SM3") + return std::vector<uint8_t>(SM3_PKCS_ID, SM3_PKCS_ID + sizeof(SM3_PKCS_ID)); + + throw Invalid_Argument("No PKCS #1 identifier for " + name); + } + +/* +* HashID as specified by IEEE 1363/X9.31 +*/ +uint8_t ieee1363_hash_id(const std::string& name) + { + if(name == "SHA-160" || name == "SHA-1" || name == "SHA1") + return 0x33; + + if(name == "SHA-224") return 0x38; + if(name == "SHA-256") return 0x34; + if(name == "SHA-384") return 0x36; + if(name == "SHA-512") return 0x35; + + if(name == "RIPEMD-160") return 0x31; + + if(name == "Whirlpool") return 0x37; + + return 0; + } + +} +/* * Hex Encoding and Decoding * (C) 2010,2020 Jack Lloyd * @@ -15687,6 +17352,154 @@ std::vector<uint8_t> hex_decode(const std::string& input, } /* +* HMAC +* (C) 1999-2007,2014,2020 Jack Lloyd +* 2007 Yves Jerschow +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/* +* Update a HMAC Calculation +*/ +void HMAC::add_data(const uint8_t input[], size_t length) + { + verify_key_set(m_ikey.empty() == false); + m_hash->update(input, length); + } + +/* +* Finalize a HMAC Calculation +*/ +void HMAC::final_result(uint8_t mac[]) + { + verify_key_set(m_okey.empty() == false); + m_hash->final(mac); + m_hash->update(m_okey); + m_hash->update(mac, m_hash_output_length); + m_hash->final(mac); + m_hash->update(m_ikey); + } + +Key_Length_Specification HMAC::key_spec() const + { + // Support very long lengths for things like PBKDF2 and the TLS PRF + return Key_Length_Specification(0, 4096); + } + +size_t HMAC::output_length() const + { + return m_hash_output_length; + } + +/* +* HMAC Key Schedule +*/ +void HMAC::key_schedule(const uint8_t key[], size_t length) + { + const uint8_t ipad = 0x36; + const uint8_t opad = 0x5C; + + m_hash->clear(); + + m_ikey.resize(m_hash_block_size); + m_okey.resize(m_hash_block_size); + + clear_mem(m_ikey.data(), m_ikey.size()); + clear_mem(m_okey.data(), m_okey.size()); + + /* + * Sometimes the HMAC key length itself is sensitive, as with PBKDF2 where it + * reveals the length of the passphrase. Make some attempt to hide this to + * side channels. Clearly if the secret is longer than the block size then the + * branch to hash first reveals that. In addition, counting the number of + * compression functions executed reveals the size at the granularity of the + * hash function's block size. + * + * The greater concern is for smaller keys; being able to detect when a + * passphrase is say 4 bytes may assist choosing weaker targets. Even though + * the loop bounds are constant, we can only actually read key[0..length] so + * it doesn't seem possible to make this computation truly constant time. + * + * We don't mind leaking if the length is exactly zero since that's + * trivial to simply check. + */ + + if(length > m_hash_block_size) + { + m_hash->update(key, length); + m_hash->final(m_ikey.data()); + } + else if(length > 0) + { + for(size_t i = 0, i_mod_length = 0; i != m_hash_block_size; ++i) + { + /* + access key[i % length] but avoiding division due to variable + time computation on some processors. + */ + auto needs_reduction = CT::Mask<size_t>::is_lte(length, i_mod_length); + i_mod_length = needs_reduction.select(0, i_mod_length); + const uint8_t kb = key[i_mod_length]; + + auto in_range = CT::Mask<size_t>::is_lt(i, length); + m_ikey[i] = static_cast<uint8_t>(in_range.if_set_return(kb)); + i_mod_length += 1; + } + } + + for(size_t i = 0; i != m_hash_block_size; ++i) + { + m_ikey[i] ^= ipad; + m_okey[i] = m_ikey[i] ^ ipad ^ opad; + } + + m_hash->update(m_ikey); + } + +/* +* Clear memory of sensitive data +*/ +void HMAC::clear() + { + m_hash->clear(); + zap(m_ikey); + zap(m_okey); + } + +/* +* Return the name of this type +*/ +std::string HMAC::name() const + { + return "HMAC(" + m_hash->name() + ")"; + } + +/* +* Return a new_object of this object +*/ +std::unique_ptr<MessageAuthenticationCode> HMAC::new_object() const + { + return std::make_unique<HMAC>(m_hash->new_object()); + } + +/* +* HMAC Constructor +*/ +HMAC::HMAC(std::unique_ptr<HashFunction> hash) : + m_hash(std::move(hash)), + m_hash_output_length(m_hash->output_length()), + m_hash_block_size(m_hash->hash_block_size()) + { + BOTAN_ARG_CHECK(m_hash_block_size >= m_hash_output_length, + "HMAC is not compatible with this hash function"); + } + +} +/* * KDF Retrieval * (C) 1999-2007 Jack Lloyd * @@ -16271,6 +18084,337 @@ void mgf1_mask(HashFunction& hash, } /* +* CBC Padding Methods +* (C) 1999-2007,2013,2018,2020 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/** +* Get a block cipher padding method by name +*/ +std::unique_ptr<BlockCipherModePaddingMethod> +BlockCipherModePaddingMethod::create(const std::string& algo_spec) + { + if(algo_spec == "NoPadding") + return std::make_unique<Null_Padding>(); + + if(algo_spec == "PKCS7") + return std::make_unique<PKCS7_Padding>(); + + if(algo_spec == "OneAndZeros") + return std::make_unique<OneAndZeros_Padding>(); + + if(algo_spec == "X9.23") + return std::make_unique<ANSI_X923_Padding>(); + + if(algo_spec == "ESP") + return std::make_unique<ESP_Padding>(); + + return nullptr; + } + +/* +* Pad with PKCS #7 Method +*/ +void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0202 + 030303 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(padding_len, buffer[i]); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with PKCS #7 Method +*/ +size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t last_byte = input[input_length-1]; + + /* + The input should == the block size so if the last byte exceeds + that then the padding is certainly invalid + */ + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Does this byte equal the expected pad byte? + const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte); + + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + bad_input |= in_range & (~pad_eq); + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ANSI X9.23 Method +*/ +void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0002 + 000003 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_zero_padding = buffer.size() - 1; + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(0, buffer[i]); + } + + buffer[buffer.size()-1] = padding_len; + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ANSI X9.23 Method +*/ +size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const size_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + const auto pad_is_nonzero = CT::Mask<size_t>::expand(input[i]); + bad_input |= pad_is_nonzero & in_range; + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with One and Zeros Method +*/ +void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 80 + 8000 + 800000 + ... + */ + + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_80 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding)); + auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding)); + buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i])); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with One and Zeros Method +*/ +size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + auto bad_input = CT::Mask<uint8_t>::cleared(); + auto seen_0x80 = CT::Mask<uint8_t>::cleared(); + + size_t pad_pos = input_length - 1; + size_t i = input_length; + + while(i) + { + const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i-1], 0x80); + const auto is_zero = CT::Mask<uint8_t>::is_zero(input[i-1]); + + seen_0x80 |= is_0x80; + pad_pos -= seen_0x80.if_not_set_return(1); + bad_input |= ~seen_0x80 & ~is_zero; + i--; + } + bad_input |= ~seen_0x80; + + CT::unpoison(input, input_length); + + return CT::Mask<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ESP Padding Method +*/ +void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0102 + 010203 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + uint8_t pad_ctr = 0x01; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(pad_ctr, buffer[i]); + pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ESP Padding Method +*/ +size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t input_length_8 = static_cast<uint8_t>(input_length); + const uint8_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) | + CT::Mask<uint8_t>::is_gt(last_byte, input_length_8); + + const uint8_t pad_pos = input_length_8 - last_byte; + size_t i = input_length_8 - 1; + while(i) + { + const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos); + const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i-1], input[i]-1); + + bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing; + --i; + } + + CT::unpoison(input, input_length); + return bad_input.select_and_unpoison(input_length_8, pad_pos); + } + + +} +/* * Cipher Modes * (C) 2015 Jack Lloyd * @@ -25107,6 +27251,797 @@ void Modular_Reducer::reduce(BigInt& t1, const BigInt& x, secure_vector<word>& w } /* +* PKCS #5 PBES2 +* (C) 1999-2008,2014,2021 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +bool known_pbes_cipher_mode(const std::string& mode) + { + return (mode == "CBC" || mode == "GCM" || mode == "SIV"); + } + +secure_vector<uint8_t> derive_key(const std::string& passphrase, + const AlgorithmIdentifier& kdf_algo, + size_t default_key_size) + { + if(kdf_algo.get_oid() == OID::from_string("PKCS5.PBKDF2")) + { + secure_vector<uint8_t> salt; + size_t iterations = 0, key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_parameters()) + .start_sequence() + .decode(salt, ASN1_Type::OctetString) + .decode(iterations) + .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal) + .decode_optional(prf_algo, ASN1_Type::Sequence, ASN1_Class::Constructed, + AlgorithmIdentifier("HMAC(SHA-160)", + AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons(); + + if(salt.size() < 8) + throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); + + if(key_length == 0) + key_length = default_key_size; + + const std::string prf = OIDS::oid2str_or_throw(prf_algo.get_oid()); + auto pbkdf_fam = PasswordHashFamily::create_or_throw("PBKDF2(" + prf + ")"); + auto pbkdf = pbkdf_fam->from_params(iterations); + + secure_vector<uint8_t> derived_key(key_length); + pbkdf->derive_key(derived_key.data(), derived_key.size(), + passphrase.data(), passphrase.size(), + salt.data(), salt.size()); + return derived_key; + } + else if(kdf_algo.get_oid() == OID::from_string("Scrypt")) + { + secure_vector<uint8_t> salt; + size_t N = 0, r = 0, p = 0; + size_t key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_parameters()) + .start_sequence() + .decode(salt, ASN1_Type::OctetString) + .decode(N) + .decode(r) + .decode(p) + .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal) + .end_cons(); + + if(key_length == 0) + key_length = default_key_size; + + secure_vector<uint8_t> derived_key(key_length); + + auto pwdhash_fam = PasswordHashFamily::create_or_throw("Scrypt"); + auto pwdhash = pwdhash_fam->from_params(N, r, p); + pwdhash->derive_key(derived_key.data(), derived_key.size(), + passphrase.data(), passphrase.size(), + salt.data(), salt.size()); + + return derived_key; + } + else + throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " + + kdf_algo.get_oid().to_string()); + } + +secure_vector<uint8_t> derive_key(const std::string& passphrase, + const std::string& digest, + RandomNumberGenerator& rng, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + size_t key_length, + AlgorithmIdentifier& kdf_algo) + { + const secure_vector<uint8_t> salt = rng.random_vec(12); + + if(digest == "Scrypt") + { + auto pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt"); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + const size_t N = pwhash->memory_param(); + const size_t r = pwhash->iterations(); + const size_t p = pwhash->parallelism(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = 0; + + std::vector<uint8_t> scrypt_params; + DER_Encoder(scrypt_params) + .start_sequence() + .encode(salt, ASN1_Type::OctetString) + .encode(N) + .encode(r) + .encode(p) + .encode(key_length) + .end_cons(); + + kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params); + return key; + } + else + { + const std::string prf = "HMAC(" + digest + ")"; + const std::string pbkdf_name = "PBKDF2(" + prf + ")"; + + std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create(pbkdf_name); + if(!pwhash_fam) + throw Invalid_Argument("Unknown password hash digest " + digest); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + std::vector<uint8_t> pbkdf2_params; + + const size_t iterations = pwhash->iterations(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = iterations; + + DER_Encoder(pbkdf2_params) + .start_sequence() + .encode(salt, ASN1_Type::OctetString) + .encode(iterations) + .encode(key_length) + .encode_if(prf != "HMAC(SHA-160)", + AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons(); + + kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params); + return key; + } + } + +/* +* PKCS#5 v2.0 PBE Encryption +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_shared(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + const std::string& cipher, + const std::string& prf, + RandomNumberGenerator& rng) + { + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Encoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + + if(!known_pbes_cipher_mode(cipher_spec[1])) + throw Encoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + const OID cipher_oid = OIDS::str2oid_or_empty(cipher); + if(cipher_oid.empty()) + throw Encoding_Error("PBE-PKCS5 v2.0: No OID assigned for " + cipher); + + std::unique_ptr<Cipher_Mode> enc = Cipher_Mode::create(cipher, ENCRYPTION); + + if(!enc) + throw Decoding_Error("PBE-PKCS5 cannot encrypt no cipher " + cipher); + + const size_t key_length = enc->key_spec().maximum_keylength(); + + const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length()); + + AlgorithmIdentifier kdf_algo; + + const secure_vector<uint8_t> derived_key = + derive_key(passphrase, prf, rng, + msec_in_iterations_out, iterations_if_msec_null, + key_length, kdf_algo); + + enc->set_key(derived_key); + enc->start(iv); + secure_vector<uint8_t> ctext = key_bits; + enc->finish(ctext); + + std::vector<uint8_t> encoded_iv; + DER_Encoder(encoded_iv).encode(iv, ASN1_Type::OctetString); + + std::vector<uint8_t> pbes2_params; + DER_Encoder(pbes2_params) + .start_sequence() + .encode(kdf_algo) + .encode(AlgorithmIdentifier(cipher, encoded_iv)) + .end_cons(); + + AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params); + + return std::make_pair(id, unlock(ctext)); + } + +} + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + // return value msec_in_iterations_out discarded + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + + auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + + if(out_iterations_if_nonnull) + *out_iterations_if_nonnull = msec_in_iterations_out; + + return ret; + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t pbkdf_iter, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng); + } + +secure_vector<uint8_t> +pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params) + { + AlgorithmIdentifier kdf_algo, enc_algo; + + BER_Decoder(params) + .start_sequence() + .decode(kdf_algo) + .decode(enc_algo) + .end_cons(); + + const std::string cipher = OIDS::oid2str_or_throw(enc_algo.get_oid()); + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + if(!known_pbes_cipher_mode(cipher_spec[1])) + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + secure_vector<uint8_t> iv; + BER_Decoder(enc_algo.get_parameters()).decode(iv, ASN1_Type::OctetString).verify_end(); + + std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, DECRYPTION); + if(!dec) + throw Decoding_Error("PBE-PKCS5 cannot decrypt no cipher " + cipher); + + dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength())); + + dec->start(iv); + + secure_vector<uint8_t> buf = key_bits; + dec->finish(buf); + + return buf; + } + +} +/* +* PBKDF +* (C) 2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_PBKDF2) +#endif + +#if defined(BOTAN_HAS_PGP_S2K) +#endif + +namespace Botan { + +std::unique_ptr<PBKDF> PBKDF::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + // TODO OpenSSL + + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::make_unique<PKCS5_PBKDF2>(mac.release()); + + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::make_unique<PKCS5_PBKDF2>(mac.release()); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + return std::make_unique<OpenPGP_S2K>(hash.release()); + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PBKDF> +PBKDF::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PBKDF::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PBKDF", algo, provider); + } + +std::vector<std::string> PBKDF::providers(const std::string& algo_spec) + { + return probe_providers_of<PBKDF>(algo_spec); + } + +void PBKDF::pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + iterations = pbkdf(out, out_len, passphrase, salt, salt_len, 0, msec); + } + +void PBKDF::pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + if(iterations == 0) + throw Invalid_Argument(name() + ": Invalid iteration count"); + + const size_t iterations_run = pbkdf(out, out_len, passphrase, + salt, salt_len, iterations, + std::chrono::milliseconds(0)); + BOTAN_ASSERT_EQUAL(iterations, iterations_run, "Expected PBKDF iterations"); + } + +secure_vector<uint8_t> PBKDF::pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_iterations(out.data(), out_len, passphrase, salt, salt_len, iterations); + return out; + } + +secure_vector<uint8_t> PBKDF::pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_timed(out.data(), out_len, passphrase, salt, salt_len, msec, iterations); + return out; + } + +} +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_PBKDF2) +#endif + +#if defined(BOTAN_HAS_PGP_S2K) +#endif + +#if defined(BOTAN_HAS_SCRYPT) +#endif + +#if defined(BOTAN_HAS_ARGON2) +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) +#endif + +namespace Botan { + +void PasswordHash::derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t ad[], size_t ad_len, + const uint8_t key[], size_t key_len) const + { + BOTAN_UNUSED(ad, key); + + if(ad_len == 0 && key_len == 0) + return this->derive_key(out, out_len, + password, password_len, + salt, salt_len); + else + throw Not_Implemented("PasswordHash " + this->to_string() + " does not support AD or key"); + } + +std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::make_unique<PBKDF2_Family>(mac.release()); + + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::make_unique<PBKDF2_Family>(mac.release()); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_SCRYPT) + if(req.algo_name() == "Scrypt") + { + return std::make_unique<Scrypt_Family>(); + } +#endif + +#if defined(BOTAN_HAS_ARGON2) + if(req.algo_name() == "Argon2d") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(0)); + } + else if(req.algo_name() == "Argon2i") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(1)); + } + else if(req.algo_name() == "Argon2id") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(2)); + } +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + if(req.algo_name() == "Bcrypt-PBKDF") + { + return std::make_unique<Bcrypt_PBKDF_Family>(); + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + { + return std::make_unique<RFC4880_S2K_Family>(hash.release()); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PasswordHashFamily> +PasswordHashFamily::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PasswordHashFamily::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PasswordHashFamily", algo, provider); + } + +std::vector<std::string> PasswordHashFamily::providers(const std::string& algo_spec) + { + return probe_providers_of<PasswordHashFamily>(algo_spec); + } + +} +/* +* PBKDF2 +* (C) 1999-2007 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +void pbkdf2_set_key(MessageAuthenticationCode& prf, + const char* password, + size_t password_len) + { + try + { + prf.set_key(cast_char_ptr_to_uint8(password), password_len); + } + catch(Invalid_Key_Length&) + { + throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size"); + } + } + +} + +size_t +pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) + { + if(iterations == 0) + { + iterations = PBKDF2(prf, out_len, msec).iterations(); + } + + PBKDF2 pbkdf2(prf, iterations); + + pbkdf2.derive_key(out, out_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +namespace { + +size_t tune_pbkdf2(MessageAuthenticationCode& prf, + size_t output_length, + uint32_t msec) + { + if(output_length == 0) + output_length = 1; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + secure_vector<uint8_t> U(prf_sz); + + const size_t trial_iterations = 2000; + + // Short output ensures we only need a single PBKDF2 block + + Timer timer("PBKDF2"); + + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + prf.set_key(nullptr, 0); + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t out[12] = { 0 }; + uint8_t salt[12] = { 0 }; + pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations); + }); + + if(timer.events() == 0) + return trial_iterations; + + const uint64_t duration_nsec = timer.value() / timer.events(); + + const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000; + + if(duration_nsec > desired_nsec) + return trial_iterations; + + const size_t blocks_needed = (output_length + prf_sz - 1) / prf_sz; + + const size_t multiplier = static_cast<size_t>(desired_nsec / duration_nsec / blocks_needed); + + if(multiplier == 0) + return trial_iterations; + else + return trial_iterations * multiplier; + } + +} + +void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const uint8_t salt[], + size_t salt_len, + size_t iterations) + { + if(iterations == 0) + throw Invalid_Argument("PBKDF2: Invalid iteration count"); + + clear_mem(out, out_len); + + if(out_len == 0) + return; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + + secure_vector<uint8_t> U(prf_sz); + + uint32_t counter = 1; + while(out_len) + { + const size_t prf_output = std::min<size_t>(prf_sz, out_len); + + prf.update(salt, salt_len); + prf.update_be(counter++); + prf.final(U.data()); + + xor_buf(out, U.data(), prf_output); + + for(size_t i = 1; i != iterations; ++i) + { + prf.update(U); + prf.final(U.data()); + xor_buf(out, U.data(), prf_output); + } + + out_len -= prf_output; + out += prf_output; + } + } + +// PBKDF interface +size_t +PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const + { + if(iterations == 0) + { + iterations = PBKDF2(*m_mac, key_len, msec).iterations(); + } + + PBKDF2 pbkdf2(*m_mac, iterations); + + pbkdf2.derive_key(key, key_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +std::string PKCS5_PBKDF2::name() const + { + return "PBKDF2(" + m_mac->name() + ")"; + } + +std::unique_ptr<PBKDF> PKCS5_PBKDF2::new_object() const + { + return std::make_unique<PKCS5_PBKDF2>(m_mac->clone()); + } + +// PasswordHash interface + +PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) : + m_prf(prf.new_object()), + m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count()))) + {} + +std::string PBKDF2::to_string() const + { + return "PBKDF2(" + m_prf->name() + "," + std::to_string(m_iterations) + ")"; + } + +void PBKDF2::derive_key(uint8_t out[], size_t out_len, + const char* password, const size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + pbkdf2_set_key(*m_prf, password, password_len); + pbkdf2(*m_prf, out, out_len, salt, salt_len, m_iterations); + } + +std::string PBKDF2_Family::name() const + { + return "PBKDF2(" + m_prf->name() + ")"; + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t /*max_memory_usage_mb*/) const + { + return std::make_unique<PBKDF2>(*m_prf, output_len, msec); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const + { + return std::make_unique<PBKDF2>(*m_prf, 150000); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_params(size_t iter, size_t /*i2*/, size_t /*i3*/) const + { + return std::make_unique<PBKDF2>(*m_prf, iter); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_iterations(size_t iter) const + { + return std::make_unique<PBKDF2>(*m_prf, iter); + } + +} +/* * PEM Encoding/Decoding * (C) 1999-2007 Jack Lloyd * diff --git a/include/amalgamation-amd64/botan_all.h b/include/amalgamation-amd64/botan_all.h index e2e8671..fbddeb2 100644 --- a/include/amalgamation-amd64/botan_all.h +++ b/include/amalgamation-amd64/botan_all.h @@ -31,7 +31,7 @@ * Build configuration for Botan 3.0.0-alpha0 * * Automatically generated from -* 'configure.py --cpu=x86_64 --prefix=/usr/local/projects/zafena/elevator/botan/dist-amd64-min --minimized-build --enable-modules=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,chacha,chacha20poly1305,aead,stream,sha1,sha2_32,system_rng,sha1_sse2,sha1_x86,sha2_32_x86,simd,chacha_simd32,chacha_avx2,simd_avx2 --cxxflags= --ldflags= --amalgamation --with-doxygen' +* 'configure.py --cpu=x86_64 --prefix=/usr/local/projects/zafena/cipherpack/botan/dist-amd64-min --minimized-build --enable-modules=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,emsa_raw,pbes2,eme_pkcs1,emsa_pkcs1,chacha,chacha20poly1305,aead,stream,sha1,sha2_32,system_rng,sha1_sse2,sha1_x86,sha2_32_x86,simd,chacha_simd32,chacha_avx2,simd_avx2 --cxxflags= --ldflags= --amalgamation --with-doxygen' * * Target * - Compiler: g++ -fstack-protector -m64 -pthread -std=c++17 -D_REENTRANT -O3 @@ -57,9 +57,9 @@ #define BOTAN_MP_WORD_BITS 64 -#define BOTAN_INSTALL_PREFIX R"(/usr/local/projects/zafena/elevator/botan/dist-amd64-min)" +#define BOTAN_INSTALL_PREFIX R"(/usr/local/projects/zafena/cipherpack/botan/dist-amd64-min)" #define BOTAN_INSTALL_HEADER_DIR R"(include/botan-3)" -#define BOTAN_INSTALL_LIB_DIR R"(/usr/local/projects/zafena/elevator/botan/dist-amd64-min/lib)" +#define BOTAN_INSTALL_LIB_DIR R"(/usr/local/projects/zafena/cipherpack/botan/dist-amd64-min/lib)" #define BOTAN_LIB_LINK "" #define BOTAN_LINK_FLAGS "-fstack-protector -m64 -pthread" @@ -121,26 +121,39 @@ #define BOTAN_HAS_BASE64_CODEC 20131128 #define BOTAN_HAS_BIGINT 20210423 #define BOTAN_HAS_BIGINT_MP 20151225 +#define BOTAN_HAS_BLOCK_CIPHER 20131128 #define BOTAN_HAS_CHACHA 20180807 #define BOTAN_HAS_CHACHA_AVX2 20180418 #define BOTAN_HAS_CHACHA_SIMD32 20181104 #define BOTAN_HAS_CIPHER_MODES 20180124 +#define BOTAN_HAS_CIPHER_MODE_PADDING 20131128 #define BOTAN_HAS_CPUID 20170917 #define BOTAN_HAS_EME_OAEP 20180305 +#define BOTAN_HAS_EME_PKCS1 20190426 +#define BOTAN_HAS_EME_PKCS1v15 20131128 #define BOTAN_HAS_EME_RAW 20150313 #define BOTAN_HAS_EMSA1 20131128 +#define BOTAN_HAS_EMSA_PKCS1 20140118 #define BOTAN_HAS_EMSA_PSSR 20131128 +#define BOTAN_HAS_EMSA_RAW 20131128 #define BOTAN_HAS_ENTROPY_SOURCE 20151120 #define BOTAN_HAS_HASH 20180112 +#define BOTAN_HAS_HASH_ID 20131128 #define BOTAN_HAS_HEX_CODEC 20131128 +#define BOTAN_HAS_HMAC 20131128 #define BOTAN_HAS_KDF_BASE 20131128 #define BOTAN_HAS_MAC 20150626 #define BOTAN_HAS_MDX_HASH_FUNCTION 20131128 #define BOTAN_HAS_MGF1 20140118 #define BOTAN_HAS_MODES 20150626 +#define BOTAN_HAS_MODE_CBC 20131128 #define BOTAN_HAS_NUMBERTHEORY 20201108 #define BOTAN_HAS_OCSP 20201106 +#define BOTAN_HAS_PASSWORD_HASHING 20210419 +#define BOTAN_HAS_PBKDF 20180902 +#define BOTAN_HAS_PBKDF2 20180902 #define BOTAN_HAS_PEM_CODEC 20131128 +#define BOTAN_HAS_PKCS5_PBES2 20141119 #define BOTAN_HAS_PK_PADDING 20131128 #define BOTAN_HAS_POLY1305 20141227 #define BOTAN_HAS_PUBLIC_KEY_CRYPTO 20131128 @@ -4494,6 +4507,249 @@ inline void swap<Botan::BigInt>(Botan::BigInt& x, Botan::BigInt& y) namespace Botan { /** +* This class represents a block cipher object. +*/ +class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm + { + public: + + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<BlockCipher> + create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<BlockCipher> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + * @param algo_spec algorithm name + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * @return block size of this algorithm + */ + virtual size_t block_size() const = 0; + + /** + * @return native parallelism of this cipher in blocks + */ + virtual size_t parallelism() const { return 1; } + + /** + * @return prefererred parallelism of this cipher in bytes + */ + size_t parallel_bytes() const + { + return parallelism() * block_size() * BOTAN_BLOCK_CIPHER_PAR_MULT; + } + + /** + * @return provider information about this implementation. Default is "base", + * might also return "sse2", "avx2", "openssl", or some other arbitrary string. + */ + virtual std::string provider() const { return "base"; } + + /** + * Encrypt a block. + * @param in The plaintext block to be encrypted as a byte array. + * Must be of length block_size(). + * @param out The byte array designated to hold the encrypted block. + * Must be of length block_size(). + */ + void encrypt(const uint8_t in[], uint8_t out[]) const + { encrypt_n(in, out, 1); } + + /** + * Decrypt a block. + * @param in The ciphertext block to be decypted as a byte array. + * Must be of length block_size(). + * @param out The byte array designated to hold the decrypted block. + * Must be of length block_size(). + */ + void decrypt(const uint8_t in[], uint8_t out[]) const + { decrypt_n(in, out, 1); } + + /** + * Encrypt a block. + * @param block the plaintext block to be encrypted + * Must be of length block_size(). Will hold the result when the function + * has finished. + */ + void encrypt(uint8_t block[]) const { encrypt_n(block, block, 1); } + + /** + * Decrypt a block. + * @param block the ciphertext block to be decrypted + * Must be of length block_size(). Will hold the result when the function + * has finished. + */ + void decrypt(uint8_t block[]) const { decrypt_n(block, block, 1); } + + /** + * Encrypt one or more blocks + * @param block the input/output buffer (multiple of block_size()) + */ + template<typename Alloc> + void encrypt(std::vector<uint8_t, Alloc>& block) const + { + return encrypt_n(block.data(), block.data(), block.size() / block_size()); + } + + /** + * Decrypt one or more blocks + * @param block the input/output buffer (multiple of block_size()) + */ + template<typename Alloc> + void decrypt(std::vector<uint8_t, Alloc>& block) const + { + return decrypt_n(block.data(), block.data(), block.size() / block_size()); + } + + /** + * Encrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + */ + template<typename Alloc, typename Alloc2> + void encrypt(const std::vector<uint8_t, Alloc>& in, + std::vector<uint8_t, Alloc2>& out) const + { + return encrypt_n(in.data(), out.data(), in.size() / block_size()); + } + + /** + * Decrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + */ + template<typename Alloc, typename Alloc2> + void decrypt(const std::vector<uint8_t, Alloc>& in, + std::vector<uint8_t, Alloc2>& out) const + { + return decrypt_n(in.data(), out.data(), in.size() / block_size()); + } + + /** + * Encrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + * @param blocks the number of blocks to process + */ + virtual void encrypt_n(const uint8_t in[], uint8_t out[], + size_t blocks) const = 0; + + /** + * Decrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + * @param blocks the number of blocks to process + */ + virtual void decrypt_n(const uint8_t in[], uint8_t out[], + size_t blocks) const = 0; + + virtual void encrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const + { + const size_t BS = block_size(); + xor_buf(data, mask, blocks * BS); + encrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + virtual void decrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const + { + const size_t BS = block_size(); + xor_buf(data, mask, blocks * BS); + decrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + /** + * @return new object representing the same algorithm as *this + */ + virtual std::unique_ptr<BlockCipher> new_object() const = 0; + + BlockCipher* clone() const + { + return this->new_object().release(); + } + + virtual ~BlockCipher() = default; + }; + +/** +* Tweakable block ciphers allow setting a tweak which is a non-keyed +* value which affects the encryption/decryption operation. +*/ +class BOTAN_PUBLIC_API(2,8) Tweakable_Block_Cipher : public BlockCipher + { + public: + /** + * Set the tweak value. This must be called after setting a key. The value + * persists until either set_tweak, set_key, or clear is called. + * Different algorithms support different tweak length(s). If called with + * an unsupported length, Invalid_Argument will be thrown. + */ + virtual void set_tweak(const uint8_t tweak[], size_t len) = 0; + }; + +/** +* Represents a block cipher with a single fixed block size +*/ +template<size_t BS, size_t KMIN, size_t KMAX = 0, size_t KMOD = 1, typename BaseClass = BlockCipher> +class Block_Cipher_Fixed_Params : public BaseClass + { + public: + enum { BLOCK_SIZE = BS }; + size_t block_size() const final override { return BS; } + + // override to take advantage of compile time constant block size + void encrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const final override + { + xor_buf(data, mask, blocks * BS); + this->encrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + void decrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const final override + { + xor_buf(data, mask, blocks * BS); + this->decrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + Key_Length_Specification key_spec() const final override + { + return Key_Length_Specification(KMIN, KMAX, KMOD); + } + }; + +} + +namespace Botan { + +/** * This class represents any kind of computation which uses an internal * state, such as hash functions or MACs */ @@ -7985,6 +8241,517 @@ BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID& oid); namespace Botan { +/** +* Base class for PBKDF (password based key derivation function) +* implementations. Converts a password into a key using a salt +* and iterated hashing to make brute force attacks harder. +* +* Starting in 2.8 this functionality is also offered by PasswordHash. +* The PBKDF interface may be removed in a future release. +*/ +class BOTAN_PUBLIC_API(2,0) PBKDF + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PBKDF> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PBKDF> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * @return new instance of this same algorithm + */ + virtual std::unique_ptr<PBKDF> new_object() const = 0; + + /** + * @return new instance of this same algorithm + */ + PBKDF* clone() const + { + return this->new_object().release(); + } + + /** + * @return name of this PBKDF + */ + virtual std::string name() const = 0; + + virtual ~PBKDF() = default; + + /** + * Derive a key from a passphrase for a number of iterations + * specified by either iterations or if iterations == 0 then + * running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @return the number of iterations performed + */ + virtual size_t pbkdf(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const = 0; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + void pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + */ + void pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + // Following kept for compat with 1.10: + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt, salt_len, iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param iterations the number of iterations to use (use 10K or more) + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt.data(), salt.size(), iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt, salt_len, msec, iterations); + } + + /** + * Derive a key from a passphrase using a certain amount of time + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt.data(), salt.size(), msec, iterations); + } + }; + +/* +* Compatibility typedef +*/ +typedef PBKDF S2K; + +/** +* Password based key derivation function factory method +* @param algo_spec the name of the desired PBKDF algorithm +* @param provider the provider to use +* @return pointer to newly allocated object of that type +*/ +inline PBKDF* get_pbkdf(const std::string& algo_spec, + const std::string& provider = "") + { + return PBKDF::create_or_throw(algo_spec, provider).release(); + } + +inline PBKDF* get_s2k(const std::string& algo_spec) + { + return get_pbkdf(algo_spec); + } + + +} + +namespace Botan { + +/** +* Base class for password based key derivation functions. +* +* Converts a password into a key using a salt and iterated hashing to +* make brute force attacks harder. +*/ +class BOTAN_PUBLIC_API(2,8) PasswordHash + { + public: + virtual ~PasswordHash() = default; + + virtual std::string to_string() const = 0; + + /** + * Most password hashes have some notion of iterations. + */ + virtual size_t iterations() const = 0; + + /** + * Some password hashing algorithms have a parameter which controls how + * much memory is used. If not supported by some algorithm, returns 0. + */ + virtual size_t memory_param() const { return 0; } + + /** + * Some password hashing algorithms have a parallelism parameter. + * If the algorithm does not support this notion, then the + * function returns zero. This allows distinguishing between a + * password hash which just does not support parallel operation, + * vs one that does support parallel operation but which has been + * configured to use a single lane. + */ + virtual size_t parallelism() const { return 0; } + + /** + * Returns an estimate of the total number of bytes required to perform this + * key derivation. + * + * If this algorithm uses a small and constant amount of memory, with no + * effort made towards being memory hard, this function returns 0. + */ + virtual size_t total_memory_usage() const { return 0; } + + /** + * Derive a key from a password + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const = 0; + + /** + * Derive a key from a password plus additional data and/or a secret key + * + * Currently this is only supported for Argon2. Using a non-empty AD or key + * with other algorithms will cause a Not_Implemented exception. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param ad some additional data + * @param ad_len length of ad in bytes + * @param key a secret key + * @param key_len length of key in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t ad[], size_t ad_len, + const uint8_t key[], size_t key_len) const; + }; + +class BOTAN_PUBLIC_API(2,8) PasswordHashFamily + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PasswordHashFamily> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PasswordHashFamily> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + virtual ~PasswordHashFamily() = default; + + /** + * @return name of this PasswordHash + */ + virtual std::string name() const = 0; + + /** + * Return a new parameter set tuned for this machine + * @param output_length how long the output length will be + * @param msec the desired execution time in milliseconds + * + * @param max_memory_usage_mb some password hash functions can use a tunable + * amount of memory, in this case max_memory_usage limits the amount of RAM + * the returned parameters will require, in mebibytes (2**20 bytes). It may + * require some small amount above the request. Set to zero to place no + * limit at all. + */ + virtual std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory_usage_mb = 0) const = 0; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + virtual std::unique_ptr<PasswordHash> default_params() const = 0; + + /** + * Return a parameter chosen based on a rough approximation with the + * specified iteration count. The exact value this returns for a particular + * algorithm may change from over time. Think of it as an alternative to + * tune, where time is expressed in terms of PBKDF2 iterations rather than + * milliseconds. + */ + virtual std::unique_ptr<PasswordHash> from_iterations(size_t iterations) const = 0; + + /** + * Create a password hash using some scheme specific format. Parameters are as follows: + * - For PBKDF2, PGP-S2K, and Bcrypt-PBKDF, i1 is iterations + * - Scrypt uses N, r, p for i{1-3} + * - Argon2 family uses memory (in KB), iterations, and parallelism for i{1-3} + * + * All unneeded parameters should be set to 0 or left blank. + */ + virtual std::unique_ptr<PasswordHash> from_params( + size_t i1, + size_t i2 = 0, + size_t i3 = 0) const = 0; + }; + +} + +BOTAN_FUTURE_INTERNAL_HEADER(pbkdf2.h) + +namespace Botan { + +BOTAN_PUBLIC_API(2,0) size_t pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec); + +/** +* Perform PBKDF2. The prf is assumed to be keyed already. +*/ +BOTAN_PUBLIC_API(2,8) void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], size_t out_len, + const uint8_t salt[], size_t salt_len, + size_t iterations); + +/** +* PBKDF2 +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2 final : public PasswordHash + { + public: + PBKDF2(const MessageAuthenticationCode& prf, size_t iter) : + m_prf(prf.new_object()), + m_iterations(iter) + {} + + PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec); + + size_t iterations() const override { return m_iterations; } + + std::string to_string() const override; + + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + size_t m_iterations; + }; + +/** +* Family of PKCS #5 PBKDF2 operations +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2_Family final : public PasswordHashFamily + { + public: + PBKDF2_Family(MessageAuthenticationCode* prf) : m_prf(prf) {} + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_len, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t iter, size_t, size_t) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + }; + +/** +* PKCS #5 PBKDF2 (old interface) +*/ +class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF2 final : public PBKDF + { + public: + std::string name() const override; + + std::unique_ptr<PBKDF> new_object() const override; + + size_t pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; + + /** + * Create a PKCS #5 instance using the specified message auth code + * @param mac_fn the MAC object to use as PRF + */ + explicit PKCS5_PBKDF2(MessageAuthenticationCode* mac_fn) : m_mac(mac_fn) {} + private: + std::unique_ptr<MessageAuthenticationCode> m_mac; + }; + +} + +namespace Botan { + class DataSource; namespace PEM_Code { diff --git a/include/amalgamation-arm64/botan_all.cpp b/include/amalgamation-arm64/botan_all.cpp index 4cb262a..c128cb6 100644 --- a/include/amalgamation-arm64/botan_all.cpp +++ b/include/amalgamation-arm64/botan_all.cpp @@ -400,6 +400,297 @@ class BOTAN_TEST_API calendar_point namespace Botan { /** +* Block Cipher Mode Padding Method +* This class is pretty limited, it cannot deal well with +* randomized padding methods, or any padding method that +* wants to add more than one block. For instance, it should +* be possible to define cipher text stealing mode as simply +* a padding mode for CBC, which happens to consume the last +* two block (and requires use of the block cipher). +*/ +class BOTAN_TEST_API BlockCipherModePaddingMethod + { + public: + /** + * Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7") + * @param algo_spec block cipher padding mode name + */ + static std::unique_ptr<BlockCipherModePaddingMethod> create(const std::string& algo_spec); + + /** + * Add padding bytes to buffer. + * @param buffer data to pad + * @param final_block_bytes size of the final block in bytes + * @param block_size size of each block in bytes + */ + virtual void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const = 0; + + /** + * Remove padding bytes from block + * @param block the last block + * @param len the size of the block in bytes + * @return number of data bytes, or if the padding is invalid returns len + */ + virtual size_t unpad(const uint8_t block[], size_t len) const = 0; + + /** + * @param block_size of the cipher + * @return valid block size for this padding mode + */ + virtual bool valid_blocksize(size_t block_size) const = 0; + + /** + * @return name of the mode + */ + virtual std::string name() const = 0; + + /** + * virtual destructor + */ + virtual ~BlockCipherModePaddingMethod() = default; + }; + +/** +* PKCS#7 Padding +*/ +class BOTAN_TEST_API PKCS7_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "PKCS7"; } + }; + +/** +* ANSI X9.23 Padding +*/ +class BOTAN_TEST_API ANSI_X923_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "X9.23"; } + }; + +/** +* One And Zeros Padding (ISO/IEC 9797-1, padding method 2) +*/ +class BOTAN_TEST_API OneAndZeros_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2); } + + std::string name() const override { return "OneAndZeros"; } + }; + +/** +* ESP Padding (RFC 4304) +*/ +class BOTAN_TEST_API ESP_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "ESP"; } + }; + +/** +* Null Padding +*/ +class Null_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>&, size_t, size_t) const override + { + /* no padding */ + } + + size_t unpad(const uint8_t[], size_t size) const override { return size; } + + bool valid_blocksize(size_t) const override { return true; } + + std::string name() const override { return "NoPadding"; } + }; + +} + +namespace Botan { + +/** +* CBC Mode +*/ +class CBC_Mode : public Cipher_Mode + { + public: + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + size_t default_nonce_length() const override; + + bool valid_nonce_length(size_t n) const override; + + void clear() override; + + void reset() override; + + protected: + CBC_Mode(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding); + + const BlockCipher& cipher() const { return *m_cipher; } + + const BlockCipherModePaddingMethod& padding() const + { + BOTAN_ASSERT_NONNULL(m_padding); + return *m_padding; + } + + size_t block_size() const { return m_block_size; } + + secure_vector<uint8_t>& state() { return m_state; } + + uint8_t* state_ptr() { return m_state.data(); } + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + std::unique_ptr<BlockCipher> m_cipher; + std::unique_ptr<BlockCipherModePaddingMethod> m_padding; + secure_vector<uint8_t> m_state; + size_t m_block_size; + }; + +/** +* CBC Encryption +*/ +class CBC_Encryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Encryption(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + CBC_Mode(std::move(cipher), std::move(padding)) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + }; + +/** +* CBC Encryption with ciphertext stealing (CBC-CS3 variant) +*/ +class CTS_Encryption final : public CBC_Encryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Encryption(std::unique_ptr<BlockCipher> cipher) : + CBC_Encryption(std::move(cipher), nullptr) + {} + + size_t output_length(size_t input_length) const override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +/** +* CBC Decryption +*/ +class CBC_Decryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Decryption(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + CBC_Mode(std::move(cipher), std::move(padding)), + m_tempbuf(update_granularity()) + {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + + void reset() override; + + private: + secure_vector<uint8_t> m_tempbuf; + }; + +/** +* CBC Decryption with ciphertext stealing (CBC-CS3 variant) +*/ +class CTS_Decryption final : public CBC_Decryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Decryption(std::unique_ptr<BlockCipher> cipher) : + CBC_Decryption(std::move(cipher), nullptr) + {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +} + +namespace Botan { + +/** * DJB's ChaCha (https://cr.yp.to/chacha.html) */ class ChaCha final : public StreamCipher @@ -2088,6 +2379,26 @@ class EME namespace Botan { +/** +* EME from PKCS #1 v1.5 +*/ +class BOTAN_TEST_API EME_PKCS1v15 final : public EME + { + public: + size_t maximum_input_size(size_t) const override; + + secure_vector<uint8_t> pad(const uint8_t[], size_t, size_t, + RandomNumberGenerator&) const override; + + secure_vector<uint8_t> unpad(uint8_t& valid_mask, + const uint8_t in[], + size_t in_len) const override; + }; + +} + +namespace Botan { + class EME_Raw final : public EME { public: @@ -2261,6 +2572,126 @@ class EMSA1 final : public EMSA namespace Botan { /** +* PKCS #1 v1.5 signature padding +* aka PKCS #1 block type 1 +* aka EMSA3 from IEEE 1363 +*/ +class EMSA_PKCS1v15 final : public EMSA + { + public: + /** + * @param hash the hash function to use + */ + explicit EMSA_PKCS1v15(std::unique_ptr<HashFunction> hash); + + std::unique_ptr<EMSA> new_object() override + { + return std::make_unique<EMSA_PKCS1v15>(m_hash->new_object()); + } + + void update(const uint8_t[], size_t) override; + + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator& rng) override; + + bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&, + size_t) override; + + std::string name() const override + { return "EMSA3(" + m_hash->name() + ")"; } + + AlgorithmIdentifier config_for_x509(const Private_Key& key, + const std::string& cert_hash_name) const override; + + bool requires_message_recovery() const override { return true; } + private: + std::unique_ptr<HashFunction> m_hash; + std::vector<uint8_t> m_hash_id; + }; + +/** +* EMSA_PKCS1v15_Raw which is EMSA_PKCS1v15 without a hash or digest id +* (which according to QCA docs is "identical to PKCS#11's CKM_RSA_PKCS +* mechanism", something I have not confirmed) +*/ +class EMSA_PKCS1v15_Raw final : public EMSA + { + public: + std::unique_ptr<EMSA> new_object() override { return std::make_unique<EMSA_PKCS1v15_Raw>(); } + + void update(const uint8_t[], size_t) override; + + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator& rng) override; + + bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&, + size_t) override; + + EMSA_PKCS1v15_Raw(); + + /** + * @param hash_algo t he digest id for that hash is included in + * the signature. + */ + EMSA_PKCS1v15_Raw(const std::string& hash_algo); + + std::string name() const override + { + if(m_hash_name.empty()) return "EMSA3(Raw)"; + else return "EMSA3(Raw," + m_hash_name + ")"; + } + + bool requires_message_recovery() const override { return true; } + private: + size_t m_hash_output_len = 0; + std::string m_hash_name; + std::vector<uint8_t> m_hash_id; + secure_vector<uint8_t> m_message; + }; + +} + +namespace Botan { + +/** +* EMSA-Raw - sign inputs directly +* Don't use this unless you know what you are doing. +*/ +class EMSA_Raw final : public EMSA + { + public: + std::unique_ptr<EMSA> new_object() override { return std::make_unique<EMSA_Raw>(); } + + explicit EMSA_Raw(size_t expected_hash_size = 0) : + m_expected_size(expected_hash_size) {} + + std::string name() const override; + + bool requires_message_recovery() const override { return false; } + private: + void update(const uint8_t[], size_t) override; + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator&) override; + + bool verify(const secure_vector<uint8_t>&, + const secure_vector<uint8_t>&, + size_t) override; + + const size_t m_expected_size; + secure_vector<uint8_t> m_message; + }; + +} + +namespace Botan { + +/** * No_Filesystem_Access Exception */ class No_Filesystem_Access final : public Exception @@ -2278,6 +2709,62 @@ BOTAN_TEST_API std::vector<std::string> get_files_recursive(const std::string& d namespace Botan { +/** +* Return the PKCS #1 hash identifier +* @see RFC 3447 section 9.2 +* @param hash_name the name of the hash function +* @return uint8_t sequence identifying the hash +* @throw Invalid_Argument if the hash has no known PKCS #1 hash id +*/ +std::vector<uint8_t> BOTAN_TEST_API pkcs_hash_id(const std::string& hash_name); + +/** +* Return the IEEE 1363 hash identifier +* @param hash_name the name of the hash function +* @return uint8_t code identifying the hash, or 0 if not known +*/ +uint8_t ieee1363_hash_id(const std::string& hash_name); + +} + +namespace Botan { + +/** +* HMAC +*/ +class HMAC final : public MessageAuthenticationCode + { + public: + void clear() override; + std::string name() const override; + std::unique_ptr<MessageAuthenticationCode> new_object() const override; + + size_t output_length() const override; + + Key_Length_Specification key_spec() const override; + + /** + * @param hash the hash to use for HMACing + */ + explicit HMAC(std::unique_ptr<HashFunction> hash); + + HMAC(const HMAC&) = delete; + HMAC& operator=(const HMAC&) = delete; + private: + void add_data(const uint8_t[], size_t) override; + void final_result(uint8_t[]) override; + void key_schedule(const uint8_t[], size_t) override; + + std::unique_ptr<HashFunction> m_hash; + secure_vector<uint8_t> m_ikey, m_okey; + size_t m_hash_output_length; + size_t m_hash_block_size; + }; + +} + +namespace Botan { + namespace KeyPair { /** @@ -5254,6 +5741,77 @@ bool host_wildcard_match(const std::string& wildcard, } + +namespace Botan { + +class RandomNumberGenerator; + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds to run PBKDF2 +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds to run PBKDF2 +* @param out_iterations_if_nonnull if not null, set to the number +* of PBKDF iterations used +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param iterations how many iterations to run PBKDF2 +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t iterations, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Decrypt a PKCS #5 v2.0 encrypted stream +* @param key_bits the input +* @param passphrase the passphrase to use for decryption +* @param params the PBES2 parameters +*/ +secure_vector<uint8_t> +pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params); + +} /** * Ordinary applications should never need to include or use this * header. It is exposed only for specialized applications which want @@ -12340,6 +12898,604 @@ void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& } /* +* Block Ciphers +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_AES) +#endif + +#if defined(BOTAN_HAS_ARIA) +#endif + +#if defined(BOTAN_HAS_BLOWFISH) +#endif + +#if defined(BOTAN_HAS_CAMELLIA) +#endif + +#if defined(BOTAN_HAS_CAST_128) +#endif + +#if defined(BOTAN_HAS_CASCADE) +#endif + +#if defined(BOTAN_HAS_DES) +#endif + +#if defined(BOTAN_HAS_GOST_28147_89) +#endif + +#if defined(BOTAN_HAS_IDEA) +#endif + +#if defined(BOTAN_HAS_LION) +#endif + +#if defined(BOTAN_HAS_NOEKEON) +#endif + +#if defined(BOTAN_HAS_SEED) +#endif + +#if defined(BOTAN_HAS_SERPENT) +#endif + +#if defined(BOTAN_HAS_SHACAL2) +#endif + +#if defined(BOTAN_HAS_SM4) +#endif + +#if defined(BOTAN_HAS_TWOFISH) +#endif + +#if defined(BOTAN_HAS_THREEFISH_512) +#endif + +#if defined(BOTAN_HAS_COMMONCRYPTO) +#endif + +namespace Botan { + +std::unique_ptr<BlockCipher> +BlockCipher::create(const std::string& algo, + const std::string& provider) + { +#if defined(BOTAN_HAS_COMMONCRYPTO) + if(provider.empty() || provider == "commoncrypto") + { + if(auto bc = make_commoncrypto_block_cipher(algo)) + return bc; + + if(!provider.empty()) + return nullptr; + } +#endif + + // TODO: CryptoAPI + // TODO: /dev/crypto + + // Only base providers from here on out + if(provider.empty() == false && provider != "base") + return nullptr; + +#if defined(BOTAN_HAS_AES) + if(algo == "AES-128") + { + return std::make_unique<AES_128>(); + } + + if(algo == "AES-192") + { + return std::make_unique<AES_192>(); + } + + if(algo == "AES-256") + { + return std::make_unique<AES_256>(); + } +#endif + +#if defined(BOTAN_HAS_ARIA) + if(algo == "ARIA-128") + { + return std::make_unique<ARIA_128>(); + } + + if(algo == "ARIA-192") + { + return std::make_unique<ARIA_192>(); + } + + if(algo == "ARIA-256") + { + return std::make_unique<ARIA_256>(); + } +#endif + +#if defined(BOTAN_HAS_SERPENT) + if(algo == "Serpent") + { + return std::make_unique<Serpent>(); + } +#endif + +#if defined(BOTAN_HAS_SHACAL2) + if(algo == "SHACAL2") + { + return std::make_unique<SHACAL2>(); + } +#endif + +#if defined(BOTAN_HAS_TWOFISH) + if(algo == "Twofish") + { + return std::make_unique<Twofish>(); + } +#endif + +#if defined(BOTAN_HAS_THREEFISH_512) + if(algo == "Threefish-512") + { + return std::make_unique<Threefish_512>(); + } +#endif + +#if defined(BOTAN_HAS_BLOWFISH) + if(algo == "Blowfish") + { + return std::make_unique<Blowfish>(); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA) + if(algo == "Camellia-128") + { + return std::make_unique<Camellia_128>(); + } + + if(algo == "Camellia-192") + { + return std::make_unique<Camellia_192>(); + } + + if(algo == "Camellia-256") + { + return std::make_unique<Camellia_256>(); + } +#endif + +#if defined(BOTAN_HAS_DES) + if(algo == "DES") + { + return std::make_unique<DES>(); + } + + if(algo == "TripleDES" || algo == "3DES" || algo == "DES-EDE") + { + return std::make_unique<TripleDES>(); + } +#endif + +#if defined(BOTAN_HAS_NOEKEON) + if(algo == "Noekeon") + { + return std::make_unique<Noekeon>(); + } +#endif + +#if defined(BOTAN_HAS_CAST_128) + if(algo == "CAST-128" || algo == "CAST5") + { + return std::make_unique<CAST_128>(); + } +#endif + +#if defined(BOTAN_HAS_IDEA) + if(algo == "IDEA") + { + return std::make_unique<IDEA>(); + } +#endif + +#if defined(BOTAN_HAS_SEED) + if(algo == "SEED") + { + return std::make_unique<SEED>(); + } +#endif + +#if defined(BOTAN_HAS_SM4) + if(algo == "SM4") + { + return std::make_unique<SM4>(); + } +#endif + + const SCAN_Name req(algo); + +#if defined(BOTAN_HAS_GOST_28147_89) + if(req.algo_name() == "GOST-28147-89") + { + return std::make_unique<GOST_28147_89>(req.arg(0, "R3411_94_TestParam")); + } +#endif + +#if defined(BOTAN_HAS_CASCADE) + if(req.algo_name() == "Cascade" && req.arg_count() == 2) + { + std::unique_ptr<BlockCipher> c1 = BlockCipher::create(req.arg(0)); + std::unique_ptr<BlockCipher> c2 = BlockCipher::create(req.arg(1)); + + if(c1 && c2) + return std::make_unique<Cascade_Cipher>(std::move(c1), std::move(c2)); + } +#endif + +#if defined(BOTAN_HAS_LION) + if(req.algo_name() == "Lion" && req.arg_count_between(2, 3)) + { + std::unique_ptr<HashFunction> hash = HashFunction::create(req.arg(0)); + std::unique_ptr<StreamCipher> stream = StreamCipher::create(req.arg(1)); + + if(hash && stream) + { + const size_t block_size = req.arg_as_integer(2, 1024); + return std::make_unique<Lion>(std::move(hash), std::move(stream), block_size); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<BlockCipher> +BlockCipher::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto bc = BlockCipher::create(algo, provider)) + { + return bc; + } + throw Lookup_Error("Block cipher", algo, provider); + } + +std::vector<std::string> BlockCipher::providers(const std::string& algo) + { + return probe_providers_of<BlockCipher>(algo, { "base", "commoncrypto" }); + } + +} +/* +* CBC Mode +* (C) 1999-2007,2013,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +CBC_Mode::CBC_Mode(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + m_cipher(std::move(cipher)), + m_padding(std::move(padding)), + m_block_size(m_cipher->block_size()) + { + if(m_padding && !m_padding->valid_blocksize(m_block_size)) + throw Invalid_Argument("Padding " + m_padding->name() + + " cannot be used with " + + m_cipher->name() + "/CBC"); + } + +void CBC_Mode::clear() + { + m_cipher->clear(); + reset(); + } + +void CBC_Mode::reset() + { + m_state.clear(); + } + +std::string CBC_Mode::name() const + { + if(m_padding) + return cipher().name() + "/CBC/" + padding().name(); + else + return cipher().name() + "/CBC/CTS"; + } + +size_t CBC_Mode::update_granularity() const + { + return cipher().parallel_bytes(); + } + +Key_Length_Specification CBC_Mode::key_spec() const + { + return cipher().key_spec(); + } + +size_t CBC_Mode::default_nonce_length() const + { + return block_size(); + } + +bool CBC_Mode::valid_nonce_length(size_t n) const + { + return (n == 0 || n == block_size()); + } + +void CBC_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_cipher->set_key(key, length); + m_state.clear(); + } + +void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + /* + * A nonce of zero length means carry the last ciphertext value over + * as the new IV, as unfortunately some protocols require this. If + * this is the first message then we use an IV of all zeros. + */ + if(nonce_len) + m_state.assign(nonce, nonce + nonce_len); + else if(m_state.empty()) + m_state.resize(m_cipher->block_size()); + // else leave the state alone + } + +size_t CBC_Encryption::minimum_final_size() const + { + return 0; + } + +size_t CBC_Encryption::output_length(size_t input_length) const + { + if(input_length == 0) + return block_size(); + else + return round_up(input_length, block_size()); + } + +size_t CBC_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "CBC input is full blocks"); + const size_t blocks = sz / BS; + + if(blocks > 0) + { + xor_buf(&buf[0], state_ptr(), BS); + cipher().encrypt(&buf[0]); + + for(size_t i = 1; i != blocks; ++i) + { + xor_buf(&buf[BS*i], &buf[BS*(i-1)], BS); + cipher().encrypt(&buf[BS*i]); + } + + state().assign(&buf[BS*(blocks-1)], &buf[BS*blocks]); + } + + return sz; + } + +void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + + const size_t BS = block_size(); + + const size_t bytes_in_final_block = (buffer.size()-offset) % BS; + + padding().add_padding(buffer, bytes_in_final_block, BS); + + BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary"); + + update(buffer, offset); + } + +bool CTS_Encryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Encryption::minimum_final_size() const + { + return block_size() + 1; + } + +size_t CTS_Encryption::output_length(size_t input_length) const + { + return input_length; // no ciphertext expansion in CTS + } + +void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + uint8_t* buf = buffer.data() + offset; + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to encrypt"); + + if(sz % BS == 0) + { + update(buffer, offset); + + // swap last two blocks + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + xor_buf(last.data(), state_ptr(), BS); + cipher().encrypt(last.data()); + + for(size_t i = 0; i != final_bytes - BS; ++i) + { + last[i] ^= last[i + BS]; + last[i + BS] ^= last[i]; + } + + cipher().encrypt(last.data()); + + buffer += last; + } + } + +size_t CBC_Decryption::output_length(size_t input_length) const + { + return input_length; // precise for CTS, worst case otherwise + } + +size_t CBC_Decryption::minimum_final_size() const + { + return block_size(); + } + +size_t CBC_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "Input is full blocks"); + size_t blocks = sz / BS; + + while(blocks) + { + const size_t to_proc = std::min(BS * blocks, m_tempbuf.size()); + + cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS); + + xor_buf(m_tempbuf.data(), state_ptr(), BS); + xor_buf(&m_tempbuf[BS], buf, to_proc - BS); + copy_mem(state_ptr(), buf + (to_proc - BS), BS); + + copy_mem(buf, m_tempbuf.data(), to_proc); + + buf += to_proc; + blocks -= to_proc / BS; + } + + return sz; + } + +void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz == 0 || sz % BS) + throw Decoding_Error(name() + ": Ciphertext not a multiple of block size"); + + update(buffer, offset); + + const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.size()-BS], BS); + buffer.resize(buffer.size() - pad_bytes); // remove padding + if(pad_bytes == 0 && padding().name() != "NoPadding") + { + throw Decoding_Error("Invalid CBC padding"); + } + } + +void CBC_Decryption::reset() + { + CBC_Mode::reset(); + zeroise(m_tempbuf); + } + +bool CTS_Decryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Decryption::minimum_final_size() const + { + return block_size() + 1; + } + +void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to decrypt"); + + if(sz % BS == 0) + { + // swap last two blocks + + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + + update(buffer, offset); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + cipher().decrypt(last.data()); + + xor_buf(last.data(), &last[BS], final_bytes - BS); + + for(size_t i = 0; i != final_bytes - BS; ++i) + std::swap(last[i], last[i + BS]); + + cipher().decrypt(last.data()); + xor_buf(last.data(), state_ptr(), BS); + + buffer += last; + } + } + +} +/* * ChaCha * (C) 2014,2018 Jack Lloyd * @@ -14000,6 +15156,111 @@ OAEP::OAEP(std::unique_ptr<HashFunction> hash, } /* +* PKCS #1 v1.5 Type 2 (encryption) padding +* (C) 1999-2007,2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/* +* PKCS1 Pad Operation +*/ +secure_vector<uint8_t> EME_PKCS1v15::pad(const uint8_t in[], size_t inlen, + size_t key_length, + RandomNumberGenerator& rng) const + { + key_length /= 8; + + if(inlen > maximum_input_size(key_length * 8)) + { + throw Invalid_Argument("PKCS1: Input is too large"); + } + + secure_vector<uint8_t> out(key_length); + + out[0] = 0x02; + rng.randomize(out.data() + 1, (key_length - inlen - 2)); + + for(size_t j = 1; j != key_length - inlen - 1; ++j) + { + if(out[j] == 0) + { + out[j] = rng.next_nonzero_byte(); + } + } + + buffer_insert(out, key_length - inlen, in, inlen); + + return out; + } + +/* +* PKCS1 Unpad Operation +*/ +secure_vector<uint8_t> EME_PKCS1v15::unpad(uint8_t& valid_mask, + const uint8_t in[], size_t inlen) const + { + /* + * RSA decryption pads the ciphertext up to the modulus size, so this only + * occurs with very (!) small keys, or when fuzzing. + * + * 11 bytes == 00,02 + 8 bytes mandatory padding + 00 + */ + if(inlen < 11) + { + valid_mask = false; + return secure_vector<uint8_t>(); + } + + CT::poison(in, inlen); + + CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared(); + CT::Mask<uint8_t> seen_zero_m = CT::Mask<uint8_t>::cleared(); + size_t delim_idx = 2; // initial 0002 + + bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[0], 0); + bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[1], 2); + + for(size_t i = 2; i < inlen; ++i) + { + const auto is_zero_m = CT::Mask<uint8_t>::is_zero(in[i]); + delim_idx += seen_zero_m.if_not_set_return(1); + seen_zero_m |= is_zero_m; + } + + // no zero delim -> bad padding + bad_input_m |= ~seen_zero_m; + /* + delim indicates < 8 bytes padding -> bad padding + + We require 11 here because we are counting also the 00 delim byte + */ + bad_input_m |= CT::Mask<uint8_t>(CT::Mask<size_t>::is_lt(delim_idx, 11)); + + valid_mask = (~bad_input_m).unpoisoned_value(); + auto output = CT::copy_output(bad_input_m, in, inlen, delim_idx); + + CT::unpoison(in, inlen); + + return output; + } + +/* +* Return the max input size for a given key size +*/ +size_t EME_PKCS1v15::maximum_input_size(size_t keybits) const + { + if(keybits / 8 > 10) + return ((keybits / 8) - 10); + else + return 0; + } + +} +/* * (C) 2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) @@ -14159,6 +15420,167 @@ AlgorithmIdentifier EMSA1::config_for_x509(const Private_Key& key, } /* +* PKCS #1 v1.5 signature padding +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg, + size_t output_bits, + const uint8_t hash_id[], + size_t hash_id_length) + { + size_t output_length = output_bits / 8; + if(output_length < hash_id_length + msg.size() + 10) + throw Encoding_Error("emsa3_encoding: Output length is too small"); + + secure_vector<uint8_t> T(output_length); + const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2; + + T[0] = 0x01; + set_mem(&T[1], P_LENGTH, 0xFF); + T[P_LENGTH+1] = 0x00; + + if(hash_id_length > 0) + { + BOTAN_ASSERT_NONNULL(hash_id); + buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length); + } + + buffer_insert(T, output_length-msg.size(), msg.data(), msg.size()); + return T; + } + +} + +void EMSA_PKCS1v15::update(const uint8_t input[], size_t length) + { + m_hash->update(input, length); + } + +secure_vector<uint8_t> EMSA_PKCS1v15::raw_data() + { + return m_hash->final(); + } + +secure_vector<uint8_t> +EMSA_PKCS1v15::encoding_of(const secure_vector<uint8_t>& msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) + { + if(msg.size() != m_hash->output_length()) + throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length"); + + return emsa3_encoding(msg, output_bits, + m_hash_id.data(), m_hash_id.size()); + } + +bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t key_bits) + { + if(raw.size() != m_hash->output_length()) + return false; + + try + { + return (coded == emsa3_encoding(raw, key_bits, + m_hash_id.data(), m_hash_id.size())); + } + catch(...) + { + return false; + } + } + +AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key, + const std::string& cert_hash_name) const + { + if(cert_hash_name != m_hash->name()) + throw Invalid_Argument("Hash function from opts and hash_fn argument" + " need to be identical"); + // check that the signature algorithm and the padding scheme fit + if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3")) + { + throw Invalid_Argument("Encoding scheme with canonical name EMSA3" + " not supported for signature algorithm " + key.algo_name()); + } + + // for RSA PKCSv1.5 parameters "SHALL" be NULL + + const OID oid = OID::from_string(key.algo_name() + "/" + name()); + return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_NULL_PARAM); + } + +EMSA_PKCS1v15::EMSA_PKCS1v15(std::unique_ptr<HashFunction> hash) : + m_hash(std::move(hash)) + { + m_hash_id = pkcs_hash_id(m_hash->name()); + } + +EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw() + { + m_hash_output_len = 0; + // m_hash_id, m_hash_name left empty + } + +EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo) + { + std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo)); + m_hash_id = pkcs_hash_id(hash_algo); + m_hash_name = hash->name(); + m_hash_output_len = hash->output_length(); + } + +void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length) + { + m_message += std::make_pair(input, length); + } + +secure_vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data() + { + secure_vector<uint8_t> ret; + std::swap(ret, m_message); + + if(m_hash_output_len > 0 && ret.size() != m_hash_output_len) + throw Encoding_Error("EMSA_PKCS1v15_Raw::encoding_of: Bad input length"); + + return ret; + } + +secure_vector<uint8_t> +EMSA_PKCS1v15_Raw::encoding_of(const secure_vector<uint8_t>& msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) + { + return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size()); + } + +bool EMSA_PKCS1v15_Raw::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t key_bits) + { + if(m_hash_output_len > 0 && raw.size() != m_hash_output_len) + return false; + + try + { + return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size())); + } + catch(...) + { + return false; + } + } + +} +/* * PSSR * (C) 1999-2007,2017 Jack Lloyd * @@ -14442,6 +15864,96 @@ std::string PSSR_Raw::name() const } /* +* EMSA-Raw +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +std::string EMSA_Raw::name() const + { + if(m_expected_size > 0) + return "Raw(" + std::to_string(m_expected_size) + ")"; + return "Raw"; + } + +/* +* EMSA-Raw Encode Operation +*/ +void EMSA_Raw::update(const uint8_t input[], size_t length) + { + m_message += std::make_pair(input, length); + } + +/* +* Return the raw (unencoded) data +*/ +secure_vector<uint8_t> EMSA_Raw::raw_data() + { + if(m_expected_size && m_message.size() != m_expected_size) + throw Invalid_Argument("EMSA_Raw was configured to use a " + + std::to_string(m_expected_size) + + " byte hash but instead was used for a " + + std::to_string(m_message.size()) + " hash"); + + secure_vector<uint8_t> output; + std::swap(m_message, output); + return output; + } + +/* +* EMSA-Raw Encode Operation +*/ +secure_vector<uint8_t> +EMSA_Raw::encoding_of(const secure_vector<uint8_t>& msg, + size_t /*output_bits*/, + RandomNumberGenerator& /*rng*/) + { + if(m_expected_size && msg.size() != m_expected_size) + throw Invalid_Argument("EMSA_Raw was configured to use a " + + std::to_string(m_expected_size) + + " byte hash but instead was used for a " + + std::to_string(msg.size()) + " hash"); + + return msg; + } + +/* +* EMSA-Raw Verify Operation +*/ +bool EMSA_Raw::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t /*key_bits*/) + { + if(m_expected_size && raw.size() != m_expected_size) + return false; + + if(coded.size() == raw.size()) + return (coded == raw); + + if(coded.size() > raw.size()) + return false; + + // handle zero padding differences + const size_t leading_zeros_expected = raw.size() - coded.size(); + + bool same_modulo_leading_zeros = true; + + for(size_t i = 0; i != leading_zeros_expected; ++i) + if(raw[i]) + same_modulo_leading_zeros = false; + + if(!constant_time_compare(coded.data(), raw.data() + leading_zeros_expected, coded.size())) + same_modulo_leading_zeros = false; + + return same_modulo_leading_zeros; + } + +} +/* * Entropy Source Polling * (C) 2008-2010,2015 Jack Lloyd * @@ -14940,6 +16452,159 @@ std::vector<std::string> HashFunction::providers(const std::string& algo_spec) } /* +* Hash Function Identification +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +const uint8_t MD5_PKCS_ID[] = { +0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, +0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; + +const uint8_t RIPEMD_160_PKCS_ID[] = { +0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, +0x01, 0x05, 0x00, 0x04, 0x14 }; + +const uint8_t SHA_160_PKCS_ID[] = { +0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, +0x1A, 0x05, 0x00, 0x04, 0x14 }; + +const uint8_t SHA_224_PKCS_ID[] = { +0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C }; + +const uint8_t SHA_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA_384_PKCS_ID[] = { +0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; + +const uint8_t SHA_512_PKCS_ID[] = { +0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; + +const uint8_t SHA_512_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA3_224_PKCS_ID[] = { +0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x07, 0x05, 0x00, 0x04, 0x1C }; + +const uint8_t SHA3_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA3_384_PKCS_ID[] = { +0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x09, 0x05, 0x00, 0x04, 0x30 }; + +const uint8_t SHA3_512_PKCS_ID[] = { +0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x0A, 0x05, 0x00, 0x04, 0x40 }; + +const uint8_t SM3_PKCS_ID[] = { +0x30, 0x30, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x81, 0x1C, 0xCF, +0x55, 0x01, 0x83, 0x11, 0x05, 0x00, 0x04, 0x20, +}; + +} + +/* +* HashID as specified by PKCS +*/ +std::vector<uint8_t> pkcs_hash_id(const std::string& name) + { + // Special case for SSL/TLS RSA signatures + if(name == "Parallel(MD5,SHA-160)") + return std::vector<uint8_t>(); + + // If you add a value to this function, also update test_hash_id.cpp + + if(name == "MD5") + return std::vector<uint8_t>(MD5_PKCS_ID, + MD5_PKCS_ID + sizeof(MD5_PKCS_ID)); + + if(name == "RIPEMD-160") + return std::vector<uint8_t>(RIPEMD_160_PKCS_ID, + RIPEMD_160_PKCS_ID + sizeof(RIPEMD_160_PKCS_ID)); + + if(name == "SHA-160" || name == "SHA-1" || name == "SHA1") + return std::vector<uint8_t>(SHA_160_PKCS_ID, + SHA_160_PKCS_ID + sizeof(SHA_160_PKCS_ID)); + + if(name == "SHA-224") + return std::vector<uint8_t>(SHA_224_PKCS_ID, + SHA_224_PKCS_ID + sizeof(SHA_224_PKCS_ID)); + + if(name == "SHA-256") + return std::vector<uint8_t>(SHA_256_PKCS_ID, + SHA_256_PKCS_ID + sizeof(SHA_256_PKCS_ID)); + + if(name == "SHA-384") + return std::vector<uint8_t>(SHA_384_PKCS_ID, + SHA_384_PKCS_ID + sizeof(SHA_384_PKCS_ID)); + + if(name == "SHA-512") + return std::vector<uint8_t>(SHA_512_PKCS_ID, + SHA_512_PKCS_ID + sizeof(SHA_512_PKCS_ID)); + + if(name == "SHA-512-256") + return std::vector<uint8_t>(SHA_512_256_PKCS_ID, + SHA_512_256_PKCS_ID + sizeof(SHA_512_256_PKCS_ID)); + + if(name == "SHA-3(224)") + return std::vector<uint8_t>(SHA3_224_PKCS_ID, + SHA3_224_PKCS_ID + sizeof(SHA3_224_PKCS_ID)); + + if(name == "SHA-3(256)") + return std::vector<uint8_t>(SHA3_256_PKCS_ID, + SHA3_256_PKCS_ID + sizeof(SHA3_256_PKCS_ID)); + + if(name == "SHA-3(384)") + return std::vector<uint8_t>(SHA3_384_PKCS_ID, + SHA3_384_PKCS_ID + sizeof(SHA3_384_PKCS_ID)); + + if(name == "SHA-3(512)") + return std::vector<uint8_t>(SHA3_512_PKCS_ID, + SHA3_512_PKCS_ID + sizeof(SHA3_512_PKCS_ID)); + + if(name == "SM3") + return std::vector<uint8_t>(SM3_PKCS_ID, SM3_PKCS_ID + sizeof(SM3_PKCS_ID)); + + throw Invalid_Argument("No PKCS #1 identifier for " + name); + } + +/* +* HashID as specified by IEEE 1363/X9.31 +*/ +uint8_t ieee1363_hash_id(const std::string& name) + { + if(name == "SHA-160" || name == "SHA-1" || name == "SHA1") + return 0x33; + + if(name == "SHA-224") return 0x38; + if(name == "SHA-256") return 0x34; + if(name == "SHA-384") return 0x36; + if(name == "SHA-512") return 0x35; + + if(name == "RIPEMD-160") return 0x31; + + if(name == "Whirlpool") return 0x37; + + return 0; + } + +} +/* * Hex Encoding and Decoding * (C) 2010,2020 Jack Lloyd * @@ -15146,6 +16811,154 @@ std::vector<uint8_t> hex_decode(const std::string& input, } /* +* HMAC +* (C) 1999-2007,2014,2020 Jack Lloyd +* 2007 Yves Jerschow +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/* +* Update a HMAC Calculation +*/ +void HMAC::add_data(const uint8_t input[], size_t length) + { + verify_key_set(m_ikey.empty() == false); + m_hash->update(input, length); + } + +/* +* Finalize a HMAC Calculation +*/ +void HMAC::final_result(uint8_t mac[]) + { + verify_key_set(m_okey.empty() == false); + m_hash->final(mac); + m_hash->update(m_okey); + m_hash->update(mac, m_hash_output_length); + m_hash->final(mac); + m_hash->update(m_ikey); + } + +Key_Length_Specification HMAC::key_spec() const + { + // Support very long lengths for things like PBKDF2 and the TLS PRF + return Key_Length_Specification(0, 4096); + } + +size_t HMAC::output_length() const + { + return m_hash_output_length; + } + +/* +* HMAC Key Schedule +*/ +void HMAC::key_schedule(const uint8_t key[], size_t length) + { + const uint8_t ipad = 0x36; + const uint8_t opad = 0x5C; + + m_hash->clear(); + + m_ikey.resize(m_hash_block_size); + m_okey.resize(m_hash_block_size); + + clear_mem(m_ikey.data(), m_ikey.size()); + clear_mem(m_okey.data(), m_okey.size()); + + /* + * Sometimes the HMAC key length itself is sensitive, as with PBKDF2 where it + * reveals the length of the passphrase. Make some attempt to hide this to + * side channels. Clearly if the secret is longer than the block size then the + * branch to hash first reveals that. In addition, counting the number of + * compression functions executed reveals the size at the granularity of the + * hash function's block size. + * + * The greater concern is for smaller keys; being able to detect when a + * passphrase is say 4 bytes may assist choosing weaker targets. Even though + * the loop bounds are constant, we can only actually read key[0..length] so + * it doesn't seem possible to make this computation truly constant time. + * + * We don't mind leaking if the length is exactly zero since that's + * trivial to simply check. + */ + + if(length > m_hash_block_size) + { + m_hash->update(key, length); + m_hash->final(m_ikey.data()); + } + else if(length > 0) + { + for(size_t i = 0, i_mod_length = 0; i != m_hash_block_size; ++i) + { + /* + access key[i % length] but avoiding division due to variable + time computation on some processors. + */ + auto needs_reduction = CT::Mask<size_t>::is_lte(length, i_mod_length); + i_mod_length = needs_reduction.select(0, i_mod_length); + const uint8_t kb = key[i_mod_length]; + + auto in_range = CT::Mask<size_t>::is_lt(i, length); + m_ikey[i] = static_cast<uint8_t>(in_range.if_set_return(kb)); + i_mod_length += 1; + } + } + + for(size_t i = 0; i != m_hash_block_size; ++i) + { + m_ikey[i] ^= ipad; + m_okey[i] = m_ikey[i] ^ ipad ^ opad; + } + + m_hash->update(m_ikey); + } + +/* +* Clear memory of sensitive data +*/ +void HMAC::clear() + { + m_hash->clear(); + zap(m_ikey); + zap(m_okey); + } + +/* +* Return the name of this type +*/ +std::string HMAC::name() const + { + return "HMAC(" + m_hash->name() + ")"; + } + +/* +* Return a new_object of this object +*/ +std::unique_ptr<MessageAuthenticationCode> HMAC::new_object() const + { + return std::make_unique<HMAC>(m_hash->new_object()); + } + +/* +* HMAC Constructor +*/ +HMAC::HMAC(std::unique_ptr<HashFunction> hash) : + m_hash(std::move(hash)), + m_hash_output_length(m_hash->output_length()), + m_hash_block_size(m_hash->hash_block_size()) + { + BOTAN_ARG_CHECK(m_hash_block_size >= m_hash_output_length, + "HMAC is not compatible with this hash function"); + } + +} +/* * KDF Retrieval * (C) 1999-2007 Jack Lloyd * @@ -15730,6 +17543,337 @@ void mgf1_mask(HashFunction& hash, } /* +* CBC Padding Methods +* (C) 1999-2007,2013,2018,2020 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/** +* Get a block cipher padding method by name +*/ +std::unique_ptr<BlockCipherModePaddingMethod> +BlockCipherModePaddingMethod::create(const std::string& algo_spec) + { + if(algo_spec == "NoPadding") + return std::make_unique<Null_Padding>(); + + if(algo_spec == "PKCS7") + return std::make_unique<PKCS7_Padding>(); + + if(algo_spec == "OneAndZeros") + return std::make_unique<OneAndZeros_Padding>(); + + if(algo_spec == "X9.23") + return std::make_unique<ANSI_X923_Padding>(); + + if(algo_spec == "ESP") + return std::make_unique<ESP_Padding>(); + + return nullptr; + } + +/* +* Pad with PKCS #7 Method +*/ +void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0202 + 030303 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(padding_len, buffer[i]); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with PKCS #7 Method +*/ +size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t last_byte = input[input_length-1]; + + /* + The input should == the block size so if the last byte exceeds + that then the padding is certainly invalid + */ + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Does this byte equal the expected pad byte? + const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte); + + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + bad_input |= in_range & (~pad_eq); + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ANSI X9.23 Method +*/ +void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0002 + 000003 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_zero_padding = buffer.size() - 1; + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(0, buffer[i]); + } + + buffer[buffer.size()-1] = padding_len; + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ANSI X9.23 Method +*/ +size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const size_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + const auto pad_is_nonzero = CT::Mask<size_t>::expand(input[i]); + bad_input |= pad_is_nonzero & in_range; + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with One and Zeros Method +*/ +void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 80 + 8000 + 800000 + ... + */ + + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_80 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding)); + auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding)); + buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i])); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with One and Zeros Method +*/ +size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + auto bad_input = CT::Mask<uint8_t>::cleared(); + auto seen_0x80 = CT::Mask<uint8_t>::cleared(); + + size_t pad_pos = input_length - 1; + size_t i = input_length; + + while(i) + { + const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i-1], 0x80); + const auto is_zero = CT::Mask<uint8_t>::is_zero(input[i-1]); + + seen_0x80 |= is_0x80; + pad_pos -= seen_0x80.if_not_set_return(1); + bad_input |= ~seen_0x80 & ~is_zero; + i--; + } + bad_input |= ~seen_0x80; + + CT::unpoison(input, input_length); + + return CT::Mask<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ESP Padding Method +*/ +void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0102 + 010203 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + uint8_t pad_ctr = 0x01; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(pad_ctr, buffer[i]); + pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ESP Padding Method +*/ +size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t input_length_8 = static_cast<uint8_t>(input_length); + const uint8_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) | + CT::Mask<uint8_t>::is_gt(last_byte, input_length_8); + + const uint8_t pad_pos = input_length_8 - last_byte; + size_t i = input_length_8 - 1; + while(i) + { + const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos); + const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i-1], input[i]-1); + + bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing; + --i; + } + + CT::unpoison(input, input_length); + return bad_input.select_and_unpoison(input_length_8, pad_pos); + } + + +} +/* * Cipher Modes * (C) 2015 Jack Lloyd * @@ -24566,6 +26710,797 @@ void Modular_Reducer::reduce(BigInt& t1, const BigInt& x, secure_vector<word>& w } /* +* PKCS #5 PBES2 +* (C) 1999-2008,2014,2021 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +bool known_pbes_cipher_mode(const std::string& mode) + { + return (mode == "CBC" || mode == "GCM" || mode == "SIV"); + } + +secure_vector<uint8_t> derive_key(const std::string& passphrase, + const AlgorithmIdentifier& kdf_algo, + size_t default_key_size) + { + if(kdf_algo.get_oid() == OID::from_string("PKCS5.PBKDF2")) + { + secure_vector<uint8_t> salt; + size_t iterations = 0, key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_parameters()) + .start_sequence() + .decode(salt, ASN1_Type::OctetString) + .decode(iterations) + .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal) + .decode_optional(prf_algo, ASN1_Type::Sequence, ASN1_Class::Constructed, + AlgorithmIdentifier("HMAC(SHA-160)", + AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons(); + + if(salt.size() < 8) + throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); + + if(key_length == 0) + key_length = default_key_size; + + const std::string prf = OIDS::oid2str_or_throw(prf_algo.get_oid()); + auto pbkdf_fam = PasswordHashFamily::create_or_throw("PBKDF2(" + prf + ")"); + auto pbkdf = pbkdf_fam->from_params(iterations); + + secure_vector<uint8_t> derived_key(key_length); + pbkdf->derive_key(derived_key.data(), derived_key.size(), + passphrase.data(), passphrase.size(), + salt.data(), salt.size()); + return derived_key; + } + else if(kdf_algo.get_oid() == OID::from_string("Scrypt")) + { + secure_vector<uint8_t> salt; + size_t N = 0, r = 0, p = 0; + size_t key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_parameters()) + .start_sequence() + .decode(salt, ASN1_Type::OctetString) + .decode(N) + .decode(r) + .decode(p) + .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal) + .end_cons(); + + if(key_length == 0) + key_length = default_key_size; + + secure_vector<uint8_t> derived_key(key_length); + + auto pwdhash_fam = PasswordHashFamily::create_or_throw("Scrypt"); + auto pwdhash = pwdhash_fam->from_params(N, r, p); + pwdhash->derive_key(derived_key.data(), derived_key.size(), + passphrase.data(), passphrase.size(), + salt.data(), salt.size()); + + return derived_key; + } + else + throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " + + kdf_algo.get_oid().to_string()); + } + +secure_vector<uint8_t> derive_key(const std::string& passphrase, + const std::string& digest, + RandomNumberGenerator& rng, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + size_t key_length, + AlgorithmIdentifier& kdf_algo) + { + const secure_vector<uint8_t> salt = rng.random_vec(12); + + if(digest == "Scrypt") + { + auto pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt"); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + const size_t N = pwhash->memory_param(); + const size_t r = pwhash->iterations(); + const size_t p = pwhash->parallelism(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = 0; + + std::vector<uint8_t> scrypt_params; + DER_Encoder(scrypt_params) + .start_sequence() + .encode(salt, ASN1_Type::OctetString) + .encode(N) + .encode(r) + .encode(p) + .encode(key_length) + .end_cons(); + + kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params); + return key; + } + else + { + const std::string prf = "HMAC(" + digest + ")"; + const std::string pbkdf_name = "PBKDF2(" + prf + ")"; + + std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create(pbkdf_name); + if(!pwhash_fam) + throw Invalid_Argument("Unknown password hash digest " + digest); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + std::vector<uint8_t> pbkdf2_params; + + const size_t iterations = pwhash->iterations(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = iterations; + + DER_Encoder(pbkdf2_params) + .start_sequence() + .encode(salt, ASN1_Type::OctetString) + .encode(iterations) + .encode(key_length) + .encode_if(prf != "HMAC(SHA-160)", + AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons(); + + kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params); + return key; + } + } + +/* +* PKCS#5 v2.0 PBE Encryption +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_shared(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + const std::string& cipher, + const std::string& prf, + RandomNumberGenerator& rng) + { + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Encoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + + if(!known_pbes_cipher_mode(cipher_spec[1])) + throw Encoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + const OID cipher_oid = OIDS::str2oid_or_empty(cipher); + if(cipher_oid.empty()) + throw Encoding_Error("PBE-PKCS5 v2.0: No OID assigned for " + cipher); + + std::unique_ptr<Cipher_Mode> enc = Cipher_Mode::create(cipher, ENCRYPTION); + + if(!enc) + throw Decoding_Error("PBE-PKCS5 cannot encrypt no cipher " + cipher); + + const size_t key_length = enc->key_spec().maximum_keylength(); + + const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length()); + + AlgorithmIdentifier kdf_algo; + + const secure_vector<uint8_t> derived_key = + derive_key(passphrase, prf, rng, + msec_in_iterations_out, iterations_if_msec_null, + key_length, kdf_algo); + + enc->set_key(derived_key); + enc->start(iv); + secure_vector<uint8_t> ctext = key_bits; + enc->finish(ctext); + + std::vector<uint8_t> encoded_iv; + DER_Encoder(encoded_iv).encode(iv, ASN1_Type::OctetString); + + std::vector<uint8_t> pbes2_params; + DER_Encoder(pbes2_params) + .start_sequence() + .encode(kdf_algo) + .encode(AlgorithmIdentifier(cipher, encoded_iv)) + .end_cons(); + + AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params); + + return std::make_pair(id, unlock(ctext)); + } + +} + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + // return value msec_in_iterations_out discarded + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + + auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + + if(out_iterations_if_nonnull) + *out_iterations_if_nonnull = msec_in_iterations_out; + + return ret; + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t pbkdf_iter, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng); + } + +secure_vector<uint8_t> +pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params) + { + AlgorithmIdentifier kdf_algo, enc_algo; + + BER_Decoder(params) + .start_sequence() + .decode(kdf_algo) + .decode(enc_algo) + .end_cons(); + + const std::string cipher = OIDS::oid2str_or_throw(enc_algo.get_oid()); + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + if(!known_pbes_cipher_mode(cipher_spec[1])) + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + secure_vector<uint8_t> iv; + BER_Decoder(enc_algo.get_parameters()).decode(iv, ASN1_Type::OctetString).verify_end(); + + std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, DECRYPTION); + if(!dec) + throw Decoding_Error("PBE-PKCS5 cannot decrypt no cipher " + cipher); + + dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength())); + + dec->start(iv); + + secure_vector<uint8_t> buf = key_bits; + dec->finish(buf); + + return buf; + } + +} +/* +* PBKDF +* (C) 2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_PBKDF2) +#endif + +#if defined(BOTAN_HAS_PGP_S2K) +#endif + +namespace Botan { + +std::unique_ptr<PBKDF> PBKDF::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + // TODO OpenSSL + + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::make_unique<PKCS5_PBKDF2>(mac.release()); + + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::make_unique<PKCS5_PBKDF2>(mac.release()); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + return std::make_unique<OpenPGP_S2K>(hash.release()); + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PBKDF> +PBKDF::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PBKDF::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PBKDF", algo, provider); + } + +std::vector<std::string> PBKDF::providers(const std::string& algo_spec) + { + return probe_providers_of<PBKDF>(algo_spec); + } + +void PBKDF::pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + iterations = pbkdf(out, out_len, passphrase, salt, salt_len, 0, msec); + } + +void PBKDF::pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + if(iterations == 0) + throw Invalid_Argument(name() + ": Invalid iteration count"); + + const size_t iterations_run = pbkdf(out, out_len, passphrase, + salt, salt_len, iterations, + std::chrono::milliseconds(0)); + BOTAN_ASSERT_EQUAL(iterations, iterations_run, "Expected PBKDF iterations"); + } + +secure_vector<uint8_t> PBKDF::pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_iterations(out.data(), out_len, passphrase, salt, salt_len, iterations); + return out; + } + +secure_vector<uint8_t> PBKDF::pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_timed(out.data(), out_len, passphrase, salt, salt_len, msec, iterations); + return out; + } + +} +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_PBKDF2) +#endif + +#if defined(BOTAN_HAS_PGP_S2K) +#endif + +#if defined(BOTAN_HAS_SCRYPT) +#endif + +#if defined(BOTAN_HAS_ARGON2) +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) +#endif + +namespace Botan { + +void PasswordHash::derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t ad[], size_t ad_len, + const uint8_t key[], size_t key_len) const + { + BOTAN_UNUSED(ad, key); + + if(ad_len == 0 && key_len == 0) + return this->derive_key(out, out_len, + password, password_len, + salt, salt_len); + else + throw Not_Implemented("PasswordHash " + this->to_string() + " does not support AD or key"); + } + +std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::make_unique<PBKDF2_Family>(mac.release()); + + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::make_unique<PBKDF2_Family>(mac.release()); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_SCRYPT) + if(req.algo_name() == "Scrypt") + { + return std::make_unique<Scrypt_Family>(); + } +#endif + +#if defined(BOTAN_HAS_ARGON2) + if(req.algo_name() == "Argon2d") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(0)); + } + else if(req.algo_name() == "Argon2i") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(1)); + } + else if(req.algo_name() == "Argon2id") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(2)); + } +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + if(req.algo_name() == "Bcrypt-PBKDF") + { + return std::make_unique<Bcrypt_PBKDF_Family>(); + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + { + return std::make_unique<RFC4880_S2K_Family>(hash.release()); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PasswordHashFamily> +PasswordHashFamily::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PasswordHashFamily::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PasswordHashFamily", algo, provider); + } + +std::vector<std::string> PasswordHashFamily::providers(const std::string& algo_spec) + { + return probe_providers_of<PasswordHashFamily>(algo_spec); + } + +} +/* +* PBKDF2 +* (C) 1999-2007 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +void pbkdf2_set_key(MessageAuthenticationCode& prf, + const char* password, + size_t password_len) + { + try + { + prf.set_key(cast_char_ptr_to_uint8(password), password_len); + } + catch(Invalid_Key_Length&) + { + throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size"); + } + } + +} + +size_t +pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) + { + if(iterations == 0) + { + iterations = PBKDF2(prf, out_len, msec).iterations(); + } + + PBKDF2 pbkdf2(prf, iterations); + + pbkdf2.derive_key(out, out_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +namespace { + +size_t tune_pbkdf2(MessageAuthenticationCode& prf, + size_t output_length, + uint32_t msec) + { + if(output_length == 0) + output_length = 1; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + secure_vector<uint8_t> U(prf_sz); + + const size_t trial_iterations = 2000; + + // Short output ensures we only need a single PBKDF2 block + + Timer timer("PBKDF2"); + + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + prf.set_key(nullptr, 0); + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t out[12] = { 0 }; + uint8_t salt[12] = { 0 }; + pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations); + }); + + if(timer.events() == 0) + return trial_iterations; + + const uint64_t duration_nsec = timer.value() / timer.events(); + + const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000; + + if(duration_nsec > desired_nsec) + return trial_iterations; + + const size_t blocks_needed = (output_length + prf_sz - 1) / prf_sz; + + const size_t multiplier = static_cast<size_t>(desired_nsec / duration_nsec / blocks_needed); + + if(multiplier == 0) + return trial_iterations; + else + return trial_iterations * multiplier; + } + +} + +void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const uint8_t salt[], + size_t salt_len, + size_t iterations) + { + if(iterations == 0) + throw Invalid_Argument("PBKDF2: Invalid iteration count"); + + clear_mem(out, out_len); + + if(out_len == 0) + return; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + + secure_vector<uint8_t> U(prf_sz); + + uint32_t counter = 1; + while(out_len) + { + const size_t prf_output = std::min<size_t>(prf_sz, out_len); + + prf.update(salt, salt_len); + prf.update_be(counter++); + prf.final(U.data()); + + xor_buf(out, U.data(), prf_output); + + for(size_t i = 1; i != iterations; ++i) + { + prf.update(U); + prf.final(U.data()); + xor_buf(out, U.data(), prf_output); + } + + out_len -= prf_output; + out += prf_output; + } + } + +// PBKDF interface +size_t +PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const + { + if(iterations == 0) + { + iterations = PBKDF2(*m_mac, key_len, msec).iterations(); + } + + PBKDF2 pbkdf2(*m_mac, iterations); + + pbkdf2.derive_key(key, key_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +std::string PKCS5_PBKDF2::name() const + { + return "PBKDF2(" + m_mac->name() + ")"; + } + +std::unique_ptr<PBKDF> PKCS5_PBKDF2::new_object() const + { + return std::make_unique<PKCS5_PBKDF2>(m_mac->clone()); + } + +// PasswordHash interface + +PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) : + m_prf(prf.new_object()), + m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count()))) + {} + +std::string PBKDF2::to_string() const + { + return "PBKDF2(" + m_prf->name() + "," + std::to_string(m_iterations) + ")"; + } + +void PBKDF2::derive_key(uint8_t out[], size_t out_len, + const char* password, const size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + pbkdf2_set_key(*m_prf, password, password_len); + pbkdf2(*m_prf, out, out_len, salt, salt_len, m_iterations); + } + +std::string PBKDF2_Family::name() const + { + return "PBKDF2(" + m_prf->name() + ")"; + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t /*max_memory_usage_mb*/) const + { + return std::make_unique<PBKDF2>(*m_prf, output_len, msec); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const + { + return std::make_unique<PBKDF2>(*m_prf, 150000); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_params(size_t iter, size_t /*i2*/, size_t /*i3*/) const + { + return std::make_unique<PBKDF2>(*m_prf, iter); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_iterations(size_t iter) const + { + return std::make_unique<PBKDF2>(*m_prf, iter); + } + +} +/* * PEM Encoding/Decoding * (C) 1999-2007 Jack Lloyd * diff --git a/include/amalgamation-arm64/botan_all.h b/include/amalgamation-arm64/botan_all.h index a6a22b6..06d7d8c 100644 --- a/include/amalgamation-arm64/botan_all.h +++ b/include/amalgamation-arm64/botan_all.h @@ -31,7 +31,7 @@ * Build configuration for Botan 3.0.0-alpha0 * * Automatically generated from -* 'configure.py --cpu=aarch64 --prefix=/usr/local/projects/zafena/elevator/botan/dist-arm64-min --minimized-build --enable-modules=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,chacha,chacha20poly1305,aead,stream,sha1,sha2_32,system_rng,sha1_armv8,sha2_32_armv8,simd,chacha_simd32,chacha_avx2,simd_avx2 --cxxflags= --ldflags= --amalgamation --with-doxygen' +* 'configure.py --cpu=aarch64 --prefix=/usr/local/projects/zafena/cipherpack/botan/dist-arm64-min --minimized-build --enable-modules=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,emsa_raw,pbes2,eme_pkcs1,emsa_pkcs1,chacha,chacha20poly1305,aead,stream,sha1,sha2_32,system_rng,sha1_armv8,sha2_32_armv8,simd,chacha_simd32,chacha_avx2,simd_avx2 --cxxflags= --ldflags= --amalgamation --with-doxygen' * * Target * - Compiler: g++ -fstack-protector -pthread -std=c++17 -D_REENTRANT -O3 @@ -57,9 +57,9 @@ #define BOTAN_MP_WORD_BITS 64 -#define BOTAN_INSTALL_PREFIX R"(/usr/local/projects/zafena/elevator/botan/dist-arm64-min)" +#define BOTAN_INSTALL_PREFIX R"(/usr/local/projects/zafena/cipherpack/botan/dist-arm64-min)" #define BOTAN_INSTALL_HEADER_DIR R"(include/botan-3)" -#define BOTAN_INSTALL_LIB_DIR R"(/usr/local/projects/zafena/elevator/botan/dist-arm64-min/lib)" +#define BOTAN_INSTALL_LIB_DIR R"(/usr/local/projects/zafena/cipherpack/botan/dist-arm64-min/lib)" #define BOTAN_LIB_LINK "" #define BOTAN_LINK_FLAGS "-fstack-protector -pthread" @@ -117,25 +117,38 @@ #define BOTAN_HAS_BASE64_CODEC 20131128 #define BOTAN_HAS_BIGINT 20210423 #define BOTAN_HAS_BIGINT_MP 20151225 +#define BOTAN_HAS_BLOCK_CIPHER 20131128 #define BOTAN_HAS_CHACHA 20180807 #define BOTAN_HAS_CHACHA_SIMD32 20181104 #define BOTAN_HAS_CIPHER_MODES 20180124 +#define BOTAN_HAS_CIPHER_MODE_PADDING 20131128 #define BOTAN_HAS_CPUID 20170917 #define BOTAN_HAS_EME_OAEP 20180305 +#define BOTAN_HAS_EME_PKCS1 20190426 +#define BOTAN_HAS_EME_PKCS1v15 20131128 #define BOTAN_HAS_EME_RAW 20150313 #define BOTAN_HAS_EMSA1 20131128 +#define BOTAN_HAS_EMSA_PKCS1 20140118 #define BOTAN_HAS_EMSA_PSSR 20131128 +#define BOTAN_HAS_EMSA_RAW 20131128 #define BOTAN_HAS_ENTROPY_SOURCE 20151120 #define BOTAN_HAS_HASH 20180112 +#define BOTAN_HAS_HASH_ID 20131128 #define BOTAN_HAS_HEX_CODEC 20131128 +#define BOTAN_HAS_HMAC 20131128 #define BOTAN_HAS_KDF_BASE 20131128 #define BOTAN_HAS_MAC 20150626 #define BOTAN_HAS_MDX_HASH_FUNCTION 20131128 #define BOTAN_HAS_MGF1 20140118 #define BOTAN_HAS_MODES 20150626 +#define BOTAN_HAS_MODE_CBC 20131128 #define BOTAN_HAS_NUMBERTHEORY 20201108 #define BOTAN_HAS_OCSP 20201106 +#define BOTAN_HAS_PASSWORD_HASHING 20210419 +#define BOTAN_HAS_PBKDF 20180902 +#define BOTAN_HAS_PBKDF2 20180902 #define BOTAN_HAS_PEM_CODEC 20131128 +#define BOTAN_HAS_PKCS5_PBES2 20141119 #define BOTAN_HAS_PK_PADDING 20131128 #define BOTAN_HAS_POLY1305 20141227 #define BOTAN_HAS_PUBLIC_KEY_CRYPTO 20131128 @@ -4487,6 +4500,249 @@ inline void swap<Botan::BigInt>(Botan::BigInt& x, Botan::BigInt& y) namespace Botan { /** +* This class represents a block cipher object. +*/ +class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm + { + public: + + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<BlockCipher> + create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<BlockCipher> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + * @param algo_spec algorithm name + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * @return block size of this algorithm + */ + virtual size_t block_size() const = 0; + + /** + * @return native parallelism of this cipher in blocks + */ + virtual size_t parallelism() const { return 1; } + + /** + * @return prefererred parallelism of this cipher in bytes + */ + size_t parallel_bytes() const + { + return parallelism() * block_size() * BOTAN_BLOCK_CIPHER_PAR_MULT; + } + + /** + * @return provider information about this implementation. Default is "base", + * might also return "sse2", "avx2", "openssl", or some other arbitrary string. + */ + virtual std::string provider() const { return "base"; } + + /** + * Encrypt a block. + * @param in The plaintext block to be encrypted as a byte array. + * Must be of length block_size(). + * @param out The byte array designated to hold the encrypted block. + * Must be of length block_size(). + */ + void encrypt(const uint8_t in[], uint8_t out[]) const + { encrypt_n(in, out, 1); } + + /** + * Decrypt a block. + * @param in The ciphertext block to be decypted as a byte array. + * Must be of length block_size(). + * @param out The byte array designated to hold the decrypted block. + * Must be of length block_size(). + */ + void decrypt(const uint8_t in[], uint8_t out[]) const + { decrypt_n(in, out, 1); } + + /** + * Encrypt a block. + * @param block the plaintext block to be encrypted + * Must be of length block_size(). Will hold the result when the function + * has finished. + */ + void encrypt(uint8_t block[]) const { encrypt_n(block, block, 1); } + + /** + * Decrypt a block. + * @param block the ciphertext block to be decrypted + * Must be of length block_size(). Will hold the result when the function + * has finished. + */ + void decrypt(uint8_t block[]) const { decrypt_n(block, block, 1); } + + /** + * Encrypt one or more blocks + * @param block the input/output buffer (multiple of block_size()) + */ + template<typename Alloc> + void encrypt(std::vector<uint8_t, Alloc>& block) const + { + return encrypt_n(block.data(), block.data(), block.size() / block_size()); + } + + /** + * Decrypt one or more blocks + * @param block the input/output buffer (multiple of block_size()) + */ + template<typename Alloc> + void decrypt(std::vector<uint8_t, Alloc>& block) const + { + return decrypt_n(block.data(), block.data(), block.size() / block_size()); + } + + /** + * Encrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + */ + template<typename Alloc, typename Alloc2> + void encrypt(const std::vector<uint8_t, Alloc>& in, + std::vector<uint8_t, Alloc2>& out) const + { + return encrypt_n(in.data(), out.data(), in.size() / block_size()); + } + + /** + * Decrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + */ + template<typename Alloc, typename Alloc2> + void decrypt(const std::vector<uint8_t, Alloc>& in, + std::vector<uint8_t, Alloc2>& out) const + { + return decrypt_n(in.data(), out.data(), in.size() / block_size()); + } + + /** + * Encrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + * @param blocks the number of blocks to process + */ + virtual void encrypt_n(const uint8_t in[], uint8_t out[], + size_t blocks) const = 0; + + /** + * Decrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + * @param blocks the number of blocks to process + */ + virtual void decrypt_n(const uint8_t in[], uint8_t out[], + size_t blocks) const = 0; + + virtual void encrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const + { + const size_t BS = block_size(); + xor_buf(data, mask, blocks * BS); + encrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + virtual void decrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const + { + const size_t BS = block_size(); + xor_buf(data, mask, blocks * BS); + decrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + /** + * @return new object representing the same algorithm as *this + */ + virtual std::unique_ptr<BlockCipher> new_object() const = 0; + + BlockCipher* clone() const + { + return this->new_object().release(); + } + + virtual ~BlockCipher() = default; + }; + +/** +* Tweakable block ciphers allow setting a tweak which is a non-keyed +* value which affects the encryption/decryption operation. +*/ +class BOTAN_PUBLIC_API(2,8) Tweakable_Block_Cipher : public BlockCipher + { + public: + /** + * Set the tweak value. This must be called after setting a key. The value + * persists until either set_tweak, set_key, or clear is called. + * Different algorithms support different tweak length(s). If called with + * an unsupported length, Invalid_Argument will be thrown. + */ + virtual void set_tweak(const uint8_t tweak[], size_t len) = 0; + }; + +/** +* Represents a block cipher with a single fixed block size +*/ +template<size_t BS, size_t KMIN, size_t KMAX = 0, size_t KMOD = 1, typename BaseClass = BlockCipher> +class Block_Cipher_Fixed_Params : public BaseClass + { + public: + enum { BLOCK_SIZE = BS }; + size_t block_size() const final override { return BS; } + + // override to take advantage of compile time constant block size + void encrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const final override + { + xor_buf(data, mask, blocks * BS); + this->encrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + void decrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const final override + { + xor_buf(data, mask, blocks * BS); + this->decrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + Key_Length_Specification key_spec() const final override + { + return Key_Length_Specification(KMIN, KMAX, KMOD); + } + }; + +} + +namespace Botan { + +/** * This class represents any kind of computation which uses an internal * state, such as hash functions or MACs */ @@ -7978,6 +8234,517 @@ BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID& oid); namespace Botan { +/** +* Base class for PBKDF (password based key derivation function) +* implementations. Converts a password into a key using a salt +* and iterated hashing to make brute force attacks harder. +* +* Starting in 2.8 this functionality is also offered by PasswordHash. +* The PBKDF interface may be removed in a future release. +*/ +class BOTAN_PUBLIC_API(2,0) PBKDF + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PBKDF> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PBKDF> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * @return new instance of this same algorithm + */ + virtual std::unique_ptr<PBKDF> new_object() const = 0; + + /** + * @return new instance of this same algorithm + */ + PBKDF* clone() const + { + return this->new_object().release(); + } + + /** + * @return name of this PBKDF + */ + virtual std::string name() const = 0; + + virtual ~PBKDF() = default; + + /** + * Derive a key from a passphrase for a number of iterations + * specified by either iterations or if iterations == 0 then + * running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @return the number of iterations performed + */ + virtual size_t pbkdf(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const = 0; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + void pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + */ + void pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + // Following kept for compat with 1.10: + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt, salt_len, iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param iterations the number of iterations to use (use 10K or more) + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt.data(), salt.size(), iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt, salt_len, msec, iterations); + } + + /** + * Derive a key from a passphrase using a certain amount of time + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt.data(), salt.size(), msec, iterations); + } + }; + +/* +* Compatibility typedef +*/ +typedef PBKDF S2K; + +/** +* Password based key derivation function factory method +* @param algo_spec the name of the desired PBKDF algorithm +* @param provider the provider to use +* @return pointer to newly allocated object of that type +*/ +inline PBKDF* get_pbkdf(const std::string& algo_spec, + const std::string& provider = "") + { + return PBKDF::create_or_throw(algo_spec, provider).release(); + } + +inline PBKDF* get_s2k(const std::string& algo_spec) + { + return get_pbkdf(algo_spec); + } + + +} + +namespace Botan { + +/** +* Base class for password based key derivation functions. +* +* Converts a password into a key using a salt and iterated hashing to +* make brute force attacks harder. +*/ +class BOTAN_PUBLIC_API(2,8) PasswordHash + { + public: + virtual ~PasswordHash() = default; + + virtual std::string to_string() const = 0; + + /** + * Most password hashes have some notion of iterations. + */ + virtual size_t iterations() const = 0; + + /** + * Some password hashing algorithms have a parameter which controls how + * much memory is used. If not supported by some algorithm, returns 0. + */ + virtual size_t memory_param() const { return 0; } + + /** + * Some password hashing algorithms have a parallelism parameter. + * If the algorithm does not support this notion, then the + * function returns zero. This allows distinguishing between a + * password hash which just does not support parallel operation, + * vs one that does support parallel operation but which has been + * configured to use a single lane. + */ + virtual size_t parallelism() const { return 0; } + + /** + * Returns an estimate of the total number of bytes required to perform this + * key derivation. + * + * If this algorithm uses a small and constant amount of memory, with no + * effort made towards being memory hard, this function returns 0. + */ + virtual size_t total_memory_usage() const { return 0; } + + /** + * Derive a key from a password + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const = 0; + + /** + * Derive a key from a password plus additional data and/or a secret key + * + * Currently this is only supported for Argon2. Using a non-empty AD or key + * with other algorithms will cause a Not_Implemented exception. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param ad some additional data + * @param ad_len length of ad in bytes + * @param key a secret key + * @param key_len length of key in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t ad[], size_t ad_len, + const uint8_t key[], size_t key_len) const; + }; + +class BOTAN_PUBLIC_API(2,8) PasswordHashFamily + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PasswordHashFamily> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PasswordHashFamily> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + virtual ~PasswordHashFamily() = default; + + /** + * @return name of this PasswordHash + */ + virtual std::string name() const = 0; + + /** + * Return a new parameter set tuned for this machine + * @param output_length how long the output length will be + * @param msec the desired execution time in milliseconds + * + * @param max_memory_usage_mb some password hash functions can use a tunable + * amount of memory, in this case max_memory_usage limits the amount of RAM + * the returned parameters will require, in mebibytes (2**20 bytes). It may + * require some small amount above the request. Set to zero to place no + * limit at all. + */ + virtual std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory_usage_mb = 0) const = 0; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + virtual std::unique_ptr<PasswordHash> default_params() const = 0; + + /** + * Return a parameter chosen based on a rough approximation with the + * specified iteration count. The exact value this returns for a particular + * algorithm may change from over time. Think of it as an alternative to + * tune, where time is expressed in terms of PBKDF2 iterations rather than + * milliseconds. + */ + virtual std::unique_ptr<PasswordHash> from_iterations(size_t iterations) const = 0; + + /** + * Create a password hash using some scheme specific format. Parameters are as follows: + * - For PBKDF2, PGP-S2K, and Bcrypt-PBKDF, i1 is iterations + * - Scrypt uses N, r, p for i{1-3} + * - Argon2 family uses memory (in KB), iterations, and parallelism for i{1-3} + * + * All unneeded parameters should be set to 0 or left blank. + */ + virtual std::unique_ptr<PasswordHash> from_params( + size_t i1, + size_t i2 = 0, + size_t i3 = 0) const = 0; + }; + +} + +BOTAN_FUTURE_INTERNAL_HEADER(pbkdf2.h) + +namespace Botan { + +BOTAN_PUBLIC_API(2,0) size_t pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec); + +/** +* Perform PBKDF2. The prf is assumed to be keyed already. +*/ +BOTAN_PUBLIC_API(2,8) void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], size_t out_len, + const uint8_t salt[], size_t salt_len, + size_t iterations); + +/** +* PBKDF2 +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2 final : public PasswordHash + { + public: + PBKDF2(const MessageAuthenticationCode& prf, size_t iter) : + m_prf(prf.new_object()), + m_iterations(iter) + {} + + PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec); + + size_t iterations() const override { return m_iterations; } + + std::string to_string() const override; + + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + size_t m_iterations; + }; + +/** +* Family of PKCS #5 PBKDF2 operations +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2_Family final : public PasswordHashFamily + { + public: + PBKDF2_Family(MessageAuthenticationCode* prf) : m_prf(prf) {} + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_len, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t iter, size_t, size_t) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + }; + +/** +* PKCS #5 PBKDF2 (old interface) +*/ +class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF2 final : public PBKDF + { + public: + std::string name() const override; + + std::unique_ptr<PBKDF> new_object() const override; + + size_t pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; + + /** + * Create a PKCS #5 instance using the specified message auth code + * @param mac_fn the MAC object to use as PRF + */ + explicit PKCS5_PBKDF2(MessageAuthenticationCode* mac_fn) : m_mac(mac_fn) {} + private: + std::unique_ptr<MessageAuthenticationCode> m_mac; + }; + +} + +namespace Botan { + class DataSource; namespace PEM_Code { diff --git a/include/amalgamation-armhf/botan_all.cpp b/include/amalgamation-armhf/botan_all.cpp index 65a77b9..f0e23c9 100644 --- a/include/amalgamation-armhf/botan_all.cpp +++ b/include/amalgamation-armhf/botan_all.cpp @@ -400,6 +400,297 @@ class BOTAN_TEST_API calendar_point namespace Botan { /** +* Block Cipher Mode Padding Method +* This class is pretty limited, it cannot deal well with +* randomized padding methods, or any padding method that +* wants to add more than one block. For instance, it should +* be possible to define cipher text stealing mode as simply +* a padding mode for CBC, which happens to consume the last +* two block (and requires use of the block cipher). +*/ +class BOTAN_TEST_API BlockCipherModePaddingMethod + { + public: + /** + * Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7") + * @param algo_spec block cipher padding mode name + */ + static std::unique_ptr<BlockCipherModePaddingMethod> create(const std::string& algo_spec); + + /** + * Add padding bytes to buffer. + * @param buffer data to pad + * @param final_block_bytes size of the final block in bytes + * @param block_size size of each block in bytes + */ + virtual void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const = 0; + + /** + * Remove padding bytes from block + * @param block the last block + * @param len the size of the block in bytes + * @return number of data bytes, or if the padding is invalid returns len + */ + virtual size_t unpad(const uint8_t block[], size_t len) const = 0; + + /** + * @param block_size of the cipher + * @return valid block size for this padding mode + */ + virtual bool valid_blocksize(size_t block_size) const = 0; + + /** + * @return name of the mode + */ + virtual std::string name() const = 0; + + /** + * virtual destructor + */ + virtual ~BlockCipherModePaddingMethod() = default; + }; + +/** +* PKCS#7 Padding +*/ +class BOTAN_TEST_API PKCS7_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "PKCS7"; } + }; + +/** +* ANSI X9.23 Padding +*/ +class BOTAN_TEST_API ANSI_X923_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "X9.23"; } + }; + +/** +* One And Zeros Padding (ISO/IEC 9797-1, padding method 2) +*/ +class BOTAN_TEST_API OneAndZeros_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2); } + + std::string name() const override { return "OneAndZeros"; } + }; + +/** +* ESP Padding (RFC 4304) +*/ +class BOTAN_TEST_API ESP_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "ESP"; } + }; + +/** +* Null Padding +*/ +class Null_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>&, size_t, size_t) const override + { + /* no padding */ + } + + size_t unpad(const uint8_t[], size_t size) const override { return size; } + + bool valid_blocksize(size_t) const override { return true; } + + std::string name() const override { return "NoPadding"; } + }; + +} + +namespace Botan { + +/** +* CBC Mode +*/ +class CBC_Mode : public Cipher_Mode + { + public: + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + size_t default_nonce_length() const override; + + bool valid_nonce_length(size_t n) const override; + + void clear() override; + + void reset() override; + + protected: + CBC_Mode(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding); + + const BlockCipher& cipher() const { return *m_cipher; } + + const BlockCipherModePaddingMethod& padding() const + { + BOTAN_ASSERT_NONNULL(m_padding); + return *m_padding; + } + + size_t block_size() const { return m_block_size; } + + secure_vector<uint8_t>& state() { return m_state; } + + uint8_t* state_ptr() { return m_state.data(); } + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + std::unique_ptr<BlockCipher> m_cipher; + std::unique_ptr<BlockCipherModePaddingMethod> m_padding; + secure_vector<uint8_t> m_state; + size_t m_block_size; + }; + +/** +* CBC Encryption +*/ +class CBC_Encryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Encryption(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + CBC_Mode(std::move(cipher), std::move(padding)) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + }; + +/** +* CBC Encryption with ciphertext stealing (CBC-CS3 variant) +*/ +class CTS_Encryption final : public CBC_Encryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Encryption(std::unique_ptr<BlockCipher> cipher) : + CBC_Encryption(std::move(cipher), nullptr) + {} + + size_t output_length(size_t input_length) const override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +/** +* CBC Decryption +*/ +class CBC_Decryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Decryption(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + CBC_Mode(std::move(cipher), std::move(padding)), + m_tempbuf(update_granularity()) + {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + + void reset() override; + + private: + secure_vector<uint8_t> m_tempbuf; + }; + +/** +* CBC Decryption with ciphertext stealing (CBC-CS3 variant) +*/ +class CTS_Decryption final : public CBC_Decryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Decryption(std::unique_ptr<BlockCipher> cipher) : + CBC_Decryption(std::move(cipher), nullptr) + {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +} + +namespace Botan { + +/** * DJB's ChaCha (https://cr.yp.to/chacha.html) */ class ChaCha final : public StreamCipher @@ -2088,6 +2379,26 @@ class EME namespace Botan { +/** +* EME from PKCS #1 v1.5 +*/ +class BOTAN_TEST_API EME_PKCS1v15 final : public EME + { + public: + size_t maximum_input_size(size_t) const override; + + secure_vector<uint8_t> pad(const uint8_t[], size_t, size_t, + RandomNumberGenerator&) const override; + + secure_vector<uint8_t> unpad(uint8_t& valid_mask, + const uint8_t in[], + size_t in_len) const override; + }; + +} + +namespace Botan { + class EME_Raw final : public EME { public: @@ -2261,6 +2572,126 @@ class EMSA1 final : public EMSA namespace Botan { /** +* PKCS #1 v1.5 signature padding +* aka PKCS #1 block type 1 +* aka EMSA3 from IEEE 1363 +*/ +class EMSA_PKCS1v15 final : public EMSA + { + public: + /** + * @param hash the hash function to use + */ + explicit EMSA_PKCS1v15(std::unique_ptr<HashFunction> hash); + + std::unique_ptr<EMSA> new_object() override + { + return std::make_unique<EMSA_PKCS1v15>(m_hash->new_object()); + } + + void update(const uint8_t[], size_t) override; + + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator& rng) override; + + bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&, + size_t) override; + + std::string name() const override + { return "EMSA3(" + m_hash->name() + ")"; } + + AlgorithmIdentifier config_for_x509(const Private_Key& key, + const std::string& cert_hash_name) const override; + + bool requires_message_recovery() const override { return true; } + private: + std::unique_ptr<HashFunction> m_hash; + std::vector<uint8_t> m_hash_id; + }; + +/** +* EMSA_PKCS1v15_Raw which is EMSA_PKCS1v15 without a hash or digest id +* (which according to QCA docs is "identical to PKCS#11's CKM_RSA_PKCS +* mechanism", something I have not confirmed) +*/ +class EMSA_PKCS1v15_Raw final : public EMSA + { + public: + std::unique_ptr<EMSA> new_object() override { return std::make_unique<EMSA_PKCS1v15_Raw>(); } + + void update(const uint8_t[], size_t) override; + + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator& rng) override; + + bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&, + size_t) override; + + EMSA_PKCS1v15_Raw(); + + /** + * @param hash_algo t he digest id for that hash is included in + * the signature. + */ + EMSA_PKCS1v15_Raw(const std::string& hash_algo); + + std::string name() const override + { + if(m_hash_name.empty()) return "EMSA3(Raw)"; + else return "EMSA3(Raw," + m_hash_name + ")"; + } + + bool requires_message_recovery() const override { return true; } + private: + size_t m_hash_output_len = 0; + std::string m_hash_name; + std::vector<uint8_t> m_hash_id; + secure_vector<uint8_t> m_message; + }; + +} + +namespace Botan { + +/** +* EMSA-Raw - sign inputs directly +* Don't use this unless you know what you are doing. +*/ +class EMSA_Raw final : public EMSA + { + public: + std::unique_ptr<EMSA> new_object() override { return std::make_unique<EMSA_Raw>(); } + + explicit EMSA_Raw(size_t expected_hash_size = 0) : + m_expected_size(expected_hash_size) {} + + std::string name() const override; + + bool requires_message_recovery() const override { return false; } + private: + void update(const uint8_t[], size_t) override; + secure_vector<uint8_t> raw_data() override; + + secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t, + RandomNumberGenerator&) override; + + bool verify(const secure_vector<uint8_t>&, + const secure_vector<uint8_t>&, + size_t) override; + + const size_t m_expected_size; + secure_vector<uint8_t> m_message; + }; + +} + +namespace Botan { + +/** * No_Filesystem_Access Exception */ class No_Filesystem_Access final : public Exception @@ -2278,6 +2709,62 @@ BOTAN_TEST_API std::vector<std::string> get_files_recursive(const std::string& d namespace Botan { +/** +* Return the PKCS #1 hash identifier +* @see RFC 3447 section 9.2 +* @param hash_name the name of the hash function +* @return uint8_t sequence identifying the hash +* @throw Invalid_Argument if the hash has no known PKCS #1 hash id +*/ +std::vector<uint8_t> BOTAN_TEST_API pkcs_hash_id(const std::string& hash_name); + +/** +* Return the IEEE 1363 hash identifier +* @param hash_name the name of the hash function +* @return uint8_t code identifying the hash, or 0 if not known +*/ +uint8_t ieee1363_hash_id(const std::string& hash_name); + +} + +namespace Botan { + +/** +* HMAC +*/ +class HMAC final : public MessageAuthenticationCode + { + public: + void clear() override; + std::string name() const override; + std::unique_ptr<MessageAuthenticationCode> new_object() const override; + + size_t output_length() const override; + + Key_Length_Specification key_spec() const override; + + /** + * @param hash the hash to use for HMACing + */ + explicit HMAC(std::unique_ptr<HashFunction> hash); + + HMAC(const HMAC&) = delete; + HMAC& operator=(const HMAC&) = delete; + private: + void add_data(const uint8_t[], size_t) override; + void final_result(uint8_t[]) override; + void key_schedule(const uint8_t[], size_t) override; + + std::unique_ptr<HashFunction> m_hash; + secure_vector<uint8_t> m_ikey, m_okey; + size_t m_hash_output_length; + size_t m_hash_block_size; + }; + +} + +namespace Botan { + namespace KeyPair { /** @@ -5254,6 +5741,77 @@ bool host_wildcard_match(const std::string& wildcard, } + +namespace Botan { + +class RandomNumberGenerator; + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds to run PBKDF2 +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds to run PBKDF2 +* @param out_iterations_if_nonnull if not null, set to the number +* of PBKDF iterations used +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param iterations how many iterations to run PBKDF2 +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t iterations, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Decrypt a PKCS #5 v2.0 encrypted stream +* @param key_bits the input +* @param passphrase the passphrase to use for decryption +* @param params the PBES2 parameters +*/ +secure_vector<uint8_t> +pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params); + +} /** * Ordinary applications should never need to include or use this * header. It is exposed only for specialized applications which want @@ -11679,6 +12237,604 @@ void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& } /* +* Block Ciphers +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_AES) +#endif + +#if defined(BOTAN_HAS_ARIA) +#endif + +#if defined(BOTAN_HAS_BLOWFISH) +#endif + +#if defined(BOTAN_HAS_CAMELLIA) +#endif + +#if defined(BOTAN_HAS_CAST_128) +#endif + +#if defined(BOTAN_HAS_CASCADE) +#endif + +#if defined(BOTAN_HAS_DES) +#endif + +#if defined(BOTAN_HAS_GOST_28147_89) +#endif + +#if defined(BOTAN_HAS_IDEA) +#endif + +#if defined(BOTAN_HAS_LION) +#endif + +#if defined(BOTAN_HAS_NOEKEON) +#endif + +#if defined(BOTAN_HAS_SEED) +#endif + +#if defined(BOTAN_HAS_SERPENT) +#endif + +#if defined(BOTAN_HAS_SHACAL2) +#endif + +#if defined(BOTAN_HAS_SM4) +#endif + +#if defined(BOTAN_HAS_TWOFISH) +#endif + +#if defined(BOTAN_HAS_THREEFISH_512) +#endif + +#if defined(BOTAN_HAS_COMMONCRYPTO) +#endif + +namespace Botan { + +std::unique_ptr<BlockCipher> +BlockCipher::create(const std::string& algo, + const std::string& provider) + { +#if defined(BOTAN_HAS_COMMONCRYPTO) + if(provider.empty() || provider == "commoncrypto") + { + if(auto bc = make_commoncrypto_block_cipher(algo)) + return bc; + + if(!provider.empty()) + return nullptr; + } +#endif + + // TODO: CryptoAPI + // TODO: /dev/crypto + + // Only base providers from here on out + if(provider.empty() == false && provider != "base") + return nullptr; + +#if defined(BOTAN_HAS_AES) + if(algo == "AES-128") + { + return std::make_unique<AES_128>(); + } + + if(algo == "AES-192") + { + return std::make_unique<AES_192>(); + } + + if(algo == "AES-256") + { + return std::make_unique<AES_256>(); + } +#endif + +#if defined(BOTAN_HAS_ARIA) + if(algo == "ARIA-128") + { + return std::make_unique<ARIA_128>(); + } + + if(algo == "ARIA-192") + { + return std::make_unique<ARIA_192>(); + } + + if(algo == "ARIA-256") + { + return std::make_unique<ARIA_256>(); + } +#endif + +#if defined(BOTAN_HAS_SERPENT) + if(algo == "Serpent") + { + return std::make_unique<Serpent>(); + } +#endif + +#if defined(BOTAN_HAS_SHACAL2) + if(algo == "SHACAL2") + { + return std::make_unique<SHACAL2>(); + } +#endif + +#if defined(BOTAN_HAS_TWOFISH) + if(algo == "Twofish") + { + return std::make_unique<Twofish>(); + } +#endif + +#if defined(BOTAN_HAS_THREEFISH_512) + if(algo == "Threefish-512") + { + return std::make_unique<Threefish_512>(); + } +#endif + +#if defined(BOTAN_HAS_BLOWFISH) + if(algo == "Blowfish") + { + return std::make_unique<Blowfish>(); + } +#endif + +#if defined(BOTAN_HAS_CAMELLIA) + if(algo == "Camellia-128") + { + return std::make_unique<Camellia_128>(); + } + + if(algo == "Camellia-192") + { + return std::make_unique<Camellia_192>(); + } + + if(algo == "Camellia-256") + { + return std::make_unique<Camellia_256>(); + } +#endif + +#if defined(BOTAN_HAS_DES) + if(algo == "DES") + { + return std::make_unique<DES>(); + } + + if(algo == "TripleDES" || algo == "3DES" || algo == "DES-EDE") + { + return std::make_unique<TripleDES>(); + } +#endif + +#if defined(BOTAN_HAS_NOEKEON) + if(algo == "Noekeon") + { + return std::make_unique<Noekeon>(); + } +#endif + +#if defined(BOTAN_HAS_CAST_128) + if(algo == "CAST-128" || algo == "CAST5") + { + return std::make_unique<CAST_128>(); + } +#endif + +#if defined(BOTAN_HAS_IDEA) + if(algo == "IDEA") + { + return std::make_unique<IDEA>(); + } +#endif + +#if defined(BOTAN_HAS_SEED) + if(algo == "SEED") + { + return std::make_unique<SEED>(); + } +#endif + +#if defined(BOTAN_HAS_SM4) + if(algo == "SM4") + { + return std::make_unique<SM4>(); + } +#endif + + const SCAN_Name req(algo); + +#if defined(BOTAN_HAS_GOST_28147_89) + if(req.algo_name() == "GOST-28147-89") + { + return std::make_unique<GOST_28147_89>(req.arg(0, "R3411_94_TestParam")); + } +#endif + +#if defined(BOTAN_HAS_CASCADE) + if(req.algo_name() == "Cascade" && req.arg_count() == 2) + { + std::unique_ptr<BlockCipher> c1 = BlockCipher::create(req.arg(0)); + std::unique_ptr<BlockCipher> c2 = BlockCipher::create(req.arg(1)); + + if(c1 && c2) + return std::make_unique<Cascade_Cipher>(std::move(c1), std::move(c2)); + } +#endif + +#if defined(BOTAN_HAS_LION) + if(req.algo_name() == "Lion" && req.arg_count_between(2, 3)) + { + std::unique_ptr<HashFunction> hash = HashFunction::create(req.arg(0)); + std::unique_ptr<StreamCipher> stream = StreamCipher::create(req.arg(1)); + + if(hash && stream) + { + const size_t block_size = req.arg_as_integer(2, 1024); + return std::make_unique<Lion>(std::move(hash), std::move(stream), block_size); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<BlockCipher> +BlockCipher::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto bc = BlockCipher::create(algo, provider)) + { + return bc; + } + throw Lookup_Error("Block cipher", algo, provider); + } + +std::vector<std::string> BlockCipher::providers(const std::string& algo) + { + return probe_providers_of<BlockCipher>(algo, { "base", "commoncrypto" }); + } + +} +/* +* CBC Mode +* (C) 1999-2007,2013,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +CBC_Mode::CBC_Mode(std::unique_ptr<BlockCipher> cipher, + std::unique_ptr<BlockCipherModePaddingMethod> padding) : + m_cipher(std::move(cipher)), + m_padding(std::move(padding)), + m_block_size(m_cipher->block_size()) + { + if(m_padding && !m_padding->valid_blocksize(m_block_size)) + throw Invalid_Argument("Padding " + m_padding->name() + + " cannot be used with " + + m_cipher->name() + "/CBC"); + } + +void CBC_Mode::clear() + { + m_cipher->clear(); + reset(); + } + +void CBC_Mode::reset() + { + m_state.clear(); + } + +std::string CBC_Mode::name() const + { + if(m_padding) + return cipher().name() + "/CBC/" + padding().name(); + else + return cipher().name() + "/CBC/CTS"; + } + +size_t CBC_Mode::update_granularity() const + { + return cipher().parallel_bytes(); + } + +Key_Length_Specification CBC_Mode::key_spec() const + { + return cipher().key_spec(); + } + +size_t CBC_Mode::default_nonce_length() const + { + return block_size(); + } + +bool CBC_Mode::valid_nonce_length(size_t n) const + { + return (n == 0 || n == block_size()); + } + +void CBC_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_cipher->set_key(key, length); + m_state.clear(); + } + +void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + /* + * A nonce of zero length means carry the last ciphertext value over + * as the new IV, as unfortunately some protocols require this. If + * this is the first message then we use an IV of all zeros. + */ + if(nonce_len) + m_state.assign(nonce, nonce + nonce_len); + else if(m_state.empty()) + m_state.resize(m_cipher->block_size()); + // else leave the state alone + } + +size_t CBC_Encryption::minimum_final_size() const + { + return 0; + } + +size_t CBC_Encryption::output_length(size_t input_length) const + { + if(input_length == 0) + return block_size(); + else + return round_up(input_length, block_size()); + } + +size_t CBC_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "CBC input is full blocks"); + const size_t blocks = sz / BS; + + if(blocks > 0) + { + xor_buf(&buf[0], state_ptr(), BS); + cipher().encrypt(&buf[0]); + + for(size_t i = 1; i != blocks; ++i) + { + xor_buf(&buf[BS*i], &buf[BS*(i-1)], BS); + cipher().encrypt(&buf[BS*i]); + } + + state().assign(&buf[BS*(blocks-1)], &buf[BS*blocks]); + } + + return sz; + } + +void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + + const size_t BS = block_size(); + + const size_t bytes_in_final_block = (buffer.size()-offset) % BS; + + padding().add_padding(buffer, bytes_in_final_block, BS); + + BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary"); + + update(buffer, offset); + } + +bool CTS_Encryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Encryption::minimum_final_size() const + { + return block_size() + 1; + } + +size_t CTS_Encryption::output_length(size_t input_length) const + { + return input_length; // no ciphertext expansion in CTS + } + +void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + uint8_t* buf = buffer.data() + offset; + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to encrypt"); + + if(sz % BS == 0) + { + update(buffer, offset); + + // swap last two blocks + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + xor_buf(last.data(), state_ptr(), BS); + cipher().encrypt(last.data()); + + for(size_t i = 0; i != final_bytes - BS; ++i) + { + last[i] ^= last[i + BS]; + last[i + BS] ^= last[i]; + } + + cipher().encrypt(last.data()); + + buffer += last; + } + } + +size_t CBC_Decryption::output_length(size_t input_length) const + { + return input_length; // precise for CTS, worst case otherwise + } + +size_t CBC_Decryption::minimum_final_size() const + { + return block_size(); + } + +size_t CBC_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "Input is full blocks"); + size_t blocks = sz / BS; + + while(blocks) + { + const size_t to_proc = std::min(BS * blocks, m_tempbuf.size()); + + cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS); + + xor_buf(m_tempbuf.data(), state_ptr(), BS); + xor_buf(&m_tempbuf[BS], buf, to_proc - BS); + copy_mem(state_ptr(), buf + (to_proc - BS), BS); + + copy_mem(buf, m_tempbuf.data(), to_proc); + + buf += to_proc; + blocks -= to_proc / BS; + } + + return sz; + } + +void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz == 0 || sz % BS) + throw Decoding_Error(name() + ": Ciphertext not a multiple of block size"); + + update(buffer, offset); + + const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.size()-BS], BS); + buffer.resize(buffer.size() - pad_bytes); // remove padding + if(pad_bytes == 0 && padding().name() != "NoPadding") + { + throw Decoding_Error("Invalid CBC padding"); + } + } + +void CBC_Decryption::reset() + { + CBC_Mode::reset(); + zeroise(m_tempbuf); + } + +bool CTS_Decryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Decryption::minimum_final_size() const + { + return block_size() + 1; + } + +void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to decrypt"); + + if(sz % BS == 0) + { + // swap last two blocks + + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + + update(buffer, offset); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + cipher().decrypt(last.data()); + + xor_buf(last.data(), &last[BS], final_bytes - BS); + + for(size_t i = 0; i != final_bytes - BS; ++i) + std::swap(last[i], last[i + BS]); + + cipher().decrypt(last.data()); + xor_buf(last.data(), state_ptr(), BS); + + buffer += last; + } + } + +} +/* * ChaCha * (C) 2014,2018 Jack Lloyd * @@ -13136,6 +14292,111 @@ OAEP::OAEP(std::unique_ptr<HashFunction> hash, } /* +* PKCS #1 v1.5 Type 2 (encryption) padding +* (C) 1999-2007,2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/* +* PKCS1 Pad Operation +*/ +secure_vector<uint8_t> EME_PKCS1v15::pad(const uint8_t in[], size_t inlen, + size_t key_length, + RandomNumberGenerator& rng) const + { + key_length /= 8; + + if(inlen > maximum_input_size(key_length * 8)) + { + throw Invalid_Argument("PKCS1: Input is too large"); + } + + secure_vector<uint8_t> out(key_length); + + out[0] = 0x02; + rng.randomize(out.data() + 1, (key_length - inlen - 2)); + + for(size_t j = 1; j != key_length - inlen - 1; ++j) + { + if(out[j] == 0) + { + out[j] = rng.next_nonzero_byte(); + } + } + + buffer_insert(out, key_length - inlen, in, inlen); + + return out; + } + +/* +* PKCS1 Unpad Operation +*/ +secure_vector<uint8_t> EME_PKCS1v15::unpad(uint8_t& valid_mask, + const uint8_t in[], size_t inlen) const + { + /* + * RSA decryption pads the ciphertext up to the modulus size, so this only + * occurs with very (!) small keys, or when fuzzing. + * + * 11 bytes == 00,02 + 8 bytes mandatory padding + 00 + */ + if(inlen < 11) + { + valid_mask = false; + return secure_vector<uint8_t>(); + } + + CT::poison(in, inlen); + + CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared(); + CT::Mask<uint8_t> seen_zero_m = CT::Mask<uint8_t>::cleared(); + size_t delim_idx = 2; // initial 0002 + + bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[0], 0); + bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[1], 2); + + for(size_t i = 2; i < inlen; ++i) + { + const auto is_zero_m = CT::Mask<uint8_t>::is_zero(in[i]); + delim_idx += seen_zero_m.if_not_set_return(1); + seen_zero_m |= is_zero_m; + } + + // no zero delim -> bad padding + bad_input_m |= ~seen_zero_m; + /* + delim indicates < 8 bytes padding -> bad padding + + We require 11 here because we are counting also the 00 delim byte + */ + bad_input_m |= CT::Mask<uint8_t>(CT::Mask<size_t>::is_lt(delim_idx, 11)); + + valid_mask = (~bad_input_m).unpoisoned_value(); + auto output = CT::copy_output(bad_input_m, in, inlen, delim_idx); + + CT::unpoison(in, inlen); + + return output; + } + +/* +* Return the max input size for a given key size +*/ +size_t EME_PKCS1v15::maximum_input_size(size_t keybits) const + { + if(keybits / 8 > 10) + return ((keybits / 8) - 10); + else + return 0; + } + +} +/* * (C) 2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) @@ -13295,6 +14556,167 @@ AlgorithmIdentifier EMSA1::config_for_x509(const Private_Key& key, } /* +* PKCS #1 v1.5 signature padding +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg, + size_t output_bits, + const uint8_t hash_id[], + size_t hash_id_length) + { + size_t output_length = output_bits / 8; + if(output_length < hash_id_length + msg.size() + 10) + throw Encoding_Error("emsa3_encoding: Output length is too small"); + + secure_vector<uint8_t> T(output_length); + const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2; + + T[0] = 0x01; + set_mem(&T[1], P_LENGTH, 0xFF); + T[P_LENGTH+1] = 0x00; + + if(hash_id_length > 0) + { + BOTAN_ASSERT_NONNULL(hash_id); + buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length); + } + + buffer_insert(T, output_length-msg.size(), msg.data(), msg.size()); + return T; + } + +} + +void EMSA_PKCS1v15::update(const uint8_t input[], size_t length) + { + m_hash->update(input, length); + } + +secure_vector<uint8_t> EMSA_PKCS1v15::raw_data() + { + return m_hash->final(); + } + +secure_vector<uint8_t> +EMSA_PKCS1v15::encoding_of(const secure_vector<uint8_t>& msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) + { + if(msg.size() != m_hash->output_length()) + throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length"); + + return emsa3_encoding(msg, output_bits, + m_hash_id.data(), m_hash_id.size()); + } + +bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t key_bits) + { + if(raw.size() != m_hash->output_length()) + return false; + + try + { + return (coded == emsa3_encoding(raw, key_bits, + m_hash_id.data(), m_hash_id.size())); + } + catch(...) + { + return false; + } + } + +AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key, + const std::string& cert_hash_name) const + { + if(cert_hash_name != m_hash->name()) + throw Invalid_Argument("Hash function from opts and hash_fn argument" + " need to be identical"); + // check that the signature algorithm and the padding scheme fit + if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3")) + { + throw Invalid_Argument("Encoding scheme with canonical name EMSA3" + " not supported for signature algorithm " + key.algo_name()); + } + + // for RSA PKCSv1.5 parameters "SHALL" be NULL + + const OID oid = OID::from_string(key.algo_name() + "/" + name()); + return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_NULL_PARAM); + } + +EMSA_PKCS1v15::EMSA_PKCS1v15(std::unique_ptr<HashFunction> hash) : + m_hash(std::move(hash)) + { + m_hash_id = pkcs_hash_id(m_hash->name()); + } + +EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw() + { + m_hash_output_len = 0; + // m_hash_id, m_hash_name left empty + } + +EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo) + { + std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo)); + m_hash_id = pkcs_hash_id(hash_algo); + m_hash_name = hash->name(); + m_hash_output_len = hash->output_length(); + } + +void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length) + { + m_message += std::make_pair(input, length); + } + +secure_vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data() + { + secure_vector<uint8_t> ret; + std::swap(ret, m_message); + + if(m_hash_output_len > 0 && ret.size() != m_hash_output_len) + throw Encoding_Error("EMSA_PKCS1v15_Raw::encoding_of: Bad input length"); + + return ret; + } + +secure_vector<uint8_t> +EMSA_PKCS1v15_Raw::encoding_of(const secure_vector<uint8_t>& msg, + size_t output_bits, + RandomNumberGenerator& /*rng*/) + { + return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size()); + } + +bool EMSA_PKCS1v15_Raw::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t key_bits) + { + if(m_hash_output_len > 0 && raw.size() != m_hash_output_len) + return false; + + try + { + return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size())); + } + catch(...) + { + return false; + } + } + +} +/* * PSSR * (C) 1999-2007,2017 Jack Lloyd * @@ -13578,6 +15000,96 @@ std::string PSSR_Raw::name() const } /* +* EMSA-Raw +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +std::string EMSA_Raw::name() const + { + if(m_expected_size > 0) + return "Raw(" + std::to_string(m_expected_size) + ")"; + return "Raw"; + } + +/* +* EMSA-Raw Encode Operation +*/ +void EMSA_Raw::update(const uint8_t input[], size_t length) + { + m_message += std::make_pair(input, length); + } + +/* +* Return the raw (unencoded) data +*/ +secure_vector<uint8_t> EMSA_Raw::raw_data() + { + if(m_expected_size && m_message.size() != m_expected_size) + throw Invalid_Argument("EMSA_Raw was configured to use a " + + std::to_string(m_expected_size) + + " byte hash but instead was used for a " + + std::to_string(m_message.size()) + " hash"); + + secure_vector<uint8_t> output; + std::swap(m_message, output); + return output; + } + +/* +* EMSA-Raw Encode Operation +*/ +secure_vector<uint8_t> +EMSA_Raw::encoding_of(const secure_vector<uint8_t>& msg, + size_t /*output_bits*/, + RandomNumberGenerator& /*rng*/) + { + if(m_expected_size && msg.size() != m_expected_size) + throw Invalid_Argument("EMSA_Raw was configured to use a " + + std::to_string(m_expected_size) + + " byte hash but instead was used for a " + + std::to_string(msg.size()) + " hash"); + + return msg; + } + +/* +* EMSA-Raw Verify Operation +*/ +bool EMSA_Raw::verify(const secure_vector<uint8_t>& coded, + const secure_vector<uint8_t>& raw, + size_t /*key_bits*/) + { + if(m_expected_size && raw.size() != m_expected_size) + return false; + + if(coded.size() == raw.size()) + return (coded == raw); + + if(coded.size() > raw.size()) + return false; + + // handle zero padding differences + const size_t leading_zeros_expected = raw.size() - coded.size(); + + bool same_modulo_leading_zeros = true; + + for(size_t i = 0; i != leading_zeros_expected; ++i) + if(raw[i]) + same_modulo_leading_zeros = false; + + if(!constant_time_compare(coded.data(), raw.data() + leading_zeros_expected, coded.size())) + same_modulo_leading_zeros = false; + + return same_modulo_leading_zeros; + } + +} +/* * Entropy Source Polling * (C) 2008-2010,2015 Jack Lloyd * @@ -14076,6 +15588,159 @@ std::vector<std::string> HashFunction::providers(const std::string& algo_spec) } /* +* Hash Function Identification +* (C) 1999-2008 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +const uint8_t MD5_PKCS_ID[] = { +0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, +0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; + +const uint8_t RIPEMD_160_PKCS_ID[] = { +0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, +0x01, 0x05, 0x00, 0x04, 0x14 }; + +const uint8_t SHA_160_PKCS_ID[] = { +0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, +0x1A, 0x05, 0x00, 0x04, 0x14 }; + +const uint8_t SHA_224_PKCS_ID[] = { +0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C }; + +const uint8_t SHA_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA_384_PKCS_ID[] = { +0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; + +const uint8_t SHA_512_PKCS_ID[] = { +0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; + +const uint8_t SHA_512_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA3_224_PKCS_ID[] = { +0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x07, 0x05, 0x00, 0x04, 0x1C }; + +const uint8_t SHA3_256_PKCS_ID[] = { +0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20 }; + +const uint8_t SHA3_384_PKCS_ID[] = { +0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x09, 0x05, 0x00, 0x04, 0x30 }; + +const uint8_t SHA3_512_PKCS_ID[] = { +0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, +0x03, 0x04, 0x02, 0x0A, 0x05, 0x00, 0x04, 0x40 }; + +const uint8_t SM3_PKCS_ID[] = { +0x30, 0x30, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x81, 0x1C, 0xCF, +0x55, 0x01, 0x83, 0x11, 0x05, 0x00, 0x04, 0x20, +}; + +} + +/* +* HashID as specified by PKCS +*/ +std::vector<uint8_t> pkcs_hash_id(const std::string& name) + { + // Special case for SSL/TLS RSA signatures + if(name == "Parallel(MD5,SHA-160)") + return std::vector<uint8_t>(); + + // If you add a value to this function, also update test_hash_id.cpp + + if(name == "MD5") + return std::vector<uint8_t>(MD5_PKCS_ID, + MD5_PKCS_ID + sizeof(MD5_PKCS_ID)); + + if(name == "RIPEMD-160") + return std::vector<uint8_t>(RIPEMD_160_PKCS_ID, + RIPEMD_160_PKCS_ID + sizeof(RIPEMD_160_PKCS_ID)); + + if(name == "SHA-160" || name == "SHA-1" || name == "SHA1") + return std::vector<uint8_t>(SHA_160_PKCS_ID, + SHA_160_PKCS_ID + sizeof(SHA_160_PKCS_ID)); + + if(name == "SHA-224") + return std::vector<uint8_t>(SHA_224_PKCS_ID, + SHA_224_PKCS_ID + sizeof(SHA_224_PKCS_ID)); + + if(name == "SHA-256") + return std::vector<uint8_t>(SHA_256_PKCS_ID, + SHA_256_PKCS_ID + sizeof(SHA_256_PKCS_ID)); + + if(name == "SHA-384") + return std::vector<uint8_t>(SHA_384_PKCS_ID, + SHA_384_PKCS_ID + sizeof(SHA_384_PKCS_ID)); + + if(name == "SHA-512") + return std::vector<uint8_t>(SHA_512_PKCS_ID, + SHA_512_PKCS_ID + sizeof(SHA_512_PKCS_ID)); + + if(name == "SHA-512-256") + return std::vector<uint8_t>(SHA_512_256_PKCS_ID, + SHA_512_256_PKCS_ID + sizeof(SHA_512_256_PKCS_ID)); + + if(name == "SHA-3(224)") + return std::vector<uint8_t>(SHA3_224_PKCS_ID, + SHA3_224_PKCS_ID + sizeof(SHA3_224_PKCS_ID)); + + if(name == "SHA-3(256)") + return std::vector<uint8_t>(SHA3_256_PKCS_ID, + SHA3_256_PKCS_ID + sizeof(SHA3_256_PKCS_ID)); + + if(name == "SHA-3(384)") + return std::vector<uint8_t>(SHA3_384_PKCS_ID, + SHA3_384_PKCS_ID + sizeof(SHA3_384_PKCS_ID)); + + if(name == "SHA-3(512)") + return std::vector<uint8_t>(SHA3_512_PKCS_ID, + SHA3_512_PKCS_ID + sizeof(SHA3_512_PKCS_ID)); + + if(name == "SM3") + return std::vector<uint8_t>(SM3_PKCS_ID, SM3_PKCS_ID + sizeof(SM3_PKCS_ID)); + + throw Invalid_Argument("No PKCS #1 identifier for " + name); + } + +/* +* HashID as specified by IEEE 1363/X9.31 +*/ +uint8_t ieee1363_hash_id(const std::string& name) + { + if(name == "SHA-160" || name == "SHA-1" || name == "SHA1") + return 0x33; + + if(name == "SHA-224") return 0x38; + if(name == "SHA-256") return 0x34; + if(name == "SHA-384") return 0x36; + if(name == "SHA-512") return 0x35; + + if(name == "RIPEMD-160") return 0x31; + + if(name == "Whirlpool") return 0x37; + + return 0; + } + +} +/* * Hex Encoding and Decoding * (C) 2010,2020 Jack Lloyd * @@ -14282,6 +15947,154 @@ std::vector<uint8_t> hex_decode(const std::string& input, } /* +* HMAC +* (C) 1999-2007,2014,2020 Jack Lloyd +* 2007 Yves Jerschow +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/* +* Update a HMAC Calculation +*/ +void HMAC::add_data(const uint8_t input[], size_t length) + { + verify_key_set(m_ikey.empty() == false); + m_hash->update(input, length); + } + +/* +* Finalize a HMAC Calculation +*/ +void HMAC::final_result(uint8_t mac[]) + { + verify_key_set(m_okey.empty() == false); + m_hash->final(mac); + m_hash->update(m_okey); + m_hash->update(mac, m_hash_output_length); + m_hash->final(mac); + m_hash->update(m_ikey); + } + +Key_Length_Specification HMAC::key_spec() const + { + // Support very long lengths for things like PBKDF2 and the TLS PRF + return Key_Length_Specification(0, 4096); + } + +size_t HMAC::output_length() const + { + return m_hash_output_length; + } + +/* +* HMAC Key Schedule +*/ +void HMAC::key_schedule(const uint8_t key[], size_t length) + { + const uint8_t ipad = 0x36; + const uint8_t opad = 0x5C; + + m_hash->clear(); + + m_ikey.resize(m_hash_block_size); + m_okey.resize(m_hash_block_size); + + clear_mem(m_ikey.data(), m_ikey.size()); + clear_mem(m_okey.data(), m_okey.size()); + + /* + * Sometimes the HMAC key length itself is sensitive, as with PBKDF2 where it + * reveals the length of the passphrase. Make some attempt to hide this to + * side channels. Clearly if the secret is longer than the block size then the + * branch to hash first reveals that. In addition, counting the number of + * compression functions executed reveals the size at the granularity of the + * hash function's block size. + * + * The greater concern is for smaller keys; being able to detect when a + * passphrase is say 4 bytes may assist choosing weaker targets. Even though + * the loop bounds are constant, we can only actually read key[0..length] so + * it doesn't seem possible to make this computation truly constant time. + * + * We don't mind leaking if the length is exactly zero since that's + * trivial to simply check. + */ + + if(length > m_hash_block_size) + { + m_hash->update(key, length); + m_hash->final(m_ikey.data()); + } + else if(length > 0) + { + for(size_t i = 0, i_mod_length = 0; i != m_hash_block_size; ++i) + { + /* + access key[i % length] but avoiding division due to variable + time computation on some processors. + */ + auto needs_reduction = CT::Mask<size_t>::is_lte(length, i_mod_length); + i_mod_length = needs_reduction.select(0, i_mod_length); + const uint8_t kb = key[i_mod_length]; + + auto in_range = CT::Mask<size_t>::is_lt(i, length); + m_ikey[i] = static_cast<uint8_t>(in_range.if_set_return(kb)); + i_mod_length += 1; + } + } + + for(size_t i = 0; i != m_hash_block_size; ++i) + { + m_ikey[i] ^= ipad; + m_okey[i] = m_ikey[i] ^ ipad ^ opad; + } + + m_hash->update(m_ikey); + } + +/* +* Clear memory of sensitive data +*/ +void HMAC::clear() + { + m_hash->clear(); + zap(m_ikey); + zap(m_okey); + } + +/* +* Return the name of this type +*/ +std::string HMAC::name() const + { + return "HMAC(" + m_hash->name() + ")"; + } + +/* +* Return a new_object of this object +*/ +std::unique_ptr<MessageAuthenticationCode> HMAC::new_object() const + { + return std::make_unique<HMAC>(m_hash->new_object()); + } + +/* +* HMAC Constructor +*/ +HMAC::HMAC(std::unique_ptr<HashFunction> hash) : + m_hash(std::move(hash)), + m_hash_output_length(m_hash->output_length()), + m_hash_block_size(m_hash->hash_block_size()) + { + BOTAN_ARG_CHECK(m_hash_block_size >= m_hash_output_length, + "HMAC is not compatible with this hash function"); + } + +} +/* * KDF Retrieval * (C) 1999-2007 Jack Lloyd * @@ -14866,6 +16679,337 @@ void mgf1_mask(HashFunction& hash, } /* +* CBC Padding Methods +* (C) 1999-2007,2013,2018,2020 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +/** +* Get a block cipher padding method by name +*/ +std::unique_ptr<BlockCipherModePaddingMethod> +BlockCipherModePaddingMethod::create(const std::string& algo_spec) + { + if(algo_spec == "NoPadding") + return std::make_unique<Null_Padding>(); + + if(algo_spec == "PKCS7") + return std::make_unique<PKCS7_Padding>(); + + if(algo_spec == "OneAndZeros") + return std::make_unique<OneAndZeros_Padding>(); + + if(algo_spec == "X9.23") + return std::make_unique<ANSI_X923_Padding>(); + + if(algo_spec == "ESP") + return std::make_unique<ESP_Padding>(); + + return nullptr; + } + +/* +* Pad with PKCS #7 Method +*/ +void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0202 + 030303 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(padding_len, buffer[i]); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with PKCS #7 Method +*/ +size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t last_byte = input[input_length-1]; + + /* + The input should == the block size so if the last byte exceeds + that then the padding is certainly invalid + */ + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Does this byte equal the expected pad byte? + const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte); + + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + bad_input |= in_range & (~pad_eq); + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ANSI X9.23 Method +*/ +void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0002 + 000003 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_zero_padding = buffer.size() - 1; + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(0, buffer[i]); + } + + buffer[buffer.size()-1] = padding_len; + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ANSI X9.23 Method +*/ +size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const size_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + const auto pad_is_nonzero = CT::Mask<size_t>::expand(input[i]); + bad_input |= pad_is_nonzero & in_range; + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with One and Zeros Method +*/ +void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 80 + 8000 + 800000 + ... + */ + + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_80 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding)); + auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding)); + buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i])); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with One and Zeros Method +*/ +size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + auto bad_input = CT::Mask<uint8_t>::cleared(); + auto seen_0x80 = CT::Mask<uint8_t>::cleared(); + + size_t pad_pos = input_length - 1; + size_t i = input_length; + + while(i) + { + const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i-1], 0x80); + const auto is_zero = CT::Mask<uint8_t>::is_zero(input[i-1]); + + seen_0x80 |= is_0x80; + pad_pos -= seen_0x80.if_not_set_return(1); + bad_input |= ~seen_0x80 & ~is_zero; + i--; + } + bad_input |= ~seen_0x80; + + CT::unpoison(input, input_length); + + return CT::Mask<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ESP Padding Method +*/ +void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0102 + 010203 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + uint8_t pad_ctr = 0x01; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(pad_ctr, buffer[i]); + pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ESP Padding Method +*/ +size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t input_length_8 = static_cast<uint8_t>(input_length); + const uint8_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) | + CT::Mask<uint8_t>::is_gt(last_byte, input_length_8); + + const uint8_t pad_pos = input_length_8 - last_byte; + size_t i = input_length_8 - 1; + while(i) + { + const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos); + const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i-1], input[i]-1); + + bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing; + --i; + } + + CT::unpoison(input, input_length); + return bad_input.select_and_unpoison(input_length_8, pad_pos); + } + + +} +/* * Cipher Modes * (C) 2015 Jack Lloyd * @@ -23702,6 +25846,797 @@ void Modular_Reducer::reduce(BigInt& t1, const BigInt& x, secure_vector<word>& w } /* +* PKCS #5 PBES2 +* (C) 1999-2008,2014,2021 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +bool known_pbes_cipher_mode(const std::string& mode) + { + return (mode == "CBC" || mode == "GCM" || mode == "SIV"); + } + +secure_vector<uint8_t> derive_key(const std::string& passphrase, + const AlgorithmIdentifier& kdf_algo, + size_t default_key_size) + { + if(kdf_algo.get_oid() == OID::from_string("PKCS5.PBKDF2")) + { + secure_vector<uint8_t> salt; + size_t iterations = 0, key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_parameters()) + .start_sequence() + .decode(salt, ASN1_Type::OctetString) + .decode(iterations) + .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal) + .decode_optional(prf_algo, ASN1_Type::Sequence, ASN1_Class::Constructed, + AlgorithmIdentifier("HMAC(SHA-160)", + AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons(); + + if(salt.size() < 8) + throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); + + if(key_length == 0) + key_length = default_key_size; + + const std::string prf = OIDS::oid2str_or_throw(prf_algo.get_oid()); + auto pbkdf_fam = PasswordHashFamily::create_or_throw("PBKDF2(" + prf + ")"); + auto pbkdf = pbkdf_fam->from_params(iterations); + + secure_vector<uint8_t> derived_key(key_length); + pbkdf->derive_key(derived_key.data(), derived_key.size(), + passphrase.data(), passphrase.size(), + salt.data(), salt.size()); + return derived_key; + } + else if(kdf_algo.get_oid() == OID::from_string("Scrypt")) + { + secure_vector<uint8_t> salt; + size_t N = 0, r = 0, p = 0; + size_t key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_parameters()) + .start_sequence() + .decode(salt, ASN1_Type::OctetString) + .decode(N) + .decode(r) + .decode(p) + .decode_optional(key_length, ASN1_Type::Integer, ASN1_Class::Universal) + .end_cons(); + + if(key_length == 0) + key_length = default_key_size; + + secure_vector<uint8_t> derived_key(key_length); + + auto pwdhash_fam = PasswordHashFamily::create_or_throw("Scrypt"); + auto pwdhash = pwdhash_fam->from_params(N, r, p); + pwdhash->derive_key(derived_key.data(), derived_key.size(), + passphrase.data(), passphrase.size(), + salt.data(), salt.size()); + + return derived_key; + } + else + throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " + + kdf_algo.get_oid().to_string()); + } + +secure_vector<uint8_t> derive_key(const std::string& passphrase, + const std::string& digest, + RandomNumberGenerator& rng, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + size_t key_length, + AlgorithmIdentifier& kdf_algo) + { + const secure_vector<uint8_t> salt = rng.random_vec(12); + + if(digest == "Scrypt") + { + auto pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt"); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + const size_t N = pwhash->memory_param(); + const size_t r = pwhash->iterations(); + const size_t p = pwhash->parallelism(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = 0; + + std::vector<uint8_t> scrypt_params; + DER_Encoder(scrypt_params) + .start_sequence() + .encode(salt, ASN1_Type::OctetString) + .encode(N) + .encode(r) + .encode(p) + .encode(key_length) + .end_cons(); + + kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params); + return key; + } + else + { + const std::string prf = "HMAC(" + digest + ")"; + const std::string pbkdf_name = "PBKDF2(" + prf + ")"; + + std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create(pbkdf_name); + if(!pwhash_fam) + throw Invalid_Argument("Unknown password hash digest " + digest); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + std::vector<uint8_t> pbkdf2_params; + + const size_t iterations = pwhash->iterations(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = iterations; + + DER_Encoder(pbkdf2_params) + .start_sequence() + .encode(salt, ASN1_Type::OctetString) + .encode(iterations) + .encode(key_length) + .encode_if(prf != "HMAC(SHA-160)", + AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons(); + + kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params); + return key; + } + } + +/* +* PKCS#5 v2.0 PBE Encryption +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_shared(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + const std::string& cipher, + const std::string& prf, + RandomNumberGenerator& rng) + { + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Encoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + + if(!known_pbes_cipher_mode(cipher_spec[1])) + throw Encoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + const OID cipher_oid = OIDS::str2oid_or_empty(cipher); + if(cipher_oid.empty()) + throw Encoding_Error("PBE-PKCS5 v2.0: No OID assigned for " + cipher); + + std::unique_ptr<Cipher_Mode> enc = Cipher_Mode::create(cipher, ENCRYPTION); + + if(!enc) + throw Decoding_Error("PBE-PKCS5 cannot encrypt no cipher " + cipher); + + const size_t key_length = enc->key_spec().maximum_keylength(); + + const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length()); + + AlgorithmIdentifier kdf_algo; + + const secure_vector<uint8_t> derived_key = + derive_key(passphrase, prf, rng, + msec_in_iterations_out, iterations_if_msec_null, + key_length, kdf_algo); + + enc->set_key(derived_key); + enc->start(iv); + secure_vector<uint8_t> ctext = key_bits; + enc->finish(ctext); + + std::vector<uint8_t> encoded_iv; + DER_Encoder(encoded_iv).encode(iv, ASN1_Type::OctetString); + + std::vector<uint8_t> pbes2_params; + DER_Encoder(pbes2_params) + .start_sequence() + .encode(kdf_algo) + .encode(AlgorithmIdentifier(cipher, encoded_iv)) + .end_cons(); + + AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params); + + return std::make_pair(id, unlock(ctext)); + } + +} + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + // return value msec_in_iterations_out discarded + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + + auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + + if(out_iterations_if_nonnull) + *out_iterations_if_nonnull = msec_in_iterations_out; + + return ret; + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t pbkdf_iter, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng); + } + +secure_vector<uint8_t> +pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params) + { + AlgorithmIdentifier kdf_algo, enc_algo; + + BER_Decoder(params) + .start_sequence() + .decode(kdf_algo) + .decode(enc_algo) + .end_cons(); + + const std::string cipher = OIDS::oid2str_or_throw(enc_algo.get_oid()); + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + if(!known_pbes_cipher_mode(cipher_spec[1])) + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + secure_vector<uint8_t> iv; + BER_Decoder(enc_algo.get_parameters()).decode(iv, ASN1_Type::OctetString).verify_end(); + + std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, DECRYPTION); + if(!dec) + throw Decoding_Error("PBE-PKCS5 cannot decrypt no cipher " + cipher); + + dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength())); + + dec->start(iv); + + secure_vector<uint8_t> buf = key_bits; + dec->finish(buf); + + return buf; + } + +} +/* +* PBKDF +* (C) 2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_PBKDF2) +#endif + +#if defined(BOTAN_HAS_PGP_S2K) +#endif + +namespace Botan { + +std::unique_ptr<PBKDF> PBKDF::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + // TODO OpenSSL + + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::make_unique<PKCS5_PBKDF2>(mac.release()); + + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::make_unique<PKCS5_PBKDF2>(mac.release()); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + return std::make_unique<OpenPGP_S2K>(hash.release()); + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PBKDF> +PBKDF::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PBKDF::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PBKDF", algo, provider); + } + +std::vector<std::string> PBKDF::providers(const std::string& algo_spec) + { + return probe_providers_of<PBKDF>(algo_spec); + } + +void PBKDF::pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + iterations = pbkdf(out, out_len, passphrase, salt, salt_len, 0, msec); + } + +void PBKDF::pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + if(iterations == 0) + throw Invalid_Argument(name() + ": Invalid iteration count"); + + const size_t iterations_run = pbkdf(out, out_len, passphrase, + salt, salt_len, iterations, + std::chrono::milliseconds(0)); + BOTAN_ASSERT_EQUAL(iterations, iterations_run, "Expected PBKDF iterations"); + } + +secure_vector<uint8_t> PBKDF::pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_iterations(out.data(), out_len, passphrase, salt, salt_len, iterations); + return out; + } + +secure_vector<uint8_t> PBKDF::pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + secure_vector<uint8_t> out(out_len); + pbkdf_timed(out.data(), out_len, passphrase, salt, salt_len, msec, iterations); + return out; + } + +} +/* +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +#if defined(BOTAN_HAS_PBKDF2) +#endif + +#if defined(BOTAN_HAS_PGP_S2K) +#endif + +#if defined(BOTAN_HAS_SCRYPT) +#endif + +#if defined(BOTAN_HAS_ARGON2) +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) +#endif + +namespace Botan { + +void PasswordHash::derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t ad[], size_t ad_len, + const uint8_t key[], size_t key_len) const + { + BOTAN_UNUSED(ad, key); + + if(ad_len == 0 && key_len == 0) + return this->derive_key(out, out_len, + password, password_len, + salt, salt_len); + else + throw Not_Implemented("PasswordHash " + this->to_string() + " does not support AD or key"); + } + +std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec, + const std::string& provider) + { + const SCAN_Name req(algo_spec); + +#if defined(BOTAN_HAS_PBKDF2) + if(req.algo_name() == "PBKDF2") + { + if(provider.empty() || provider == "base") + { + if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")")) + return std::make_unique<PBKDF2_Family>(mac.release()); + + if(auto mac = MessageAuthenticationCode::create(req.arg(0))) + return std::make_unique<PBKDF2_Family>(mac.release()); + } + + return nullptr; + } +#endif + +#if defined(BOTAN_HAS_SCRYPT) + if(req.algo_name() == "Scrypt") + { + return std::make_unique<Scrypt_Family>(); + } +#endif + +#if defined(BOTAN_HAS_ARGON2) + if(req.algo_name() == "Argon2d") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(0)); + } + else if(req.algo_name() == "Argon2i") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(1)); + } + else if(req.algo_name() == "Argon2id") + { + return std::make_unique<Argon2_Family>(static_cast<uint8_t>(2)); + } +#endif + +#if defined(BOTAN_HAS_PBKDF_BCRYPT) + if(req.algo_name() == "Bcrypt-PBKDF") + { + return std::make_unique<Bcrypt_PBKDF_Family>(); + } +#endif + +#if defined(BOTAN_HAS_PGP_S2K) + if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1) + { + if(auto hash = HashFunction::create(req.arg(0))) + { + return std::make_unique<RFC4880_S2K_Family>(hash.release()); + } + } +#endif + + BOTAN_UNUSED(req); + BOTAN_UNUSED(provider); + + return nullptr; + } + +//static +std::unique_ptr<PasswordHashFamily> +PasswordHashFamily::create_or_throw(const std::string& algo, + const std::string& provider) + { + if(auto pbkdf = PasswordHashFamily::create(algo, provider)) + { + return pbkdf; + } + throw Lookup_Error("PasswordHashFamily", algo, provider); + } + +std::vector<std::string> PasswordHashFamily::providers(const std::string& algo_spec) + { + return probe_providers_of<PasswordHashFamily>(algo_spec); + } + +} +/* +* PBKDF2 +* (C) 1999-2007 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + + +namespace Botan { + +namespace { + +void pbkdf2_set_key(MessageAuthenticationCode& prf, + const char* password, + size_t password_len) + { + try + { + prf.set_key(cast_char_ptr_to_uint8(password), password_len); + } + catch(Invalid_Key_Length&) + { + throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size"); + } + } + +} + +size_t +pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) + { + if(iterations == 0) + { + iterations = PBKDF2(prf, out_len, msec).iterations(); + } + + PBKDF2 pbkdf2(prf, iterations); + + pbkdf2.derive_key(out, out_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +namespace { + +size_t tune_pbkdf2(MessageAuthenticationCode& prf, + size_t output_length, + uint32_t msec) + { + if(output_length == 0) + output_length = 1; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + secure_vector<uint8_t> U(prf_sz); + + const size_t trial_iterations = 2000; + + // Short output ensures we only need a single PBKDF2 block + + Timer timer("PBKDF2"); + + const auto tune_time = BOTAN_PBKDF_TUNING_TIME; + + prf.set_key(nullptr, 0); + + timer.run_until_elapsed(tune_time, [&]() { + uint8_t out[12] = { 0 }; + uint8_t salt[12] = { 0 }; + pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations); + }); + + if(timer.events() == 0) + return trial_iterations; + + const uint64_t duration_nsec = timer.value() / timer.events(); + + const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000; + + if(duration_nsec > desired_nsec) + return trial_iterations; + + const size_t blocks_needed = (output_length + prf_sz - 1) / prf_sz; + + const size_t multiplier = static_cast<size_t>(desired_nsec / duration_nsec / blocks_needed); + + if(multiplier == 0) + return trial_iterations; + else + return trial_iterations * multiplier; + } + +} + +void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const uint8_t salt[], + size_t salt_len, + size_t iterations) + { + if(iterations == 0) + throw Invalid_Argument("PBKDF2: Invalid iteration count"); + + clear_mem(out, out_len); + + if(out_len == 0) + return; + + const size_t prf_sz = prf.output_length(); + BOTAN_ASSERT_NOMSG(prf_sz > 0); + + secure_vector<uint8_t> U(prf_sz); + + uint32_t counter = 1; + while(out_len) + { + const size_t prf_output = std::min<size_t>(prf_sz, out_len); + + prf.update(salt, salt_len); + prf.update_be(counter++); + prf.final(U.data()); + + xor_buf(out, U.data(), prf_output); + + for(size_t i = 1; i != iterations; ++i) + { + prf.update(U); + prf.final(U.data()); + xor_buf(out, U.data(), prf_output); + } + + out_len -= prf_output; + out += prf_output; + } + } + +// PBKDF interface +size_t +PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len, + const std::string& password, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const + { + if(iterations == 0) + { + iterations = PBKDF2(*m_mac, key_len, msec).iterations(); + } + + PBKDF2 pbkdf2(*m_mac, iterations); + + pbkdf2.derive_key(key, key_len, + password.c_str(), password.size(), + salt, salt_len); + + return iterations; + } + +std::string PKCS5_PBKDF2::name() const + { + return "PBKDF2(" + m_mac->name() + ")"; + } + +std::unique_ptr<PBKDF> PKCS5_PBKDF2::new_object() const + { + return std::make_unique<PKCS5_PBKDF2>(m_mac->clone()); + } + +// PasswordHash interface + +PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) : + m_prf(prf.new_object()), + m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count()))) + {} + +std::string PBKDF2::to_string() const + { + return "PBKDF2(" + m_prf->name() + "," + std::to_string(m_iterations) + ")"; + } + +void PBKDF2::derive_key(uint8_t out[], size_t out_len, + const char* password, const size_t password_len, + const uint8_t salt[], size_t salt_len) const + { + pbkdf2_set_key(*m_prf, password, password_len); + pbkdf2(*m_prf, out, out_len, salt, salt_len, m_iterations); + } + +std::string PBKDF2_Family::name() const + { + return "PBKDF2(" + m_prf->name() + ")"; + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t /*max_memory_usage_mb*/) const + { + return std::make_unique<PBKDF2>(*m_prf, output_len, msec); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const + { + return std::make_unique<PBKDF2>(*m_prf, 150000); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_params(size_t iter, size_t /*i2*/, size_t /*i3*/) const + { + return std::make_unique<PBKDF2>(*m_prf, iter); + } + +std::unique_ptr<PasswordHash> PBKDF2_Family::from_iterations(size_t iter) const + { + return std::make_unique<PBKDF2>(*m_prf, iter); + } + +} +/* * PEM Encoding/Decoding * (C) 1999-2007 Jack Lloyd * diff --git a/include/amalgamation-armhf/botan_all.h b/include/amalgamation-armhf/botan_all.h index a263b6a..4e9a6cc 100644 --- a/include/amalgamation-armhf/botan_all.h +++ b/include/amalgamation-armhf/botan_all.h @@ -31,7 +31,7 @@ * Build configuration for Botan 3.0.0-alpha0 * * Automatically generated from -* 'configure.py --cpu=armhf --disable-neon --prefix=/usr/local/projects/zafena/elevator/botan/dist-armhf-min --minimized-build --enable-modules=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,chacha,chacha20poly1305,aead,stream,sha1,sha2_32,system_rng,simd,chacha_simd32,chacha_avx2,simd_avx2 --cxxflags= --ldflags= --amalgamation --with-doxygen' +* 'configure.py --cpu=armhf --disable-neon --prefix=/usr/local/projects/zafena/cipherpack/botan/dist-armhf-min --minimized-build --enable-modules=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,emsa_raw,pbes2,eme_pkcs1,emsa_pkcs1,chacha,chacha20poly1305,aead,stream,sha1,sha2_32,system_rng,simd,chacha_simd32,chacha_avx2,simd_avx2 --cxxflags= --ldflags= --amalgamation --with-doxygen' * * Target * - Compiler: g++ -fstack-protector -pthread -std=c++17 -D_REENTRANT -O3 @@ -57,9 +57,9 @@ #define BOTAN_MP_WORD_BITS 32 -#define BOTAN_INSTALL_PREFIX R"(/usr/local/projects/zafena/elevator/botan/dist-armhf-min)" +#define BOTAN_INSTALL_PREFIX R"(/usr/local/projects/zafena/cipherpack/botan/dist-armhf-min)" #define BOTAN_INSTALL_HEADER_DIR R"(include/botan-3)" -#define BOTAN_INSTALL_LIB_DIR R"(/usr/local/projects/zafena/elevator/botan/dist-armhf-min/lib)" +#define BOTAN_INSTALL_LIB_DIR R"(/usr/local/projects/zafena/cipherpack/botan/dist-armhf-min/lib)" #define BOTAN_LIB_LINK "" #define BOTAN_LINK_FLAGS "-fstack-protector -pthread" @@ -110,24 +110,37 @@ #define BOTAN_HAS_BASE64_CODEC 20131128 #define BOTAN_HAS_BIGINT 20210423 #define BOTAN_HAS_BIGINT_MP 20151225 +#define BOTAN_HAS_BLOCK_CIPHER 20131128 #define BOTAN_HAS_CHACHA 20180807 #define BOTAN_HAS_CIPHER_MODES 20180124 +#define BOTAN_HAS_CIPHER_MODE_PADDING 20131128 #define BOTAN_HAS_CPUID 20170917 #define BOTAN_HAS_EME_OAEP 20180305 +#define BOTAN_HAS_EME_PKCS1 20190426 +#define BOTAN_HAS_EME_PKCS1v15 20131128 #define BOTAN_HAS_EME_RAW 20150313 #define BOTAN_HAS_EMSA1 20131128 +#define BOTAN_HAS_EMSA_PKCS1 20140118 #define BOTAN_HAS_EMSA_PSSR 20131128 +#define BOTAN_HAS_EMSA_RAW 20131128 #define BOTAN_HAS_ENTROPY_SOURCE 20151120 #define BOTAN_HAS_HASH 20180112 +#define BOTAN_HAS_HASH_ID 20131128 #define BOTAN_HAS_HEX_CODEC 20131128 +#define BOTAN_HAS_HMAC 20131128 #define BOTAN_HAS_KDF_BASE 20131128 #define BOTAN_HAS_MAC 20150626 #define BOTAN_HAS_MDX_HASH_FUNCTION 20131128 #define BOTAN_HAS_MGF1 20140118 #define BOTAN_HAS_MODES 20150626 +#define BOTAN_HAS_MODE_CBC 20131128 #define BOTAN_HAS_NUMBERTHEORY 20201108 #define BOTAN_HAS_OCSP 20201106 +#define BOTAN_HAS_PASSWORD_HASHING 20210419 +#define BOTAN_HAS_PBKDF 20180902 +#define BOTAN_HAS_PBKDF2 20180902 #define BOTAN_HAS_PEM_CODEC 20131128 +#define BOTAN_HAS_PKCS5_PBES2 20141119 #define BOTAN_HAS_PK_PADDING 20131128 #define BOTAN_HAS_POLY1305 20141227 #define BOTAN_HAS_PUBLIC_KEY_CRYPTO 20131128 @@ -4476,6 +4489,249 @@ inline void swap<Botan::BigInt>(Botan::BigInt& x, Botan::BigInt& y) namespace Botan { /** +* This class represents a block cipher object. +*/ +class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm + { + public: + + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<BlockCipher> + create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<BlockCipher> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + * @param algo_spec algorithm name + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * @return block size of this algorithm + */ + virtual size_t block_size() const = 0; + + /** + * @return native parallelism of this cipher in blocks + */ + virtual size_t parallelism() const { return 1; } + + /** + * @return prefererred parallelism of this cipher in bytes + */ + size_t parallel_bytes() const + { + return parallelism() * block_size() * BOTAN_BLOCK_CIPHER_PAR_MULT; + } + + /** + * @return provider information about this implementation. Default is "base", + * might also return "sse2", "avx2", "openssl", or some other arbitrary string. + */ + virtual std::string provider() const { return "base"; } + + /** + * Encrypt a block. + * @param in The plaintext block to be encrypted as a byte array. + * Must be of length block_size(). + * @param out The byte array designated to hold the encrypted block. + * Must be of length block_size(). + */ + void encrypt(const uint8_t in[], uint8_t out[]) const + { encrypt_n(in, out, 1); } + + /** + * Decrypt a block. + * @param in The ciphertext block to be decypted as a byte array. + * Must be of length block_size(). + * @param out The byte array designated to hold the decrypted block. + * Must be of length block_size(). + */ + void decrypt(const uint8_t in[], uint8_t out[]) const + { decrypt_n(in, out, 1); } + + /** + * Encrypt a block. + * @param block the plaintext block to be encrypted + * Must be of length block_size(). Will hold the result when the function + * has finished. + */ + void encrypt(uint8_t block[]) const { encrypt_n(block, block, 1); } + + /** + * Decrypt a block. + * @param block the ciphertext block to be decrypted + * Must be of length block_size(). Will hold the result when the function + * has finished. + */ + void decrypt(uint8_t block[]) const { decrypt_n(block, block, 1); } + + /** + * Encrypt one or more blocks + * @param block the input/output buffer (multiple of block_size()) + */ + template<typename Alloc> + void encrypt(std::vector<uint8_t, Alloc>& block) const + { + return encrypt_n(block.data(), block.data(), block.size() / block_size()); + } + + /** + * Decrypt one or more blocks + * @param block the input/output buffer (multiple of block_size()) + */ + template<typename Alloc> + void decrypt(std::vector<uint8_t, Alloc>& block) const + { + return decrypt_n(block.data(), block.data(), block.size() / block_size()); + } + + /** + * Encrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + */ + template<typename Alloc, typename Alloc2> + void encrypt(const std::vector<uint8_t, Alloc>& in, + std::vector<uint8_t, Alloc2>& out) const + { + return encrypt_n(in.data(), out.data(), in.size() / block_size()); + } + + /** + * Decrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + */ + template<typename Alloc, typename Alloc2> + void decrypt(const std::vector<uint8_t, Alloc>& in, + std::vector<uint8_t, Alloc2>& out) const + { + return decrypt_n(in.data(), out.data(), in.size() / block_size()); + } + + /** + * Encrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + * @param blocks the number of blocks to process + */ + virtual void encrypt_n(const uint8_t in[], uint8_t out[], + size_t blocks) const = 0; + + /** + * Decrypt one or more blocks + * @param in the input buffer (multiple of block_size()) + * @param out the output buffer (same size as in) + * @param blocks the number of blocks to process + */ + virtual void decrypt_n(const uint8_t in[], uint8_t out[], + size_t blocks) const = 0; + + virtual void encrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const + { + const size_t BS = block_size(); + xor_buf(data, mask, blocks * BS); + encrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + virtual void decrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const + { + const size_t BS = block_size(); + xor_buf(data, mask, blocks * BS); + decrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + /** + * @return new object representing the same algorithm as *this + */ + virtual std::unique_ptr<BlockCipher> new_object() const = 0; + + BlockCipher* clone() const + { + return this->new_object().release(); + } + + virtual ~BlockCipher() = default; + }; + +/** +* Tweakable block ciphers allow setting a tweak which is a non-keyed +* value which affects the encryption/decryption operation. +*/ +class BOTAN_PUBLIC_API(2,8) Tweakable_Block_Cipher : public BlockCipher + { + public: + /** + * Set the tweak value. This must be called after setting a key. The value + * persists until either set_tweak, set_key, or clear is called. + * Different algorithms support different tweak length(s). If called with + * an unsupported length, Invalid_Argument will be thrown. + */ + virtual void set_tweak(const uint8_t tweak[], size_t len) = 0; + }; + +/** +* Represents a block cipher with a single fixed block size +*/ +template<size_t BS, size_t KMIN, size_t KMAX = 0, size_t KMOD = 1, typename BaseClass = BlockCipher> +class Block_Cipher_Fixed_Params : public BaseClass + { + public: + enum { BLOCK_SIZE = BS }; + size_t block_size() const final override { return BS; } + + // override to take advantage of compile time constant block size + void encrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const final override + { + xor_buf(data, mask, blocks * BS); + this->encrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + void decrypt_n_xex(uint8_t data[], + const uint8_t mask[], + size_t blocks) const final override + { + xor_buf(data, mask, blocks * BS); + this->decrypt_n(data, data, blocks); + xor_buf(data, mask, blocks * BS); + } + + Key_Length_Specification key_spec() const final override + { + return Key_Length_Specification(KMIN, KMAX, KMOD); + } + }; + +} + +namespace Botan { + +/** * This class represents any kind of computation which uses an internal * state, such as hash functions or MACs */ @@ -7967,6 +8223,517 @@ BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID& oid); namespace Botan { +/** +* Base class for PBKDF (password based key derivation function) +* implementations. Converts a password into a key using a salt +* and iterated hashing to make brute force attacks harder. +* +* Starting in 2.8 this functionality is also offered by PasswordHash. +* The PBKDF interface may be removed in a future release. +*/ +class BOTAN_PUBLIC_API(2,0) PBKDF + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PBKDF> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PBKDF> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * @return new instance of this same algorithm + */ + virtual std::unique_ptr<PBKDF> new_object() const = 0; + + /** + * @return new instance of this same algorithm + */ + PBKDF* clone() const + { + return this->new_object().release(); + } + + /** + * @return name of this PBKDF + */ + virtual std::string name() const = 0; + + virtual ~PBKDF() = default; + + /** + * Derive a key from a passphrase for a number of iterations + * specified by either iterations or if iterations == 0 then + * running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @return the number of iterations performed + */ + virtual size_t pbkdf(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const = 0; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + void pbkdf_iterations(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + */ + void pbkdf_timed(uint8_t out[], size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + /** + * Derive a key from a passphrase for a number of iterations. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_iterations(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase, running until msec time has elapsed. + * + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec if iterations is zero, then instead the PBKDF is + * run until msec milliseconds has passed. + * @param iterations set to the number iterations executed + * @return the derived key + */ + secure_vector<uint8_t> pbkdf_timed(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + // Following kept for compat with 1.10: + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt, salt_len, iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param iterations the number of iterations to use (use 10K or more) + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + size_t iterations) const + { + return pbkdf_iterations(out_len, passphrase, salt.data(), salt.size(), iterations); + } + + /** + * Derive a key from a passphrase + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt, salt_len, msec, iterations); + } + + /** + * Derive a key from a passphrase using a certain amount of time + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + template<typename Alloc> + OctetString derive_key(size_t out_len, + const std::string& passphrase, + const std::vector<uint8_t, Alloc>& salt, + std::chrono::milliseconds msec, + size_t& iterations) const + { + return pbkdf_timed(out_len, passphrase, salt.data(), salt.size(), msec, iterations); + } + }; + +/* +* Compatibility typedef +*/ +typedef PBKDF S2K; + +/** +* Password based key derivation function factory method +* @param algo_spec the name of the desired PBKDF algorithm +* @param provider the provider to use +* @return pointer to newly allocated object of that type +*/ +inline PBKDF* get_pbkdf(const std::string& algo_spec, + const std::string& provider = "") + { + return PBKDF::create_or_throw(algo_spec, provider).release(); + } + +inline PBKDF* get_s2k(const std::string& algo_spec) + { + return get_pbkdf(algo_spec); + } + + +} + +namespace Botan { + +/** +* Base class for password based key derivation functions. +* +* Converts a password into a key using a salt and iterated hashing to +* make brute force attacks harder. +*/ +class BOTAN_PUBLIC_API(2,8) PasswordHash + { + public: + virtual ~PasswordHash() = default; + + virtual std::string to_string() const = 0; + + /** + * Most password hashes have some notion of iterations. + */ + virtual size_t iterations() const = 0; + + /** + * Some password hashing algorithms have a parameter which controls how + * much memory is used. If not supported by some algorithm, returns 0. + */ + virtual size_t memory_param() const { return 0; } + + /** + * Some password hashing algorithms have a parallelism parameter. + * If the algorithm does not support this notion, then the + * function returns zero. This allows distinguishing between a + * password hash which just does not support parallel operation, + * vs one that does support parallel operation but which has been + * configured to use a single lane. + */ + virtual size_t parallelism() const { return 0; } + + /** + * Returns an estimate of the total number of bytes required to perform this + * key derivation. + * + * If this algorithm uses a small and constant amount of memory, with no + * effort made towards being memory hard, this function returns 0. + */ + virtual size_t total_memory_usage() const { return 0; } + + /** + * Derive a key from a password + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const = 0; + + /** + * Derive a key from a password plus additional data and/or a secret key + * + * Currently this is only supported for Argon2. Using a non-empty AD or key + * with other algorithms will cause a Not_Implemented exception. + * + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param password the password to derive the key from + * @param password_len the length of password in bytes + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param ad some additional data + * @param ad_len length of ad in bytes + * @param key a secret key + * @param key_len length of key in bytes + * + * This function is const, but is not thread safe. Different threads should + * either use unique objects, or serialize all access. + */ + virtual void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len, + const uint8_t ad[], size_t ad_len, + const uint8_t key[], size_t key_len) const; + }; + +class BOTAN_PUBLIC_API(2,8) PasswordHashFamily + { + public: + /** + * Create an instance based on a name + * If provider is empty then best available is chosen. + * @param algo_spec algorithm name + * @param provider provider implementation to choose + * @return a null pointer if the algo/provider combination cannot be found + */ + static std::unique_ptr<PasswordHashFamily> create(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * Create an instance based on a name, or throw if the + * algo/provider combination cannot be found. If provider is + * empty then best available is chosen. + */ + static std::unique_ptr<PasswordHashFamily> + create_or_throw(const std::string& algo_spec, + const std::string& provider = ""); + + /** + * @return list of available providers for this algorithm, empty if not available + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + virtual ~PasswordHashFamily() = default; + + /** + * @return name of this PasswordHash + */ + virtual std::string name() const = 0; + + /** + * Return a new parameter set tuned for this machine + * @param output_length how long the output length will be + * @param msec the desired execution time in milliseconds + * + * @param max_memory_usage_mb some password hash functions can use a tunable + * amount of memory, in this case max_memory_usage limits the amount of RAM + * the returned parameters will require, in mebibytes (2**20 bytes). It may + * require some small amount above the request. Set to zero to place no + * limit at all. + */ + virtual std::unique_ptr<PasswordHash> tune(size_t output_length, + std::chrono::milliseconds msec, + size_t max_memory_usage_mb = 0) const = 0; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + virtual std::unique_ptr<PasswordHash> default_params() const = 0; + + /** + * Return a parameter chosen based on a rough approximation with the + * specified iteration count. The exact value this returns for a particular + * algorithm may change from over time. Think of it as an alternative to + * tune, where time is expressed in terms of PBKDF2 iterations rather than + * milliseconds. + */ + virtual std::unique_ptr<PasswordHash> from_iterations(size_t iterations) const = 0; + + /** + * Create a password hash using some scheme specific format. Parameters are as follows: + * - For PBKDF2, PGP-S2K, and Bcrypt-PBKDF, i1 is iterations + * - Scrypt uses N, r, p for i{1-3} + * - Argon2 family uses memory (in KB), iterations, and parallelism for i{1-3} + * + * All unneeded parameters should be set to 0 or left blank. + */ + virtual std::unique_ptr<PasswordHash> from_params( + size_t i1, + size_t i2 = 0, + size_t i3 = 0) const = 0; + }; + +} + +BOTAN_FUTURE_INTERNAL_HEADER(pbkdf2.h) + +namespace Botan { + +BOTAN_PUBLIC_API(2,0) size_t pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], + size_t out_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec); + +/** +* Perform PBKDF2. The prf is assumed to be keyed already. +*/ +BOTAN_PUBLIC_API(2,8) void pbkdf2(MessageAuthenticationCode& prf, + uint8_t out[], size_t out_len, + const uint8_t salt[], size_t salt_len, + size_t iterations); + +/** +* PBKDF2 +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2 final : public PasswordHash + { + public: + PBKDF2(const MessageAuthenticationCode& prf, size_t iter) : + m_prf(prf.new_object()), + m_iterations(iter) + {} + + PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec); + + size_t iterations() const override { return m_iterations; } + + std::string to_string() const override; + + void derive_key(uint8_t out[], size_t out_len, + const char* password, size_t password_len, + const uint8_t salt[], size_t salt_len) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + size_t m_iterations; + }; + +/** +* Family of PKCS #5 PBKDF2 operations +*/ +class BOTAN_PUBLIC_API(2,8) PBKDF2_Family final : public PasswordHashFamily + { + public: + PBKDF2_Family(MessageAuthenticationCode* prf) : m_prf(prf) {} + + std::string name() const override; + + std::unique_ptr<PasswordHash> tune(size_t output_len, + std::chrono::milliseconds msec, + size_t max_memory) const override; + + /** + * Return some default parameter set for this PBKDF that should be good + * enough for most users. The value returned may change over time as + * processing power and attacks improve. + */ + std::unique_ptr<PasswordHash> default_params() const override; + + std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override; + + std::unique_ptr<PasswordHash> from_params( + size_t iter, size_t, size_t) const override; + private: + std::unique_ptr<MessageAuthenticationCode> m_prf; + }; + +/** +* PKCS #5 PBKDF2 (old interface) +*/ +class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF2 final : public PBKDF + { + public: + std::string name() const override; + + std::unique_ptr<PBKDF> new_object() const override; + + size_t pbkdf(uint8_t output_buf[], size_t output_len, + const std::string& passphrase, + const uint8_t salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; + + /** + * Create a PKCS #5 instance using the specified message auth code + * @param mac_fn the MAC object to use as PRF + */ + explicit PKCS5_PBKDF2(MessageAuthenticationCode* mac_fn) : m_mac(mac_fn) {} + private: + std::unique_ptr<MessageAuthenticationCode> m_mac; + }; + +} + +namespace Botan { + class DataSource; namespace PEM_Code { diff --git a/scripts/build-botan.sh b/scripts/build-botan.sh index 62c5aa3..fea95a5 100644 --- a/scripts/build-botan.sh +++ b/scripts/build-botan.sh @@ -26,8 +26,8 @@ MOD_HASH=sha1,sha2_32 #MOD_CIPHER=aes,gcm,chacha,chacha20poly1305,aead,stream MOD_CIPHER=chacha,chacha20poly1305,aead,stream # MOD_COMPRESSION=lzma,bzip2 -#MOD_BASIC=base,cryptobox,pubkey,rsa,x509,eme_oaep,eme_raw -MOD_BASIC=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1 +#MOD_BASIC=base,cryptobox,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,emsa_raw,pbes2,eme_pkcs1,emsa_pkcs1 +MOD_BASIC=base,pubkey,rsa,x509,eme_oaep,eme_raw,emsa1,emsa_raw,pbes2,eme_pkcs1,emsa_pkcs1 case "$archabi" in "armhf") |