aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose Pereira <[email protected]>2018-09-06 23:18:45 -0700
committerJose Pereira <[email protected]>2018-09-09 14:04:13 -0700
commit77e5fed44b3e24445b06ed80eaa31978081469b5 (patch)
tree9edf0c3e13980937395f1d96587fc28496f6dd78
parentd3b55672f63621c82d5a9d5b7be2e6af319006d6 (diff)
Add CommonCrypto cipher modes support
-rw-r--r--src/lib/modes/cipher_mode.cpp19
-rw-r--r--src/lib/prov/commoncrypto/commoncrypto.h23
-rw-r--r--src/lib/prov/commoncrypto/commoncrypto_mode.cpp240
-rw-r--r--src/lib/prov/commoncrypto/commoncrypto_utils.cpp149
-rw-r--r--src/lib/prov/commoncrypto/commoncrypto_utils.h32
-rw-r--r--src/lib/prov/commoncrypto/info.txt4
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>