diff options
Diffstat (limited to 'src')
25 files changed, 1064 insertions, 80 deletions
diff --git a/src/build-data/botan.doxy.in b/src/build-data/botan.doxy.in index c3261a7a9..743a6e0b4 100644 --- a/src/build-data/botan.doxy.in +++ b/src/build-data/botan.doxy.in @@ -157,6 +157,7 @@ PREDEFINED = BOTAN_HAS_AES_ARMV8 \ BOTAN_HAS_AES_POWER8 \ BOTAN_HAS_AES_SSSE3 \ BOTAN_HAS_CHACHA_SSE2 \ + BOTAN_HAS_CHACHA_AVX2 \ BOTAN_HAS_IDEA_SSE2 \ BOTAN_HAS_NOEKEON_SIMD \ BOTAN_HAS_SERPENT_SIMD \ diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index f7e9da1cf..80fe6591b 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -277,6 +277,8 @@ class Summary final std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg) { + const size_t MAX_BUF_SIZE = 64*1024*1024; + std::set<size_t> buf; for(std::string size_str : Botan::split_on(cmdline_arg, ',')) { @@ -294,8 +296,11 @@ std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg) throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size"); } - if(x == 0 || x > 16*1024*1024) - throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size"); + if(x == 0) + throw CLI_Usage_Error("Cannot have a zero-sized buffer"); + + if(x > MAX_BUF_SIZE) + throw CLI_Usage_Error("Specified buffer size is too large"); buf.insert(x); } @@ -513,9 +518,9 @@ class Speed final : public Command } #endif #if defined(BOTAN_HAS_CIPHER_MODES) - else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::ENCRYPTION)) + else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::ENCRYPTION, provider)) { - auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::DECRYPTION); + auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::DECRYPTION, provider); bench_cipher_mode(*enc, *dec, msec, buf_sizes); } #endif diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index c7bb134e2..aef8e2512 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -31,6 +31,31 @@ namespace Botan_CLI { +class CLI_Policy : public Botan::TLS::Policy + { + public: + + CLI_Policy(Botan::TLS::Protocol_Version req_version) : m_version(req_version) {} + + std::vector<std::string> allowed_ciphers() const override + { + // Allow CBC mode only in versions which don't support AEADs + if(m_version.supports_aead_modes() == false) + { + return { "AES-256", "AES-128" }; + } + + return Botan::TLS::Policy::allowed_ciphers(); + } + + bool allow_tls10() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V10; } + bool allow_tls11() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V11; } + bool allow_tls12() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V12; } + + private: + Botan::TLS::Protocol_Version m_version; + }; + class TLS_Client final : public Command, public Botan::TLS::Callbacks { public: @@ -101,11 +126,6 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks policy.reset(new Botan::TLS::Text_Policy(policy_stream)); } - if(!policy) - { - policy.reset(new Botan::TLS::Policy); - } - if(transport != "tcp" && transport != "udp") { throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS"); @@ -115,19 +135,35 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks const std::vector<std::string> protocols_to_offer = Botan::split_on(next_protos, ','); - m_sockfd = connect_to_host(host, port, use_tcp); - - using namespace std::placeholders; - - auto version = policy->latest_supported_version(!use_tcp); + Botan::TLS::Protocol_Version version = + use_tcp ? Botan::TLS::Protocol_Version::TLS_V12 : Botan::TLS::Protocol_Version::DTLS_V12; if(flag_set("tls1.0")) { version = Botan::TLS::Protocol_Version::TLS_V10; + if(!policy) + policy.reset(new CLI_Policy(version)); } else if(flag_set("tls1.1")) { version = Botan::TLS::Protocol_Version::TLS_V11; + if(!policy) + policy.reset(new CLI_Policy(version)); + } + else if(flag_set("tls1.2")) + { + version = Botan::TLS::Protocol_Version::TLS_V12; + if(!policy) + policy.reset(new CLI_Policy(version)); + } + else if(!policy) + { + policy.reset(new Botan::TLS::Policy); + } + + if(policy->acceptable_protocol_version(version) == false) + { + throw CLI_Usage_Error("The policy specified does not allow the requested TLS version"); } struct sockaddr_storage addrbuf; @@ -139,6 +175,8 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks hostname = host; } + m_sockfd = connect_to_host(host, port, use_tcp); + Basic_Credentials_Manager creds(use_system_cert_store, trusted_CAs); Botan::TLS::Client client(*this, *session_mgr, creds, *policy, rng(), diff --git a/src/cli/tls_utils.cpp b/src/cli/tls_utils.cpp index 2429b5de6..16813c13a 100644 --- a/src/cli/tls_utils.cpp +++ b/src/cli/tls_utils.cpp @@ -55,8 +55,8 @@ class TLS_All_Policy final : public Botan::TLS::Policy return { "ECDSA", "RSA", "DSA" }; } - bool allow_tls10() const override { return false; } - bool allow_tls11() const override { return false; } + bool allow_tls10() const override { return true; } + bool allow_tls11() const override { return true; } bool allow_tls12() const override { return true; } }; @@ -138,6 +138,12 @@ class TLS_Ciphersuites final : public Command policy.reset(new Botan::TLS::Text_Policy(policy_txt)); } + if(policy->acceptable_protocol_version(version) == false) + { + error_output() << "Error: the policy specified does not allow the given TLS version\n"; + return; + } + for(uint16_t suite_id : policy->ciphersuite_list(version, with_srp)) { const Botan::TLS::Ciphersuite suite(Botan::TLS::Ciphersuite::by_id(suite_id)); diff --git a/src/configs/sphinx/conf.py b/src/configs/sphinx/conf.py index c172f9411..127fd5401 100644 --- a/src/configs/sphinx/conf.py +++ b/src/configs/sphinx/conf.py @@ -47,7 +47,7 @@ version_patch = version_info['release_patch'] is_website_build = check_for_tag('website') -needs_sphinx = '1.3' +needs_sphinx = '1.2' templates_path = ['templates'] diff --git a/src/lib/block/block_cipher.cpp b/src/lib/block/block_cipher.cpp index 3ace6cd4f..fb0564646 100644 --- a/src/lib/block/block_cipher.cpp +++ b/src/lib/block/block_cipher.cpp @@ -98,12 +98,27 @@ #include <botan/internal/openssl.h> #endif +#if defined(BOTAN_HAS_COMMONCRYPTO) + #include <botan/internal/commoncrypto.h> +#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 + #if defined(BOTAN_HAS_OPENSSL) if(provider.empty() || provider == "openssl") { @@ -115,7 +130,6 @@ BlockCipher::create(const std::string& algo, } #endif - // TODO: CommonCrypto // TODO: CryptoAPI // TODO: /dev/crypto @@ -343,7 +357,7 @@ BlockCipher::create_or_throw(const std::string& algo, std::vector<std::string> BlockCipher::providers(const std::string& algo) { - return probe_providers_of<BlockCipher>(algo, { "base", "openssl" }); + return probe_providers_of<BlockCipher>(algo, { "base", "openssl", "commoncrypto" }); } } diff --git a/src/lib/block/serpent/serpent.cpp b/src/lib/block/serpent/serpent.cpp index d9001d19f..7bdda0f9f 100644 --- a/src/lib/block/serpent/serpent.cpp +++ b/src/lib/block/serpent/serpent.cpp @@ -276,6 +276,13 @@ void Serpent::clear() std::string Serpent::provider() const { +#if defined(BOTAN_HAS_SERPENT_AVX2) + if(CPUID::has_avx2()) + { + return "avx2"; + } +#endif + #if defined(BOTAN_HAS_SERPENT_SIMD) if(CPUID::has_simd_32()) { diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 0a537491c..ff7086488 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1217,6 +1217,22 @@ BOTAN_PUBLIC_API(2,2) int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key, uint8_t pubkey[32]); /* +* Algorithm specific key operations: X25519 +*/ + +BOTAN_PUBLIC_API(2,8) int botan_privkey_load_x25519(botan_privkey_t* key, + const uint8_t privkey[32]); + +BOTAN_PUBLIC_API(2,8) int botan_pubkey_load_x25519(botan_pubkey_t* key, + const uint8_t pubkey[32]); + +BOTAN_PUBLIC_API(2,8) int botan_privkey_x25519_get_privkey(botan_privkey_t key, + uint8_t output[32]); + +BOTAN_PUBLIC_API(2,8) int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key, + uint8_t pubkey[32]); + +/* * Algorithm specific key operations: ECDSA and ECDH */ BOTAN_PUBLIC_API(2,2) @@ -1513,7 +1529,7 @@ int botan_key_unwrap3394(const uint8_t wrapped_key[], size_t wrapped_key_len, typedef struct botan_hotp_struct* botan_hotp_t; /** -* Initialize an HOTP instance +* Initialize a HOTP instance */ BOTAN_PUBLIC_API(2,8) int botan_hotp_init(botan_hotp_t* hotp, @@ -1528,7 +1544,7 @@ BOTAN_PUBLIC_API(2,8) int botan_hotp_destroy(botan_hotp_t hotp); /** -* Generate an HOTP code for the provided counter +* Generate a HOTP code for the provided counter */ BOTAN_PUBLIC_API(2,8) int botan_hotp_generate(botan_hotp_t hotp, @@ -1536,7 +1552,7 @@ int botan_hotp_generate(botan_hotp_t hotp, uint64_t hotp_counter); /** -* Verify an HOTP code +* Verify a HOTP code */ BOTAN_PUBLIC_API(2,8) int botan_hotp_check(botan_hotp_t hotp, @@ -1553,7 +1569,7 @@ int botan_hotp_check(botan_hotp_t hotp, typedef struct botan_totp_struct* botan_totp_t; /** -* Initialize an TOTP instance +* Initialize a TOTP instance */ BOTAN_PUBLIC_API(2,8) int botan_totp_init(botan_totp_t* totp, @@ -1569,7 +1585,10 @@ BOTAN_PUBLIC_API(2,8) int botan_totp_destroy(botan_totp_t totp); /** -* Generate an TOTP code for the provided counter +* Generate a TOTP code for the provided timestamp +* @param totp the TOTP object +* @param totp_code the OTP code will be written here +* @param timestamp the current local timestamp */ BOTAN_PUBLIC_API(2,8) int botan_totp_generate(botan_totp_t totp, @@ -1577,10 +1596,12 @@ int botan_totp_generate(botan_totp_t totp, uint64_t timestamp); /** -* Verify an TOTP code +* Verify a TOTP code * @param totp the TOTP object -* @param clock_drift if non-null and otp code is valid, -* set to the drift between the two clocks. +* @param totp_code the presented OTP +* @param timestamp the current local timestamp +* @param acceptable_clock_drift specifies the acceptable amount +* of clock drift (in terms of time steps) between the two hosts. */ BOTAN_PUBLIC_API(2,8) int botan_totp_check(botan_totp_t totp, diff --git a/src/lib/ffi/ffi_pkey_algs.cpp b/src/lib/ffi/ffi_pkey_algs.cpp index 52cbfa954..e98cb542b 100644 --- a/src/lib/ffi/ffi_pkey_algs.cpp +++ b/src/lib/ffi/ffi_pkey_algs.cpp @@ -843,6 +843,88 @@ int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key, #endif } +/* X25519 specific operations */ + +int botan_privkey_load_x25519(botan_privkey_t* key, + const uint8_t privkey[32]) + { +#if defined(BOTAN_HAS_X25519) + *key = nullptr; + return ffi_guard_thunk(BOTAN_CURRENT_FUNCTION, [=]() -> int { + const Botan::secure_vector<uint8_t> privkey_vec(privkey, privkey + 32); + *key = new botan_privkey_struct(new Botan::X25519_PrivateKey(privkey_vec)); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(key, privkey); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_pubkey_load_x25519(botan_pubkey_t* key, + const uint8_t pubkey[32]) + { +#if defined(BOTAN_HAS_X25519) + *key = nullptr; + return ffi_guard_thunk(BOTAN_CURRENT_FUNCTION, [=]() -> int { + const std::vector<uint8_t> pubkey_vec(pubkey, pubkey + 32); + *key = new botan_pubkey_struct(new Botan::X25519_PublicKey(pubkey_vec)); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(key, pubkey); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_privkey_x25519_get_privkey(botan_privkey_t key, + uint8_t output[32]) + { +#if defined(BOTAN_HAS_X25519) + return BOTAN_FFI_DO(Botan::Private_Key, key, k, { + if(Botan::X25519_PrivateKey* x25519 = dynamic_cast<Botan::X25519_PrivateKey*>(&k)) + { + const Botan::secure_vector<uint8_t>& x25519_key = x25519->get_x(); + if(x25519_key.size() != 32) + return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; + Botan::copy_mem(output, x25519_key.data(), x25519_key.size()); + return BOTAN_FFI_SUCCESS; + } + else + { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + }); +#else + BOTAN_UNUSED(key, output); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + +int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key, + uint8_t output[32]) + { +#if defined(BOTAN_HAS_X25519) + return BOTAN_FFI_DO(Botan::Public_Key, key, k, { + if(Botan::X25519_PublicKey* x25519 = dynamic_cast<Botan::X25519_PublicKey*>(&k)) + { + const std::vector<uint8_t>& x25519_key = x25519->public_value(); + if(x25519_key.size() != 32) + return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; + Botan::copy_mem(output, x25519_key.data(), x25519_key.size()); + return BOTAN_FFI_SUCCESS; + } + else + { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + }); +#else + BOTAN_UNUSED(key, output); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif + } + int botan_privkey_create_mceliece(botan_privkey_t* key_obj, botan_rng_t rng_obj, size_t n, size_t t) { const std::string mce_params = std::to_string(n) + "," + std::to_string(t); diff --git a/src/lib/math/mp/mp_madd.h b/src/lib/math/mp/mp_madd.h index 4f34efe39..43c769c7c 100644 --- a/src/lib/math/mp/mp_madd.h +++ b/src/lib/math/mp/mp_madd.h @@ -34,14 +34,12 @@ namespace Botan { #if defined(BOTAN_USE_GCC_INLINE_ASM) #define BOTAN_MP_USE_X86_32_ASM - #define ASM(x) x "\n\t" #elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) #define BOTAN_MP_USE_X86_32_MSVC_ASM #endif #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && (BOTAN_MP_WORD_BITS == 64) && (BOTAN_USE_GCC_INLINE_ASM) #define BOTAN_MP_USE_X86_64_ASM - #define ASM(x) x "\n\t" #endif #if defined(BOTAN_MP_USE_X86_32_ASM) || defined(BOTAN_MP_USE_X86_64_ASM) diff --git a/src/lib/modes/cipher_mode.cpp b/src/lib/modes/cipher_mode.cpp index 00d7a4db0..710f16ba2 100644 --- a/src/lib/modes/cipher_mode.cpp +++ b/src/lib/modes/cipher_mode.cpp @@ -35,6 +35,10 @@ #include <botan/internal/openssl.h> #endif +#if defined(BOTAN_HAS_COMMONCRYPTO) + #include <botan/internal/commoncrypto.h> +#endif + namespace Botan { std::unique_ptr<Cipher_Mode> Cipher_Mode::create_or_throw(const std::string& algo, @@ -51,6 +55,19 @@ std::unique_ptr<Cipher_Mode> Cipher_Mode::create(const std::string& algo, Cipher_Dir direction, const std::string& provider) { +#if defined(BOTAN_HAS_COMMONCRYPTO) + if(provider.empty() || provider == "commoncrypto") + { + std::unique_ptr<Cipher_Mode> commoncrypto_cipher(make_commoncrypto_cipher_mode(algo, direction)); + + if(commoncrypto_cipher) + return commoncrypto_cipher; + + if(!provider.empty()) + return std::unique_ptr<Cipher_Mode>(); + } +#endif + #if defined(BOTAN_HAS_OPENSSL) if(provider.empty() || provider == "openssl") { @@ -172,7 +189,7 @@ std::unique_ptr<Cipher_Mode> Cipher_Mode::create(const std::string& algo, //static std::vector<std::string> Cipher_Mode::providers(const std::string& algo_spec) { - const std::vector<std::string>& possible = { "base", "openssl" }; + const std::vector<std::string>& possible = { "base", "openssl", "commoncrypto" }; std::vector<std::string> providers; for(auto&& prov : possible) { diff --git a/src/lib/prov/commoncrypto/commoncrypto.h b/src/lib/prov/commoncrypto/commoncrypto.h index 34b2a3f35..958cbab7d 100644 --- a/src/lib/prov/commoncrypto/commoncrypto.h +++ b/src/lib/prov/commoncrypto/commoncrypto.h @@ -16,14 +16,33 @@ namespace Botan { +class Cipher_Mode; +class BlockCipher; class HashFunction; +enum Cipher_Dir : int; +typedef int32_t CCCryptorStatus; -class BOTAN_PUBLIC_API(2,0) CommonCrypto_Error final : public Exception - { - public: +class BOTAN_PUBLIC_API(2, 0) CommonCrypto_Error final : public Exception + { + std::string ccryptorstatus_to_string(CCCryptorStatus status); + + public: CommonCrypto_Error(const std::string& what) : - Exception(what + " failed.") {} - }; + Exception(what + " failed.") {} + + CommonCrypto_Error(const std::string& what, int32_t status) : + Exception(what + std::string(" failed. Status: ") + ccryptorstatus_to_string(status)) {} + }; + +/* Cipher Modes */ + +Cipher_Mode* +make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction); + +/* Block Ciphers */ + +std::unique_ptr<BlockCipher> +make_commoncrypto_block_cipher(const std::string& name); /* Hash */ diff --git a/src/lib/prov/commoncrypto/commoncrypto_block.cpp b/src/lib/prov/commoncrypto/commoncrypto_block.cpp new file mode 100644 index 000000000..e912ed57e --- /dev/null +++ b/src/lib/prov/commoncrypto/commoncrypto_block.cpp @@ -0,0 +1,152 @@ +/* +* Block Ciphers via CommonCrypto +* (C) 2018 Jose Luis Pereira +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/hex.h> + +#include <botan/block_cipher.h> +#include <botan/internal/commoncrypto.h> +#include <CommonCrypto/CommonCrypto.h> + +#include "commoncrypto_utils.h" + +namespace Botan { + +namespace { + +class CommonCrypto_BlockCipher final : public BlockCipher + { + public: + CommonCrypto_BlockCipher(const std::string& name, const CommonCryptor_Opts& opts); + + ~CommonCrypto_BlockCipher(); + + void clear() override; + std::string provider() const override { return "commoncrypto"; } + std::string name() const override { return m_cipher_name; } + BlockCipher* clone() const override; + + size_t block_size() const override { return m_opts.block_size; } + + Key_Length_Specification key_spec() const override { return m_opts.key_spec; } + + void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override + { + verify_key_set(m_key_set); + size_t total_len = blocks * m_opts.block_size; + size_t out_len = 0; + + CCCryptorStatus status = CCCryptorUpdate(m_encrypt, in, total_len, + out, total_len, &out_len); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorUpdate encrypt", status); + } + } + + void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override + { + verify_key_set(m_key_set); + size_t total_len = blocks * m_opts.block_size; + size_t out_len = 0; + + CCCryptorStatus status = CCCryptorUpdate(m_decrypt, in, total_len, + out, total_len, &out_len); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorUpdate decrypt", status); + } + } + + void key_schedule(const uint8_t key[], size_t key_len) override; + + std::string m_cipher_name; + CommonCryptor_Opts m_opts; + + CCCryptorRef m_encrypt = nullptr; + CCCryptorRef m_decrypt = nullptr; + bool m_key_set; + }; + +CommonCrypto_BlockCipher::CommonCrypto_BlockCipher(const std::string& algo_name, + const CommonCryptor_Opts& opts) : + m_cipher_name(algo_name), + m_opts(opts), + m_key_set(false) + { + } + +CommonCrypto_BlockCipher::~CommonCrypto_BlockCipher() + { + if(m_encrypt) + { + CCCryptorRelease(m_encrypt); + } + if(m_decrypt) + { + CCCryptorRelease(m_decrypt); + } + } + +/* +* Set the key +*/ +void CommonCrypto_BlockCipher::key_schedule(const uint8_t key[], size_t length) + { + secure_vector<uint8_t> full_key(key, key + length); + + commoncrypto_adjust_key_size(key, length, m_opts, full_key); + + CCCryptorStatus status; + status = CCCryptorCreate(kCCEncrypt, m_opts.algo, kCCOptionECBMode, + full_key.data(), full_key.size(), nullptr, &m_encrypt); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorCreate encrypt", status); + } + status = CCCryptorCreate(kCCDecrypt, m_opts.algo, kCCOptionECBMode, + full_key.data(), full_key.size(), nullptr, &m_decrypt); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorCreate decrypt", status); + } + + m_key_set = true; + } + +/* +* Return a clone of this object +*/ +BlockCipher* CommonCrypto_BlockCipher::clone() const + { + return new CommonCrypto_BlockCipher(m_cipher_name, m_opts); + } + +/* +* Clear memory of sensitive data +*/ +void CommonCrypto_BlockCipher::clear() + { + m_key_set = false; + } +} + +std::unique_ptr<BlockCipher> +make_commoncrypto_block_cipher(const std::string& name) + { + + try + { + CommonCryptor_Opts opts = commoncrypto_opts_from_algo(name); + return std::unique_ptr<BlockCipher>(new CommonCrypto_BlockCipher(name, opts)); + } + catch(CommonCrypto_Error e) + { + return nullptr; + } + } +} + diff --git a/src/lib/prov/commoncrypto/commoncrypto_mode.cpp b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp new file mode 100644 index 000000000..baae11679 --- /dev/null +++ b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp @@ -0,0 +1,240 @@ +/* +* Cipher Modes via CommonCrypto +* (C) 2018 Jose Pereira +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cipher_mode.h> +#include <botan/internal/rounding.h> +#include <botan/internal/commoncrypto.h> + +#include "commoncrypto_utils.h" + +#include <limits.h> + +namespace Botan { + +namespace { + +class CommonCrypto_Cipher_Mode final : public Cipher_Mode + { + public: + CommonCrypto_Cipher_Mode(const std::string& name, + Cipher_Dir direction, + const CommonCryptor_Opts& opts); + + ~CommonCrypto_Cipher_Mode(); + + std::string provider() const override { return "commoncrypto"; } + std::string name() const override { return m_mode_name; } + + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + size_t process(uint8_t msg[], size_t msg_len) override; + void finish(secure_vector<uint8_t>& final_block, size_t offset0) override; + size_t output_length(size_t input_length) const override; + size_t update_granularity() const override; + size_t minimum_final_size() const override; + size_t default_nonce_length() const override; + bool valid_nonce_length(size_t nonce_len) const override; + void clear() override; + void reset() override; + Key_Length_Specification key_spec() const override; + + private: + void key_schedule(const uint8_t key[], size_t length) override; + + const std::string m_mode_name; + Cipher_Dir m_direction; + CommonCryptor_Opts m_opts; + CCCryptorRef m_cipher = nullptr; + bool m_key_set; + bool m_nonce_set; + }; + +CommonCrypto_Cipher_Mode::CommonCrypto_Cipher_Mode(const std::string& name, + Cipher_Dir direction, const CommonCryptor_Opts& opts) : + m_mode_name(name), + m_direction(direction), + m_opts(opts), + m_key_set(false), + m_nonce_set(false) + { + } + +CommonCrypto_Cipher_Mode::~CommonCrypto_Cipher_Mode() + { + if(m_cipher) + { + CCCryptorRelease(m_cipher); + } + } + +void CommonCrypto_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + verify_key_set(m_key_set); + + if(!valid_nonce_length(nonce_len)) + { throw Invalid_IV_Length(name(), nonce_len); } + if(nonce_len) + { + CCCryptorStatus status = CCCryptorReset(m_cipher, nonce); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorReset on start_msg", status); + } + } + m_nonce_set = true; + } + +size_t CommonCrypto_Cipher_Mode::process(uint8_t msg[], size_t msg_len) + { + verify_key_set(m_key_set); + BOTAN_STATE_CHECK(m_nonce_set); + + if(msg_len == 0) + { return 0; } + if(msg_len > INT_MAX) + { throw Internal_Error("msg_len overflow"); } + size_t outl = CCCryptorGetOutputLength(m_cipher, msg_len, false); + + secure_vector<uint8_t> out(outl); + + if(m_opts.padding == ccNoPadding && msg_len % m_opts.block_size) + { + msg_len = outl; + } + + CCCryptorStatus status = CCCryptorUpdate(m_cipher, msg, msg_len, + out.data(), outl, &outl); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorUpdate", status); + } + memcpy(msg, out.data(), outl); + + return outl; + } + +void CommonCrypto_Cipher_Mode::finish(secure_vector<uint8_t>& buffer, + size_t offset) + { + verify_key_set(m_key_set); + + BOTAN_ASSERT(buffer.size() >= offset, "Offset ok"); + uint8_t* buf = buffer.data() + offset; + const size_t buf_size = buffer.size() - offset; + + size_t written = process(buf, buf_size); + + size_t outl = CCCryptorGetOutputLength(m_cipher, buf_size - written, true); + secure_vector<uint8_t> out(outl); + + CCCryptorStatus status = CCCryptorFinal( + m_cipher, out.data(), outl, &outl); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorFinal", status); + } + + size_t new_len = offset + written + outl; + if(m_opts.padding != ccNoPadding || buffer.size() < new_len) + { + buffer.resize(new_len); + } + memcpy(buffer.data() - offset + written, out.data(), outl); + written += outl; + } + +size_t CommonCrypto_Cipher_Mode::update_granularity() const + { + return m_opts.block_size * BOTAN_BLOCK_CIPHER_PAR_MULT; + } + +size_t CommonCrypto_Cipher_Mode::minimum_final_size() const + { + return 0; + } + +size_t CommonCrypto_Cipher_Mode::default_nonce_length() const + { + return m_opts.block_size; + } + +bool CommonCrypto_Cipher_Mode::valid_nonce_length(size_t nonce_len) const + { + return (nonce_len == 0 || nonce_len == m_opts.block_size); + } + +size_t CommonCrypto_Cipher_Mode::output_length(size_t input_length) const + { + if(input_length == 0) + { return m_opts.block_size; } + else + { return round_up(input_length, m_opts.block_size); } + } + +void CommonCrypto_Cipher_Mode::clear() + { + m_key_set = false; + + if(m_cipher == nullptr) + { + return; + } + + if(m_cipher) + { + CCCryptorRelease(m_cipher); + m_cipher = nullptr; + } + } + +void CommonCrypto_Cipher_Mode::reset() + { + if(m_cipher == nullptr) + { + return; + } + CCCryptorStatus status = CCCryptorReset(m_cipher, nullptr); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorReset", status); + } + } + +Key_Length_Specification CommonCrypto_Cipher_Mode::key_spec() const + { + return m_opts.key_spec; + } + +void CommonCrypto_Cipher_Mode::key_schedule(const uint8_t key[], size_t length) + { + CCCryptorStatus status; + CCOperation op = m_direction == ENCRYPTION ? kCCEncrypt : kCCDecrypt; + status = CCCryptorCreateWithMode(op, m_opts.mode, m_opts.algo, m_opts.padding, + nullptr, key, length, nullptr, 0, 0, 0, &m_cipher); + if(status != kCCSuccess) + { + throw CommonCrypto_Error("CCCryptorCreate", status); + } + + m_key_set = true; + } +} + +Cipher_Mode* +make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction) + { + + try + { + CommonCryptor_Opts opts = commoncrypto_opts_from_algo(name); + return new CommonCrypto_Cipher_Mode(name, direction, opts); + } + catch(CommonCrypto_Error e) + { + return nullptr; + } + } +} diff --git a/src/lib/prov/commoncrypto/commoncrypto_utils.cpp b/src/lib/prov/commoncrypto/commoncrypto_utils.cpp new file mode 100644 index 000000000..62736b88c --- /dev/null +++ b/src/lib/prov/commoncrypto/commoncrypto_utils.cpp @@ -0,0 +1,187 @@ +/* +* Cipher Modes via CommonCrypto +* (C) 2018 Jose Pereira +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cipher_mode.h> +#include <botan/parsing.h> +#include <botan/internal/commoncrypto.h> +#include <botan/internal/rounding.h> +#include <botan/scan_name.h> + +#include "commoncrypto_utils.h" + +namespace Botan { + +std::string CommonCrypto_Error::ccryptorstatus_to_string(CCCryptorStatus status) + { + switch(status) + { + case kCCSuccess: + return "Success"; + case kCCParamError: + return "ParamError"; + case kCCBufferTooSmall: + return "BufferTooSmall"; + case kCCMemoryFailure: + return "MemoryFailure"; + case kCCAlignmentError: + return "AlignmentError"; + case kCCDecodeError: + return "DecodeError"; + case kCCUnimplemented: + return "Unimplemented"; + case kCCOverflow: + return "Overflow"; + case kCCRNGFailure: + return "RNGFailure"; + case kCCUnspecifiedError: + return "UnspecifiedError"; + case kCCCallSequenceError: + return "CallSequenceError"; + case kCCKeySizeError: + return "KeySizeError"; + default: + return "Unknown"; + } + }; + + +CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo) + { + SCAN_Name spec(algo); + + std::string algo_name = spec.algo_name(); + std::string cipher_mode = spec.cipher_mode(); + std::string cipher_mode_padding = spec.cipher_mode_pad(); + + CommonCryptor_Opts opts; + + if(algo_name.compare(0, 3, "AES") == 0) + { + opts.algo = kCCAlgorithmAES; + opts.block_size = kCCBlockSizeAES128; + if(algo_name == "AES-128") + { + opts.key_spec = Key_Length_Specification(kCCKeySizeAES128); + } + else if(algo_name == "AES-192") + { + opts.key_spec = Key_Length_Specification(kCCKeySizeAES192); + } + else if(algo_name == "AES-256") + { + opts.key_spec = Key_Length_Specification(kCCKeySizeAES256); + } + else + { + throw CommonCrypto_Error("Unknown AES algorithm"); + } + } + else if(algo_name == "DES") + { + opts.algo = kCCAlgorithmDES; + opts.block_size = kCCBlockSizeDES; + opts.key_spec = Key_Length_Specification(kCCKeySizeDES); + } + else if(algo_name == "TripleDES") + { + opts.algo = kCCAlgorithm3DES; + opts.block_size = kCCBlockSize3DES; + opts.key_spec = Key_Length_Specification(16, kCCKeySize3DES, 8); + } + else if(algo_name == "Blowfish") + { + opts.algo = kCCAlgorithmBlowfish; + opts.block_size = kCCBlockSizeBlowfish; + opts.key_spec = Key_Length_Specification(1, kCCKeySizeMaxBlowfish, 1); + } + else if(algo_name == "CAST-128") + { + opts.algo = kCCAlgorithmCAST; + opts.block_size = kCCBlockSizeCAST; + // Botan's base implementation of CAST does not support shorter keys + // so we limit its minimum key size to 11 here. + opts.key_spec = Key_Length_Specification(11, kCCKeySizeMaxCAST, 1); + } + else + { + throw CommonCrypto_Error("Unsupported cipher"); + } + + //TODO add CFB and XTS support + if(cipher_mode.empty() || cipher_mode == "ECB") + { + opts.mode = kCCModeECB; + } + else if(cipher_mode == "CBC") + { + opts.mode = kCCModeCBC; + } + else if(cipher_mode == "CTR") + { + opts.mode = kCCModeCTR; + } + else if(cipher_mode == "OFB") + { + opts.mode = kCCModeOFB; + } + else + { + throw CommonCrypto_Error("Unsupported cipher mode!"); + } + + if(cipher_mode_padding.empty() || cipher_mode_padding == "PKCS7") + { + opts.padding = ccPKCS7Padding; + } + else if(cipher_mode_padding == "NoPadding") + { + opts.padding = ccNoPadding; + } + else + { + throw CommonCrypto_Error("Unsupported cipher mode padding!"); + } + + return opts; + } + + +void commoncrypto_adjust_key_size(const uint8_t key[], size_t length, + const CommonCryptor_Opts& opts, secure_vector<uint8_t>& full_key) + { + + if(opts.algo == kCCAlgorithmBlowfish && length < 8) + { + size_t repeat; + switch(length) + { + case 1: + repeat = 8; + break; + case 2: + repeat = 4; + break; + case 3: + repeat = 3; + break; + default: + repeat = 2; + break; + } + + full_key.resize(length * repeat); + for(int i=0; i<repeat; i++) + { + memcpy(full_key.data() + i * length, key, length); + } + } + else if(opts.algo == kCCAlgorithm3DES && length == 16) + { + full_key += std::make_pair(key, 8); + } + } +} diff --git a/src/lib/prov/commoncrypto/commoncrypto_utils.h b/src/lib/prov/commoncrypto/commoncrypto_utils.h new file mode 100644 index 000000000..f4bdf4e24 --- /dev/null +++ b/src/lib/prov/commoncrypto/commoncrypto_utils.h @@ -0,0 +1,36 @@ +/* +* Utils for calling CommonCrypto +* (C) 2018 Jose Pereira +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_INTERNAL_COMMONCRYPTO_UTILS_H_ +#define BOTAN_INTERNAL_COMMONCRYPTO_UTILS_H_ + +#include <botan/key_spec.h> + +#include <CommonCrypto/CommonCrypto.h> + +namespace Botan { + +class Key_Length_Specification; + +struct CommonCryptor_Opts + { + CCAlgorithm algo; + CCMode mode; + CCPadding padding; + size_t block_size; + Key_Length_Specification key_spec{0}; + }; + +CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo); + +void commoncrypto_adjust_key_size(const uint8_t key[], size_t length, + const CommonCryptor_Opts& opts, secure_vector<uint8_t>& full_key); + + +} + +#endif diff --git a/src/lib/prov/commoncrypto/info.txt b/src/lib/prov/commoncrypto/info.txt index 00f92cd58..d56fe5731 100644 --- a/src/lib/prov/commoncrypto/info.txt +++ b/src/lib/prov/commoncrypto/info.txt @@ -11,3 +11,8 @@ commoncrypto.h <os_features> commoncrypto </os_features> + +<requires> +modes +block +</requires> diff --git a/src/lib/pubkey/curve25519/curve25519.h b/src/lib/pubkey/curve25519/curve25519.h index 1e06fae67..c2f8f42b3 100644 --- a/src/lib/pubkey/curve25519/curve25519.h +++ b/src/lib/pubkey/curve25519/curve25519.h @@ -99,6 +99,9 @@ class BOTAN_PUBLIC_API(2,0) Curve25519_PrivateKey final : public Curve25519_Publ secure_vector<uint8_t> m_private; }; +typedef Curve25519_PublicKey X25519_PublicKey; +typedef Curve25519_PrivateKey X25519_PrivateKey; + /* * The types above are just wrappers for curve25519_donna, plus defining * encodings for public and private keys. diff --git a/src/lib/pubkey/curve25519/info.txt b/src/lib/pubkey/curve25519/info.txt index 085314cfe..3818b0606 100644 --- a/src/lib/pubkey/curve25519/info.txt +++ b/src/lib/pubkey/curve25519/info.txt @@ -1,5 +1,6 @@ <defines> CURVE_25519 -> 20170621 +X25519 -> 20180910 </defines> <header:public> diff --git a/src/lib/x509/info.txt b/src/lib/x509/info.txt index 6b136cbcf..20a1aa2b0 100644 --- a/src/lib/x509/info.txt +++ b/src/lib/x509/info.txt @@ -1,5 +1,6 @@ <defines> X509_CERTIFICATES -> 20151023 +X509 -> 20180911 OCSP -> 20161118 </defines> diff --git a/src/scripts/ci/setup_travis.sh b/src/scripts/ci/setup_travis.sh index 9c6c79012..14a88a5c3 100755 --- a/src/scripts/ci/setup_travis.sh +++ b/src/scripts/ci/setup_travis.sh @@ -84,7 +84,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then # sudo apt-get remove python-requests python-openssl python-roman - sudo pip install requests pyopenssl roman sphinx + sudo pip install requests pyopenssl roman sphinx==1.7.9 fi elif [ "$TRAVIS_OS_NAME" = "osx" ]; then diff --git a/src/tests/data/cryptobox.vec b/src/tests/data/cryptobox.vec new file mode 100644 index 000000000..cb397f92b --- /dev/null +++ b/src/tests/data/cryptobox.vec @@ -0,0 +1,92 @@ + +# Generated by Botan 2.0 + +Input = +Salt = 42434041464744454A4B +Passphrase = passphrase_0 +Output = EFC2240042434041464744454A4B791BEF092F976AF5F1B9219C1F2B71BE7B247647 + +Input = 67 +Salt = 434041464744454A4B48 +Passphrase = passphrase_1 +Output = EFC22400434041464744454A4B48AAA3FCA400FF22A576D5A20C638CB5080831AB269D + +Input = 6465 +Salt = 4041464744454A4B4849 +Passphrase = passphrase_2 +Output = EFC224004041464744454A4B484981121406F83782EBCAAE06FDB1B0A8599DE6D721C279 + +Input = 656263 +Salt = 41464744454A4B48494E +Passphrase = passphrase_3 +Output = EFC2240041464744454A4B48494EF50B4430A0E524651EC7088EBA195E25F60D342A4D1839 + +Input = 62636061 +Salt = 464744454A4B48494E4F +Passphrase = passphrase_4 +Output = EFC22400464744454A4B48494E4FA1842DE30D76A586A4BFE958FC777178128A31BF836327C8 + +Input = 6360616E6F +Salt = 4744454A4B48494E4F4C +Passphrase = passphrase_5 +Output = EFC224004744454A4B48494E4F4CE6584DF80422FC28A9AB39BCDBDFD72988CC93404A6DF39C43 + +Input = 60616E6F6C6D +Salt = 44454A4B48494E4F4C4D +Passphrase = passphrase_6 +Output = EFC2240044454A4B48494E4F4C4D0A52AB379B7EB45CF9917E63582A9F958563CF21159D2350308A + +Input = 616E6F6C6D6A6B +Salt = 454A4B48494E4F4C4D52 +Passphrase = passphrase_7 +Output = EFC22400454A4B48494E4F4C4D5288055EC1F9BC81734F96CDCDCF879E939484B7A41F26AABD992CDA + +Input = 6E6F6C6D6A6B6869 +Salt = 4A4B48494E4F4C4D5253 +Passphrase = passphrase_8 +Output = EFC224004A4B48494E4F4C4D525348B8CBF00F22DACFEE8BCC1034EB41DA0CB1901F85BFED87E6C1F70C + +Input = 6F6C6D6A6B68697677 +Salt = 4B48494E4F4C4D525350 +Passphrase = passphrase_9 +Output = EFC224004B48494E4F4C4D525350C2B4D2E37730AD85D87DB14C446DEC8F32CC8FB95C90BFC8BA76B41A68 + +Input = 6C6D6A6B686976777475 +Salt = 48494E4F4C4D52535051 +Passphrase = passphrase_10 +Output = EFC2240048494E4F4C4D525350514D0A851CB9B98EAB808A159865A578008366EAE7D71AF33B018B56B3D8EE + +Input = 6D6A6B6869767774757273 +Salt = 494E4F4C4D5253505156 +Passphrase = passphrase_11 +Output = EFC22400494E4F4C4D525350515620390098C6708307789436F586FE45B6F6E5D93F4A623A4CD7F64D3A4799DA + +Input = 6A6B68697677747572737071 +Salt = 4E4F4C4D525350515657 +Passphrase = passphrase_12 +Output = EFC224004E4F4C4D52535051565761C7CC345DD0900264648975F99051996CCE0EE29F082DB8F39CBC20E2FD5C82 + +Input = 6B686976777475727370717E7F +Salt = 4F4C4D52535051565754 +Passphrase = passphrase_13 +Output = EFC224004F4C4D52535051565754C537DE170C00AD72BFF5542481B19FC89C048723170CE58BD9183714897B8BE012 + +Input = 686976777475727370717E7F7C7D +Salt = 4C4D5253505156575455 +Passphrase = passphrase_14 +Output = EFC224004C4D5253505156575455E2F9CED9F76669C5CBD91383AE80C9F653567038A71A3D59081E1CA89A1A1843BF1E + +Input = 6976777475727370717E7F7C7D7A7B +Salt = 4D52535051565754555A +Passphrase = passphrase_15 +Output = EFC224004D52535051565754555A91AC3715C0C9F6735566794D29D76157381D17F1FC3D303370011ABDD4513D93CD5128 + +Input = 76777475727370717E7F7C7D7A7B7879 +Salt = 52535051565754555A5B +Passphrase = passphrase_16 +Output = EFC2240052535051565754555A5B9B0F0B4189AC0AC3E8E0587135AEC109256C8964B01095AD8A2715B1EE51C6A54F850804 + +Input = 7946474445424340414E4F4C4D4A4B484956575455525350515E5F5C5D5A5B +Salt = 5D62636061666764656A +Passphrase = passphrase_31 +Output = EFC224005D62636061666764656A5272C74FD3E21444CC93423B7FA86172063741E0504078B25584C32B275D792C4B6B3BD3F517C1116D45F4F6238AC1112E93A0 diff --git a/src/tests/test_certstor.cpp b/src/tests/test_certstor.cpp index 4963f7465..5bf8ce8fa 100644 --- a/src/tests/test_certstor.cpp +++ b/src/tests/test_certstor.cpp @@ -7,7 +7,8 @@ #include "tests.h" #include <sstream> -#if defined(BOTAN_HAS_X509) && defined(BOTAN_HAS_PUBKEY) +#if defined(BOTAN_HAS_X509_CERTIFICATES) + #include <botan/x509cert.h> #include <botan/certstor.h> #include <botan/internal/filesystem.h> #include <botan/pkcs8.h> @@ -23,7 +24,7 @@ namespace Botan_Tests { namespace { -#if defined(BOTAN_HAS_X509) && defined(BOTAN_HAS_RSA) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) +#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) struct CertificateAndKey { diff --git a/src/tests/test_cryptobox.cpp b/src/tests/test_cryptobox.cpp index 5e7fcf08a..d3b011eb4 100644 --- a/src/tests/test_cryptobox.cpp +++ b/src/tests/test_cryptobox.cpp @@ -1,14 +1,15 @@ /* -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include "tests.h" +#include "test_rng.h" #if defined(BOTAN_HAS_CRYPTO_BOX) #include <botan/cryptobox.h> - #include <botan/hex.h> + #include <botan/pem.h> #endif namespace Botan_Tests { @@ -17,56 +18,55 @@ namespace { #if defined(BOTAN_HAS_CRYPTO_BOX) -class Cryptobox_Tests final : public Test +class Cryptobox_KAT final : public Text_Based_Test { public: - std::vector<Test::Result> run() override + Cryptobox_KAT() : Text_Based_Test("cryptobox.vec", "Input,Passphrase,Salt,Output") {} + + Test::Result run_one_test(const std::string&, const VarMap& vars) override { - Test::Result result("cryptobox"); + Test::Result result("Cryptobox"); + + const std::string password = vars.get_req_str("Passphrase"); + const std::vector<uint8_t> input = vars.get_req_bin("Input"); + const std::vector<uint8_t> salt = vars.get_req_bin("Salt"); + const std::vector<uint8_t> expected = vars.get_req_bin("Output"); + + const std::string expected_pem = Botan::PEM_Code::encode(expected, "BOTAN CRYPTOBOX MESSAGE"); + + Fixed_Output_RNG salt_rng(salt); + + const std::string ciphertext = + Botan::CryptoBox::encrypt(input.data(), input.size(), password, salt_rng); + + result.test_eq("encryption is expected value", ciphertext, expected_pem); + + result.test_eq("decryption works", Botan::CryptoBox::decrypt_bin(ciphertext, password), input); - for(size_t i = 0; i <= 128; i += 7) + // Now corrupt a bit and ensure it fails + try { - const std::string password = Test::random_password(); - const std::vector<uint8_t> input = unlock(Test::rng().random_vec(i)); - - const std::string ciphertext = - Botan::CryptoBox::encrypt(input.data(), input.size(), password, Test::rng()); - - // First verify decryption works - try - { - const Botan::secure_vector<uint8_t> decrypted = - Botan::CryptoBox::decrypt_bin(ciphertext, password); - result.test_eq("decrypt", decrypted, input); - } - catch(std::exception& e) - { - result.test_failure("cryptobox decrypt", e.what()); - } - - // Now corrupt a bit and ensure it fails - try - { - std::string corrupted = ciphertext; - corrupted[corrupted.size()/2]++; - Botan::CryptoBox::decrypt(corrupted, password); - result.test_failure("Decrypted corrupted cryptobox message"); - } - catch(Botan::Decoding_Error&) - { - result.test_success("Rejected corrupted cryptobox message"); - } - catch(Botan::Invalid_Argument&) - { - result.test_success("Rejected corrupted cryptobox message"); - } + const std::vector<uint8_t> corrupted = Test::mutate_vec(expected); + const std::string corrupted_pem = Botan::PEM_Code::encode(corrupted, "BOTAN CRYPTOBOX MESSAGE"); + + Botan::CryptoBox::decrypt(corrupted_pem, password); + result.test_failure("Decrypted corrupted cryptobox message", corrupted); + } + catch(Botan::Decoding_Error&) + { + result.test_success("Rejected corrupted cryptobox message"); + } + catch(Botan::Invalid_Argument&) + { + result.test_success("Rejected corrupted cryptobox message"); } - return {result}; + return result; } + }; -BOTAN_REGISTER_TEST("cryptobox", Cryptobox_Tests); +BOTAN_REGISTER_TEST("cryptobox", Cryptobox_KAT); #endif diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 5a323ab6d..6687697cb 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -134,6 +134,10 @@ class FFI_Unit_Tests final : public Test results.push_back(ffi_test_ed25519(rng)); #endif +#if defined(BOTAN_HAS_X25519) + results.push_back(ffi_test_x25519()); +#endif + TEST_FFI_OK(botan_rng_destroy, (rng)); results.push_back(result); @@ -2420,6 +2424,60 @@ class FFI_Unit_Tests final : public Test return result; } + Test::Result ffi_test_x25519() + { + Test::Result result("FFI X25519"); + + // From RFC 8037 + + const std::vector<uint8_t> a_pub_bits = + Botan::hex_decode("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); + const std::vector<uint8_t> b_priv_bits = + Botan::hex_decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); + const std::vector<uint8_t> b_pub_bits = + Botan::hex_decode("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"); + const std::vector<uint8_t> shared_secret_bits = + Botan::hex_decode("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"); + + botan_privkey_t b_priv; + TEST_FFI_OK(botan_privkey_load_x25519, (&b_priv, b_priv_bits.data())); + + std::vector<uint8_t> privkey_read(32); + TEST_FFI_OK(botan_privkey_x25519_get_privkey, (b_priv, privkey_read.data())); + result.test_eq("X25519 private key", privkey_read, b_priv_bits); + + std::vector<uint8_t> pubkey_read(32); + + botan_pubkey_t b_pub; + TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv)); + TEST_FFI_OK(botan_pubkey_x25519_get_pubkey, (b_pub, pubkey_read.data())); + result.test_eq("X25519 public key b", pubkey_read, b_pub_bits); + + botan_pubkey_t a_pub; + TEST_FFI_OK(botan_pubkey_load_x25519, (&a_pub, a_pub_bits.data())); + TEST_FFI_OK(botan_pubkey_x25519_get_pubkey, (a_pub, pubkey_read.data())); + result.test_eq("X25519 public key a", pubkey_read, a_pub_bits); + + botan_pk_op_ka_t ka; + REQUIRE_FFI_OK(botan_pk_op_key_agreement_create, (&ka, b_priv, "Raw", 0)); + + std::vector<uint8_t> shared_output(32); + size_t shared_len = shared_output.size(); + TEST_FFI_OK(botan_pk_op_key_agreement, (ka, + shared_output.data(), &shared_len, + a_pub_bits.data(), a_pub_bits.size(), + nullptr, 0)); + + result.test_eq("Shared secret matches expected", shared_secret_bits, shared_output); + + TEST_FFI_OK(botan_pubkey_destroy, (a_pub)); + TEST_FFI_OK(botan_pubkey_destroy, (b_pub)); + TEST_FFI_OK(botan_privkey_destroy, (b_priv)); + TEST_FFI_OK(botan_pk_op_key_agreement_destroy, (ka)); + + return result; + } + void do_elgamal_test(botan_privkey_t priv, botan_rng_t rng, Test::Result& result) { TEST_FFI_OK(botan_privkey_check_key, (priv, rng, 0)); |