diff options
author | Jose Pereira <[email protected]> | 2018-09-06 23:18:45 -0700 |
---|---|---|
committer | Jose Pereira <[email protected]> | 2018-09-09 14:04:13 -0700 |
commit | 77e5fed44b3e24445b06ed80eaa31978081469b5 (patch) | |
tree | 9edf0c3e13980937395f1d96587fc28496f6dd78 | |
parent | d3b55672f63621c82d5a9d5b7be2e6af319006d6 (diff) |
Add CommonCrypto cipher modes support
-rw-r--r-- | src/lib/modes/cipher_mode.cpp | 19 | ||||
-rw-r--r-- | src/lib/prov/commoncrypto/commoncrypto.h | 23 | ||||
-rw-r--r-- | src/lib/prov/commoncrypto/commoncrypto_mode.cpp | 240 | ||||
-rw-r--r-- | src/lib/prov/commoncrypto/commoncrypto_utils.cpp | 149 | ||||
-rw-r--r-- | src/lib/prov/commoncrypto/commoncrypto_utils.h | 32 | ||||
-rw-r--r-- | src/lib/prov/commoncrypto/info.txt | 4 |
6 files changed, 461 insertions, 6 deletions
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..aadccfdca 100644 --- a/src/lib/prov/commoncrypto/commoncrypto.h +++ b/src/lib/prov/commoncrypto/commoncrypto.h @@ -16,14 +16,27 @@ namespace Botan { +class Cipher_Mode; 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); /* Hash */ 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..fc841f45c --- /dev/null +++ b/src/lib/prov/commoncrypto/commoncrypto_utils.cpp @@ -0,0 +1,149 @@ +/* +* 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(kCCKeySize3DES);//, 16, 24, 8); + } + else if(algo_name == "Blowfish") + { + opts.algo = kCCAlgorithmBlowfish; + opts.block_size = kCCBlockSizeBlowfish; + opts.key_spec = Key_Length_Specification(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish);//, 1, 56, 1); + } + else if(algo_name == "CAST-128") + { + opts.algo = kCCAlgorithmCAST; + opts.block_size = kCCBlockSizeCAST; + opts.key_spec = Key_Length_Specification(kCCKeySizeMinCAST, kCCKeySizeMaxCAST);//, 1, 16, 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; + } +} diff --git a/src/lib/prov/commoncrypto/commoncrypto_utils.h b/src/lib/prov/commoncrypto/commoncrypto_utils.h new file mode 100644 index 000000000..9847c292a --- /dev/null +++ b/src/lib/prov/commoncrypto/commoncrypto_utils.h @@ -0,0 +1,32 @@ +/* +* 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); + +} + +#endif diff --git a/src/lib/prov/commoncrypto/info.txt b/src/lib/prov/commoncrypto/info.txt index 00f92cd58..8d5e4d50c 100644 --- a/src/lib/prov/commoncrypto/info.txt +++ b/src/lib/prov/commoncrypto/info.txt @@ -11,3 +11,7 @@ commoncrypto.h <os_features> commoncrypto </os_features> + +<requires> +modes +</requires> |