From 364119259ca7dc091f437a72d16cb575d0c48db3 Mon Sep 17 00:00:00 2001 From: Jose Pereira Date: Sun, 9 Sep 2018 14:59:35 -0700 Subject: Add CommonCrypto block cipher support --- src/lib/block/block_cipher.cpp | 18 ++- src/lib/prov/commoncrypto/commoncrypto.h | 6 + src/lib/prov/commoncrypto/commoncrypto_block.cpp | 152 +++++++++++++++++++++++ src/lib/prov/commoncrypto/commoncrypto_utils.cpp | 44 ++++++- src/lib/prov/commoncrypto/commoncrypto_utils.h | 4 + src/lib/prov/commoncrypto/info.txt | 1 + 6 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 src/lib/prov/commoncrypto/commoncrypto_block.cpp (limited to 'src') 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 #endif +#if defined(BOTAN_HAS_COMMONCRYPTO) + #include +#endif + namespace Botan { std::unique_ptr 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 BlockCipher::providers(const std::string& algo) { - return probe_providers_of(algo, { "base", "openssl" }); + return probe_providers_of(algo, { "base", "openssl", "commoncrypto" }); } } diff --git a/src/lib/prov/commoncrypto/commoncrypto.h b/src/lib/prov/commoncrypto/commoncrypto.h index aadccfdca..958cbab7d 100644 --- a/src/lib/prov/commoncrypto/commoncrypto.h +++ b/src/lib/prov/commoncrypto/commoncrypto.h @@ -17,6 +17,7 @@ namespace Botan { class Cipher_Mode; +class BlockCipher; class HashFunction; enum Cipher_Dir : int; typedef int32_t CCCryptorStatus; @@ -38,6 +39,11 @@ class BOTAN_PUBLIC_API(2, 0) CommonCrypto_Error final : public Exception Cipher_Mode* make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction); +/* Block Ciphers */ + +std::unique_ptr +make_commoncrypto_block_cipher(const std::string& name); + /* Hash */ std::unique_ptr make_commoncrypto_hash(const std::string& name); 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 + +#include +#include +#include + +#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 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 +make_commoncrypto_block_cipher(const std::string& name) + { + + try + { + CommonCryptor_Opts opts = commoncrypto_opts_from_algo(name); + return std::unique_ptr(new CommonCrypto_BlockCipher(name, 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 index fc841f45c..62736b88c 100644 --- a/src/lib/prov/commoncrypto/commoncrypto_utils.cpp +++ b/src/lib/prov/commoncrypto/commoncrypto_utils.cpp @@ -90,19 +90,21 @@ CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo) { opts.algo = kCCAlgorithm3DES; opts.block_size = kCCBlockSize3DES; - opts.key_spec = Key_Length_Specification(kCCKeySize3DES);//, 16, 24, 8); + 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(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish);//, 1, 56, 1); + opts.key_spec = Key_Length_Specification(1, kCCKeySizeMaxBlowfish, 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); + // 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 { @@ -146,4 +148,40 @@ CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo) return opts; } + + +void commoncrypto_adjust_key_size(const uint8_t key[], size_t length, + const CommonCryptor_Opts& opts, secure_vector& 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& full_key); + + } #endif diff --git a/src/lib/prov/commoncrypto/info.txt b/src/lib/prov/commoncrypto/info.txt index 8d5e4d50c..d56fe5731 100644 --- a/src/lib/prov/commoncrypto/info.txt +++ b/src/lib/prov/commoncrypto/info.txt @@ -14,4 +14,5 @@ commoncrypto modes +block -- cgit v1.2.3