aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/ffi/ffi_cipher.cpp1
-rw-r--r--src/lib/modes/cbc/cbc.cpp8
-rw-r--r--src/lib/prov/commoncrypto/commoncrypto_block.cpp2
-rw-r--r--src/lib/prov/commoncrypto/commoncrypto_mode.cpp12
-rw-r--r--src/lib/prov/commoncrypto/commoncrypto_utils.cpp10
-rw-r--r--src/lib/prov/openssl/openssl_mode.cpp13
-rw-r--r--src/tests/data/modes/cbc.vec12
-rw-r--r--src/tests/test_aead.cpp32
-rw-r--r--src/tests/test_modes.cpp243
9 files changed, 206 insertions, 127 deletions
diff --git a/src/lib/ffi/ffi_cipher.cpp b/src/lib/ffi/ffi_cipher.cpp
index ec6893405..7b672d407 100644
--- a/src/lib/ffi/ffi_cipher.cpp
+++ b/src/lib/ffi/ffi_cipher.cpp
@@ -167,6 +167,7 @@ int botan_cipher_update(botan_cipher_t cipher_obj,
while(input_size >= ud && output_size >= ud)
{
+ // FIXME we can use process here and avoid the copy
copy_mem(mbuf.data(), input, ud);
cipher.update(mbuf);
diff --git a/src/lib/modes/cbc/cbc.cpp b/src/lib/modes/cbc/cbc.cpp
index 76b78e4f6..c01fc4328 100644
--- a/src/lib/modes/cbc/cbc.cpp
+++ b/src/lib/modes/cbc/cbc.cpp
@@ -2,6 +2,7 @@
* 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)
*/
@@ -65,6 +66,7 @@ bool CBC_Mode::valid_nonce_length(size_t n) const
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)
@@ -124,6 +126,7 @@ size_t CBC_Encryption::process(uint8_t buf[], size_t 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();
@@ -155,6 +158,7 @@ size_t CTS_Encryption::output_length(size_t input_length) const
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;
@@ -237,6 +241,7 @@ size_t CBC_Decryption::process(uint8_t buf[], size_t 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;
@@ -257,7 +262,7 @@ void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
void CBC_Decryption::reset()
{
- zeroise(state());
+ CBC_Mode::reset();
zeroise(m_tempbuf);
}
@@ -273,6 +278,7 @@ size_t CTS_Decryption::minimum_final_size() const
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;
diff --git a/src/lib/prov/commoncrypto/commoncrypto_block.cpp b/src/lib/prov/commoncrypto/commoncrypto_block.cpp
index e912ed57e..b3680506b 100644
--- a/src/lib/prov/commoncrypto/commoncrypto_block.cpp
+++ b/src/lib/prov/commoncrypto/commoncrypto_block.cpp
@@ -143,7 +143,7 @@ make_commoncrypto_block_cipher(const std::string& name)
CommonCryptor_Opts opts = commoncrypto_opts_from_algo(name);
return std::unique_ptr<BlockCipher>(new CommonCrypto_BlockCipher(name, opts));
}
- catch(CommonCrypto_Error e)
+ catch(CommonCrypto_Error& e)
{
return nullptr;
}
diff --git a/src/lib/prov/commoncrypto/commoncrypto_mode.cpp b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
index baae11679..a3a27637a 100644
--- a/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
+++ b/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
@@ -120,6 +120,7 @@ void CommonCrypto_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
size_t offset)
{
verify_key_set(m_key_set);
+ BOTAN_STATE_CHECK(m_nonce_set);
BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
uint8_t* buf = buffer.data() + offset;
@@ -153,7 +154,10 @@ size_t CommonCrypto_Cipher_Mode::update_granularity() const
size_t CommonCrypto_Cipher_Mode::minimum_final_size() const
{
- return 0;
+ if(m_direction == ENCRYPTION)
+ return 0;
+ else
+ return m_opts.block_size;
}
size_t CommonCrypto_Cipher_Mode::default_nonce_length() const
@@ -196,6 +200,9 @@ void CommonCrypto_Cipher_Mode::reset()
{
return;
}
+
+ m_nonce_set = false;
+
CCCryptorStatus status = CCCryptorReset(m_cipher, nullptr);
if(status != kCCSuccess)
{
@@ -220,6 +227,7 @@ void CommonCrypto_Cipher_Mode::key_schedule(const uint8_t key[], size_t length)
}
m_key_set = true;
+ m_nonce_set = false;
}
}
@@ -232,7 +240,7 @@ make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction)
CommonCryptor_Opts opts = commoncrypto_opts_from_algo(name);
return new CommonCrypto_Cipher_Mode(name, direction, opts);
}
- catch(CommonCrypto_Error e)
+ 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 62736b88c..9ec9c30ee 100644
--- a/src/lib/prov/commoncrypto/commoncrypto_utils.cpp
+++ b/src/lib/prov/commoncrypto/commoncrypto_utils.cpp
@@ -133,14 +133,16 @@ CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo)
throw CommonCrypto_Error("Unsupported cipher mode!");
}
- if(cipher_mode_padding.empty() || cipher_mode_padding == "PKCS7")
+ if(cipher_mode_padding == "NoPadding")
{
- opts.padding = ccPKCS7Padding;
+ opts.padding = ccNoPadding;
}
- else if(cipher_mode_padding == "NoPadding")
+ /*
+ else if(cipher_mode_padding.empty() || cipher_mode_padding == "PKCS7")
{
- opts.padding = ccNoPadding;
+ opts.padding = ccPKCS7Padding;
}
+ */
else
{
throw CommonCrypto_Error("Unsupported cipher mode padding!");
diff --git a/src/lib/prov/openssl/openssl_mode.cpp b/src/lib/prov/openssl/openssl_mode.cpp
index ce98e0c62..5636d008f 100644
--- a/src/lib/prov/openssl/openssl_mode.cpp
+++ b/src/lib/prov/openssl/openssl_mode.cpp
@@ -86,11 +86,20 @@ void OpenSSL_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
if(!valid_nonce_length(nonce_len))
throw Invalid_IV_Length(name(), nonce_len);
+
if(nonce_len)
{
if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nonce, -1))
throw OpenSSL_Error("EVP_CipherInit_ex nonce");
}
+ else if(m_nonce_set == false)
+ {
+ const std::vector<uint8_t> zeros(m_block_size);
+ if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, zeros.data(), -1))
+ throw OpenSSL_Error("EVP_CipherInit_ex nonce");
+ }
+ // otherwise existing CBC state left unchanged
+
m_nonce_set = true;
}
@@ -116,6 +125,7 @@ void OpenSSL_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
size_t offset)
{
verify_key_set(m_key_set);
+ BOTAN_STATE_CHECK(m_nonce_set);
BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
uint8_t* buf = buffer.data() + offset;
@@ -163,6 +173,7 @@ size_t OpenSSL_Cipher_Mode::output_length(size_t input_length) const
void OpenSSL_Cipher_Mode::clear()
{
m_key_set = false;
+ m_nonce_set = false;
const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_cipher);
@@ -180,6 +191,7 @@ void OpenSSL_Cipher_Mode::reset()
{
if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nullptr, -1))
throw OpenSSL_Error("EVP_CipherInit_ex clear");
+ m_nonce_set = false;
}
Key_Length_Specification OpenSSL_Cipher_Mode::key_spec() const
@@ -194,6 +206,7 @@ void OpenSSL_Cipher_Mode::key_schedule(const uint8_t key[], size_t length)
if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, key, nullptr, -1))
throw OpenSSL_Error("EVP_CipherInit_ex key");
m_key_set = true;
+ m_nonce_set = false;
}
}
diff --git a/src/tests/data/modes/cbc.vec b/src/tests/data/modes/cbc.vec
index 966d23108..05d30ab7e 100644
--- a/src/tests/data/modes/cbc.vec
+++ b/src/tests/data/modes/cbc.vec
@@ -1205,6 +1205,11 @@ Nonce = 000102030405060708090A0B0C0D0E0F
In = 6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710
Out = 7649ABAC8119B246CEE98E9B12E9197D5086CB9B507219EE95DB113A917678B273BED6B8E3C1743B7116E69E222295163FF1CAA1681FAC09120ECA307586E1A7
+Key = 2B7E151628AED2A6ABF7158809CF4F3C
+Nonce = 00000000000000000000000000000000
+In = 6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710
+Out = 3AD77BB40D7A3660A89ECAF32466EF97B148C17F309EE692287AE57CF12ADD49C93D11BFAF08C5DC4D90B37B4DEE002BA7356E1207BB406639E5E5CEB9A9ED93
+
[AES-192/CBC/NoPadding]
Key = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B
Nonce = 000102030405060708090A0B0C0D0E0F
@@ -1217,13 +1222,6 @@ Nonce = 000102030405060708090A0B0C0D0E0F
In = 6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710
Out = F58C4C04D6E5F1BA779EABFB5F7BFBD69CFC4E967EDB808D679F777BC6702C7D39F23369A9D9BACFA530E26304231461B2EB05E2C39BE9FCDA6C19078C6A9D1B
-# test empty nonce, must be equivalent to zero
-[AES-128/CBC/NoPadding]
-Key = 2B7E151628AED2A6ABF7158809CF4F3C
-Nonce =
-In = 6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710
-Out = 3AD77BB40D7A3660A89ECAF32466EF97B148C17F309EE692287AE57CF12ADD49C93D11BFAF08C5DC4D90B37B4DEE002BA7356E1207BB406639E5E5CEB9A9ED93
-
# RFC 3962: Advanced Encryption Standard (AES) Encryption for Kerberos 5
[AES-128/CBC/CTS]
Key = 636869636b656e207465726979616b69
diff --git a/src/tests/test_aead.cpp b/src/tests/test_aead.cpp
index cf89a58bd..003f7c886 100644
--- a/src/tests/test_aead.cpp
+++ b/src/tests/test_aead.cpp
@@ -26,6 +26,8 @@ class AEAD_Tests final : public Text_Based_Test
const std::vector<uint8_t>& input, const std::vector<uint8_t>& expected,
const std::vector<uint8_t>& ad, const std::string& algo)
{
+ const bool is_siv = algo.find("/SIV") != std::string::npos;
+
Test::Result result(algo);
std::unique_ptr<Botan::AEAD_Mode> enc(Botan::AEAD_Mode::create(algo, Botan::ENCRYPTION));
@@ -35,8 +37,16 @@ class AEAD_Tests final : public Text_Based_Test
result.confirm("AEAD name is not empty", !enc->name().empty());
result.confirm("AEAD default nonce size is accepted", enc->valid_nonce_length(enc->default_nonce_length()));
+ Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(enc->update_granularity());
+
+ if(is_siv == false)
+ {
+ result.test_throws("Unkeyed object throws for encrypt",
+ [&]() { enc->update(garbage); });
+ }
+
result.test_throws("Unkeyed object throws for encrypt",
- [&]() { Botan::secure_vector<uint8_t> buf; enc->finish(buf); });
+ [&]() { enc->finish(garbage); });
if(enc->associated_data_requires_key())
{
@@ -47,9 +57,7 @@ class AEAD_Tests final : public Text_Based_Test
// Ensure that test resets AD and message state
enc->set_key(key);
- Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(enc->update_granularity());
-
- if(algo.find("/SIV") == std::string::npos)
+ if(is_siv == false)
{
result.test_throws("Cannot process data until nonce is set (enc)",
[&]() { enc->update(garbage); });
@@ -166,14 +174,24 @@ class AEAD_Tests final : public Text_Based_Test
const std::vector<uint8_t>& input, const std::vector<uint8_t>& expected,
const std::vector<uint8_t>& ad, const std::string& algo)
{
+ const bool is_siv = algo.find("/SIV") != std::string::npos;
+
Test::Result result(algo);
std::unique_ptr<Botan::AEAD_Mode> dec(Botan::AEAD_Mode::create(algo, Botan::DECRYPTION));
result.test_eq("AEAD decrypt output_length is correct", dec->output_length(input.size()), expected.size());
+ Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(dec->update_granularity());
+
+ if(is_siv == false)
+ {
+ result.test_throws("Unkeyed object throws for decrypt",
+ [&]() { dec->update(garbage); });
+ }
+
result.test_throws("Unkeyed object throws for decrypt",
- [&]() { Botan::secure_vector<uint8_t> buf; dec->finish(buf); });
+ [&]() { dec->finish(garbage); });
if(dec->associated_data_requires_key())
{
@@ -186,9 +204,7 @@ class AEAD_Tests final : public Text_Based_Test
dec->set_key(key);
dec->set_ad(mutate_vec(ad));
- Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(dec->update_granularity());
-
- if(algo.find("/SIV") == std::string::npos)
+ if(is_siv == false)
{
result.test_throws("Cannot process data until nonce is set (dec)",
[&]() { dec->update(garbage); });
diff --git a/src/tests/test_modes.cpp b/src/tests/test_modes.cpp
index dbfa3d2bf..bb564ee9d 100644
--- a/src/tests/test_modes.cpp
+++ b/src/tests/test_modes.cpp
@@ -1,6 +1,7 @@
/*
* (C) 2014,2015,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)
*/
@@ -52,152 +53,186 @@ class Cipher_Mode_Tests final : public Text_Based_Test
if(!enc || !dec)
{
+ if(enc)
+ result.test_failure("Provider " + provider_ask + " has encrypt but not decrypt");
+ if(dec)
+ result.test_failure("Provider " + provider_ask + " has decrypt but not encrypt");
result.note_missing(algo);
return result;
}
- result.test_is_nonempty("provider", enc->provider());
- result.test_eq("name", enc->name(), algo);
+ result.test_eq("enc and dec granularity is the same",
+ enc->update_granularity(), dec->update_granularity());
- result.test_eq("mode not authenticated", enc->authenticated(), false);
+ test_mode(result, algo, provider_ask, "encryption", *enc, key, nonce, input, expected);
+ test_mode(result, algo, provider_ask, "decryption", *dec, key, nonce, expected, input);
+ }
+
+ return result;
+ }
+
+ private:
+ void test_mode(Test::Result& result,
+ const std::string& algo,
+ const std::string& provider,
+ const std::string& direction,
+ Botan::Cipher_Mode& mode,
+ const std::vector<uint8_t>& key,
+ const std::vector<uint8_t>& nonce,
+ const std::vector<uint8_t>& input,
+ const std::vector<uint8_t>& expected)
+ {
+ const bool is_cbc = (algo.find("/CBC") != std::string::npos);
+ const bool is_ctr = (algo.find("CTR") != std::string::npos);
+
+ result.test_eq("name", mode.name(), algo);
- result.test_throws("Unkeyed object throws for encrypt",
- [&]() { Botan::secure_vector<uint8_t> bad(16); enc->finish(bad); });
- result.test_throws("Unkeyed object throws for decrypt",
- [&]() { Botan::secure_vector<uint8_t> bad(16); dec->finish(bad); });
+ // Some modes report base even if got from another provider
+ if(mode.provider() != "base")
+ {
+ result.test_eq("provider", mode.provider(), provider);
+ }
- if(algo.find("/CTR") == std::string::npos)
+ result.test_eq("mode not authenticated", mode.authenticated(), false);
+
+ const size_t update_granularity = mode.update_granularity();
+ const size_t min_final_bytes = mode.minimum_final_size();
+
+ // FFI currently requires this, so assure it is true for all modes
+ result.test_gte("buffer sizes ok", update_granularity, min_final_bytes);
+
+ result.test_throws("Unkeyed object throws", [&]() {
+ Botan::secure_vector<uint8_t> bad(update_granularity);
+ mode.finish(bad);
+ });
+
+ if(is_cbc)
+ {
+ // can't test equal due to CBC padding
+
+ if(direction == "encryption")
{
- // can't test equal due to CBC padding
- result.test_lte("output_length", enc->output_length(input.size()), expected.size());
- result.test_gte("output_length", dec->output_length(expected.size()), input.size());
+ result.test_lte("output_length", mode.output_length(input.size()), expected.size());
}
else
{
- // assume all other modes are not expanding (currently true)
- result.test_eq("output_length", enc->output_length(input.size()), expected.size());
- result.test_eq("output_length", dec->output_length(expected.size()), input.size());
+ result.test_gte("output_length", mode.output_length(input.size()), expected.size());
}
+ }
+ else
+ {
+ // assume all other modes are not expanding (currently true)
+ result.test_eq("output_length", mode.output_length(input.size()), expected.size());
+ }
- // FFI currently requires this, so assure it is true for all modes
- result.test_gte("enc buffer sizes ok", enc->update_granularity(), enc->minimum_final_size());
- result.test_gte("dec buffer sizes ok", dec->update_granularity(), dec->minimum_final_size());
+ result.confirm("default nonce size is allowed",
+ mode.valid_nonce_length(mode.default_nonce_length()));
- result.confirm("default nonce size is allowed",
- enc->valid_nonce_length(enc->default_nonce_length()));
- result.confirm("default nonce size is allowed",
- dec->valid_nonce_length(dec->default_nonce_length()));
+ // Test that disallowed nonce sizes result in an exception
+ const size_t large_nonce_size = 65000;
+ result.test_eq("Large nonce not allowed", mode.valid_nonce_length(large_nonce_size), false);
+ result.test_throws("Large nonce causes exception",
+ [&mode,large_nonce_size]() { mode.start(nullptr, large_nonce_size); });
- // Test that disallowed nonce sizes result in an exception
- const size_t large_nonce_size = 65000;
- result.test_eq("Large nonce not allowed", enc->valid_nonce_length(large_nonce_size), false);
- result.test_throws("Large nonce causes exception",
- [&enc,large_nonce_size]() { enc->start(nullptr, large_nonce_size); });
+ Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(update_granularity);
- // Test to make sure reset() resets what we need it to
- enc->set_key(mutate_vec(key));
- Botan::secure_vector<uint8_t> garbage = Test::rng().random_vec(enc->update_granularity());
+ // Test to make sure reset() resets what we need it to
+ result.test_throws("Cannot process data (update) until key is set",
+ [&]() { mode.update(garbage); });
+ result.test_throws("Cannot process data (finish) until key is set",
+ [&]() { mode.finish(garbage); });
- if(algo.find("CTR") == std::string::npos)
- {
- result.test_throws("Cannot process data until nonce is set (enc)",
- [&]() { enc->update(garbage); });
- }
+ mode.set_key(mutate_vec(key));
- enc->start(mutate_vec(nonce));
- enc->update(garbage);
+ if(is_ctr == false)
+ {
+ result.test_throws("Cannot process data until nonce is set",
+ [&]() { mode.update(garbage); });
+ }
- enc->reset();
+ mode.start(mutate_vec(nonce));
+ mode.reset();
- enc->set_key(key);
- enc->start(nonce);
+ if(is_ctr == false)
+ {
+ result.test_throws("Cannot process data until nonce is set (after start/reset)",
+ [&]() { mode.update(garbage); });
+ }
- Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
- // TODO: should first update if possible
- enc->finish(buf);
- result.test_eq("encrypt", buf, expected);
+ mode.start(mutate_vec(nonce));
+ mode.update(garbage);
- // additionally test process() if possible
- size_t update_granularity = enc->update_granularity();
- size_t input_length = input.size();
- size_t min_final_bytes = enc->minimum_final_size();
- if(input_length > (update_granularity + min_final_bytes))
- {
- // reset state first
- enc->reset();
+ mode.reset();
- enc->start(nonce);
- buf.assign(input.begin(), input.end());
+ mode.set_key(key);
+ mode.start(nonce);
- // we can process at max input_length
- const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity;
- const size_t bytes_to_process = max_blocks_to_process * update_granularity;
+ Botan::secure_vector<uint8_t> buf;
- const size_t bytes_written = enc->process(buf.data(), bytes_to_process);
+ buf.assign(input.begin(), input.end());
+ mode.finish(buf);
+ result.test_eq(direction + " all-in-one", buf, expected);
- result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
+ // additionally test update() and process() if possible
+ if(input.size() >= update_granularity + min_final_bytes)
+ {
+ const size_t max_blocks_to_process = (input.size() - min_final_bytes) / update_granularity;
+ const size_t bytes_to_process = max_blocks_to_process * update_granularity;
- enc->finish(buf, bytes_to_process);
- result.test_eq("encrypt", buf, expected);
- }
+ // test update, 1 block at a time
+ if(max_blocks_to_process > 1)
+ {
+ Botan::secure_vector<uint8_t> block(update_granularity);
+ buf.clear();
- // decryption
- buf.assign(expected.begin(), expected.end());
+ mode.start(nonce);
+ for(size_t i = 0; i != max_blocks_to_process; ++i)
+ {
+ block.assign(input.data() + i*update_granularity,
+ input.data() + (i+1)*update_granularity);
- // Test to make sure reset() resets what we need it to
- dec->set_key(mutate_vec(key));
- garbage = Test::rng().random_vec(dec->update_granularity());
+ mode.update(block);
+ buf += block;
+ }
- if(algo.find("CTR") == std::string::npos)
- {
- result.test_throws("Cannot process data until nonce is set (dec)",
- [&]() { dec->update(garbage); });
- }
+ Botan::secure_vector<uint8_t> last_bits(input.data() + bytes_to_process, input.data() + input.size());
+ mode.finish(last_bits);
+ buf += last_bits;
- dec->start(mutate_vec(nonce));
- dec->update(garbage);
+ result.test_eq(direction + " update-1", buf, expected);
+ }
- dec->reset();
+ // test update with maximum length input
+ buf.assign(input.data(), input.data() + bytes_to_process);
+ Botan::secure_vector<uint8_t> last_bits(input.data() + bytes_to_process, input.data() + input.size());
- dec->set_key(key);
- dec->start(nonce);
- dec->finish(buf);
- result.test_eq("decrypt", buf, input);
+ mode.start(nonce);
+ mode.update(buf);
+ mode.finish(last_bits);
- // additionally test process() if possible
- update_granularity = dec->update_granularity();
- input_length = expected.size();
- min_final_bytes = dec->minimum_final_size();
- if(input_length > (update_granularity + min_final_bytes))
- {
- // reset state first
- dec->reset();
+ buf += last_bits;
- dec->start(nonce);
- buf.assign(expected.begin(), expected.end());
+ result.test_eq(direction + " update-all", buf, expected);
- // we can process at max input_length
- const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity;
- const size_t bytes_to_process = max_blocks_to_process * update_granularity;
+ // test process with maximum length input
+ mode.start(nonce);
+ buf.assign(input.begin(), input.end());
- const size_t bytes_written = dec->process(buf.data(), bytes_to_process);
+ const size_t bytes_written = mode.process(buf.data(), bytes_to_process);
- result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
+ result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
- dec->finish(buf, bytes_to_process);
- result.test_eq("decrypt", buf, input);
- }
+ mode.finish(buf, bytes_to_process);
+ result.test_eq(direction + " process", buf, expected);
+ }
- enc->clear();
- dec->clear();
+ mode.clear();
- result.test_throws("Unkeyed object throws for encrypt after clear",
- [&]() { Botan::secure_vector<uint8_t> bad(16); enc->finish(bad); });
- result.test_throws("Unkeyed object throws for decrypt after clear",
- [&]() { Botan::secure_vector<uint8_t> bad(16); dec->finish(bad); });
- }
+ result.test_throws("Unkeyed object throws after clear", [&]() {
+ Botan::secure_vector<uint8_t> bad(update_granularity);
+ mode.finish(bad);
+ });
- return result;
}
};