aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-09-19 11:45:56 -0400
committerJack Lloyd <[email protected]>2017-09-19 11:45:56 -0400
commitc1476d8014f462a1e572396467995f6e596d67b2 (patch)
tree400f9913152663fa37f6470cad52494092d9f214 /src
parent050c02433e810b0516130ea0d436226f25cacc2b (diff)
parent92245ad040b8f0e08a4a57137be5739e5c7bfbdc (diff)
Merge GH #1205 Support large blocks in OCB mode
Diffstat (limited to 'src')
-rw-r--r--src/lib/modes/aead/ocb/ocb.cpp185
-rw-r--r--src/lib/modes/aead/ocb/ocb.h11
-rw-r--r--src/tests/data/aead/ocb.vec29
-rw-r--r--src/tests/data/ocb_wide.vec60
-rw-r--r--src/tests/data/ocb_wide_long.vec15
-rw-r--r--src/tests/test_ocb.cpp237
-rw-r--r--src/tests/tests.cpp5
7 files changed, 479 insertions, 63 deletions
diff --git a/src/lib/modes/aead/ocb/ocb.cpp b/src/lib/modes/aead/ocb/ocb.cpp
index 4e1076cba..a48f0751a 100644
--- a/src/lib/modes/aead/ocb/ocb.cpp
+++ b/src/lib/modes/aead/ocb/ocb.cpp
@@ -1,6 +1,6 @@
/*
* OCB Mode
-* (C) 2013 Jack Lloyd
+* (C) 2013,2017 Jack Lloyd
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
@@ -30,16 +30,18 @@ class L_computer
const secure_vector<uint8_t>& operator()(size_t i) const { return get(i); }
- const secure_vector<uint8_t>& compute_offsets(secure_vector<uint8_t>& offset,
- size_t block_index,
- size_t blocks) const
+ const secure_vector<uint8_t>&
+ compute_offsets(secure_vector<uint8_t>& offset,
+ size_t block_index,
+ size_t blocks,
+ size_t BS) const
{
- m_offset_buf.resize(blocks * 16);
+ m_offset_buf.resize(blocks * BS);
for(size_t i = 0; i != blocks; ++i)
{ // could be done in parallel
offset ^= get(ctz(block_index + 1 + i));
- copy_mem(&m_offset_buf[16*i], offset.data(), 16);
+ copy_mem(&m_offset_buf[BS*i], offset.data(), BS);
}
return m_offset_buf;
@@ -72,16 +74,17 @@ namespace {
* OCB's HASH
*/
secure_vector<uint8_t> ocb_hash(const L_computer& L,
- const BlockCipher& cipher,
- const uint8_t ad[], size_t ad_len)
+ const BlockCipher& cipher,
+ const uint8_t ad[], size_t ad_len)
{
- secure_vector<uint8_t> sum(16);
- secure_vector<uint8_t> offset(16);
+ const size_t BS = cipher.block_size();
+ secure_vector<uint8_t> sum(BS);
+ secure_vector<uint8_t> offset(BS);
- secure_vector<uint8_t> buf(16);
+ secure_vector<uint8_t> buf(BS);
- const size_t ad_blocks = (ad_len / 16);
- const size_t ad_remainder = (ad_len % 16);
+ const size_t ad_blocks = (ad_len / BS);
+ const size_t ad_remainder = (ad_len % BS);
for(size_t i = 0; i != ad_blocks; ++i)
{
@@ -89,7 +92,7 @@ secure_vector<uint8_t> ocb_hash(const L_computer& L,
offset ^= L(ctz(i+1));
buf = offset;
- xor_buf(buf.data(), &ad[16*i], 16);
+ xor_buf(buf.data(), &ad[BS*i], BS);
cipher.encrypt(buf);
@@ -101,8 +104,8 @@ secure_vector<uint8_t> ocb_hash(const L_computer& L,
offset ^= L.star();
buf = offset;
- xor_buf(buf.data(), &ad[16*ad_blocks], ad_remainder);
- buf[ad_len % 16] ^= 0x80;
+ xor_buf(buf.data(), &ad[BS*ad_blocks], ad_remainder);
+ buf[ad_len % BS] ^= 0x80;
cipher.encrypt(buf);
@@ -117,14 +120,21 @@ secure_vector<uint8_t> ocb_hash(const L_computer& L,
OCB_Mode::OCB_Mode(BlockCipher* cipher, size_t tag_size) :
m_cipher(cipher),
m_checksum(m_cipher->parallel_bytes()),
- m_offset(16),
- m_ad_hash(16),
+ m_offset(m_cipher->block_size()),
+ m_ad_hash(m_cipher->block_size()),
m_tag_size(tag_size)
{
- if(m_cipher->block_size() != 16)
- throw Invalid_Argument("OCB requires 128 bit cipher");
+ const size_t BS = m_cipher->block_size();
- if(m_tag_size % 4 != 0 || m_tag_size < 8 || m_tag_size > 16)
+ /*
+ * draft-krovetz-ocb-wide-d1 specifies OCB for several other block
+ * sizes but only 128, 192, 256 and 512 bit are currently supported
+ * by this implementation.
+ */
+ if(BS != 16 && BS != 24 && BS != 32 && BS != 64)
+ throw Invalid_Argument("OCB does not support cipher " + m_cipher->name());
+
+ if(m_tag_size % 4 != 0 || m_tag_size < 8 || m_tag_size > BS || m_tag_size > 32)
throw Invalid_Argument("Invalid OCB tag length");
}
@@ -149,12 +159,17 @@ void OCB_Mode::reset()
bool OCB_Mode::valid_nonce_length(size_t length) const
{
- return (length > 0 && length < m_cipher->block_size());
+ if(length == 0)
+ return false;
+ if(m_cipher->block_size() == 16)
+ return length < 16;
+ else
+ return length < (m_cipher->block_size() - 1);
}
std::string OCB_Mode::name() const
{
- return m_cipher->name() + "/OCB"; // include tag size
+ return m_cipher->name() + "/OCB"; // include tag size?
}
size_t OCB_Mode::update_granularity() const
@@ -182,16 +197,25 @@ void OCB_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
secure_vector<uint8_t>
OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len)
{
- BOTAN_ASSERT(nonce_len < 16, "OCB nonce is less than cipher block size");
+ const size_t BS = m_cipher->block_size();
+
+ BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64,
+ "OCB block size is supported");
+
+ const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
+
+ const uint8_t BOTTOM_MASK =
+ static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
+
+ secure_vector<uint8_t> nonce_buf(BS);
- secure_vector<uint8_t> nonce_buf(16);
+ copy_mem(&nonce_buf[BS - nonce_len], nonce, nonce_len);
+ nonce_buf[0] = ((tag_size()*8) % (BS*8)) << (BS <= 16 ? 1 : 0);
- copy_mem(&nonce_buf[16 - nonce_len], nonce, nonce_len);
- nonce_buf[0] = ((tag_size() * 8) % 128) << 1;
- nonce_buf[16 - nonce_len - 1] = 1;
+ nonce_buf[BS - nonce_len - 1] ^= 1;
- const uint8_t bottom = nonce_buf[16-1] & 0x3F;
- nonce_buf[16-1] &= 0xC0;
+ const uint8_t bottom = nonce_buf[BS-1] & BOTTOM_MASK;
+ nonce_buf[BS-1] &= ~BOTTOM_MASK;
const bool need_new_stretch = (m_last_nonce != nonce_buf);
@@ -201,19 +225,58 @@ OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len)
m_cipher->encrypt(nonce_buf);
- for(size_t i = 0; i != 16 / 2; ++i)
- nonce_buf.push_back(nonce_buf[i] ^ nonce_buf[i+1]);
+ /*
+ The loop bounds (BS vs BS/2) are derived from the relation
+ between the block size and the MASKLEN. Using the terminology
+ of draft-krovetz-ocb-wide, we have to derive enough bits in
+ ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
+
+ +----------+---------+-------+---------+
+ | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
+ +----------+---------+-------+---------+
+ | 32 | 141 | 17 | 4 |
+ | 64 | 27 | 25 | 5 |
+ | 96 | 1601 | 33 | 6 |
+ | 128 | 135 | 8 | 6 |
+ | 192 | 135 | 40 | 7 |
+ | 256 | 1061 | 1 | 8 |
+ | 384 | 4109 | 80 | 8 |
+ | 512 | 293 | 176 | 8 |
+ | 1024 | 524355 | 352 | 9 |
+ +----------+---------+-------+---------+
+ */
+ if(BS == 16)
+ {
+ for(size_t i = 0; i != BS / 2; ++i)
+ nonce_buf.push_back(nonce_buf[i] ^ nonce_buf[i+1]);
+ }
+ else if(BS == 24)
+ {
+ for(size_t i = 0; i != 16; ++i)
+ nonce_buf.push_back(nonce_buf[i] ^ nonce_buf[i+5]);
+ }
+ else if(BS == 32)
+ {
+ for(size_t i = 0; i != BS; ++i)
+ nonce_buf.push_back(nonce_buf[i] ^ (nonce_buf[i] << 1) ^ (nonce_buf[i+1] >> 7));
+ }
+ else if(BS == 64)
+ {
+ for(size_t i = 0; i != BS / 2; ++i)
+ nonce_buf.push_back(nonce_buf[i] ^ nonce_buf[i+22]);
+ }
m_stretch = nonce_buf;
}
// now set the offset from stretch and bottom
-
const size_t shift_bytes = bottom / 8;
const size_t shift_bits = bottom % 8;
- secure_vector<uint8_t> offset(16);
- for(size_t i = 0; i != 16; ++i)
+ BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
+
+ secure_vector<uint8_t> offset(BS);
+ for(size_t i = 0; i != BS; ++i)
{
offset[i] = (m_stretch[i+shift_bytes] << shift_bits);
offset[i] |= (m_stretch[i+shift_bytes+1] >> (8-shift_bits));
@@ -236,16 +299,17 @@ void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks)
{
- const size_t par_blocks = m_checksum.size() / 16;
+ const size_t BS = m_cipher->block_size();
+ const size_t par_blocks = m_checksum.size() / BS;
while(blocks)
{
const size_t proc_blocks = std::min(blocks, par_blocks);
- const size_t proc_bytes = proc_blocks * 16;
+ const size_t proc_bytes = proc_blocks * BS;
BOTAN_ASSERT(m_L, "A key was set");
- const auto& offsets = m_L->compute_offsets(m_offset, m_block_index, proc_blocks);
+ const auto& offsets = m_L->compute_offsets(m_offset, m_block_index, proc_blocks, BS);
xor_buf(m_checksum.data(), buffer, proc_bytes);
@@ -261,27 +325,30 @@ void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks)
size_t OCB_Encryption::process(uint8_t buf[], size_t sz)
{
- BOTAN_ASSERT(sz % 16 == 0, "Invalid OCB input size");
- encrypt(buf, sz / 16);
+ const size_t BS = m_cipher->block_size();
+ BOTAN_ASSERT(sz % BS == 0, "Invalid OCB input size");
+ encrypt(buf, sz / BS);
return sz;
}
void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
{
+ const size_t BS = m_cipher->block_size();
+
BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
const size_t sz = buffer.size() - offset;
uint8_t* buf = buffer.data() + offset;
if(sz)
{
- const size_t final_full_blocks = sz / 16;
- const size_t remainder_bytes = sz - (final_full_blocks * 16);
+ const size_t final_full_blocks = sz / BS;
+ const size_t remainder_bytes = sz - (final_full_blocks * BS);
encrypt(buf, final_full_blocks);
if(remainder_bytes)
{
- BOTAN_ASSERT(remainder_bytes < 16, "Only a partial block left");
+ BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
uint8_t* remainder = &buf[sz - remainder_bytes];
xor_buf(m_checksum.data(), remainder, remainder_bytes);
@@ -289,13 +356,13 @@ void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
m_offset ^= m_L->star(); // Offset_*
- secure_vector<uint8_t> zeros(16);
+ secure_vector<uint8_t> zeros(BS);
m_cipher->encrypt(m_offset, zeros);
xor_buf(remainder, zeros.data(), remainder_bytes);
}
}
- secure_vector<uint8_t> checksum(16);
+ secure_vector<uint8_t> checksum(BS);
// fold checksum
for(size_t i = 0; i != m_checksum.size(); ++i)
@@ -305,9 +372,7 @@ void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
secure_vector<uint8_t> mac = m_offset;
mac ^= checksum;
mac ^= m_L->dollar();
-
m_cipher->encrypt(mac);
-
mac ^= m_ad_hash;
buffer += std::make_pair(mac.data(), tag_size());
@@ -319,18 +384,19 @@ void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks)
{
+ const size_t BS = m_cipher->block_size();
const size_t par_bytes = m_cipher->parallel_bytes();
- BOTAN_ASSERT(par_bytes % 16 == 0, "Cipher is parallel in full blocks");
+ BOTAN_ASSERT(par_bytes % BS == 0, "Cipher is parallel in full blocks");
- const size_t par_blocks = par_bytes / 16;
+ const size_t par_blocks = par_bytes / BS;
while(blocks)
{
const size_t proc_blocks = std::min(blocks, par_blocks);
- const size_t proc_bytes = proc_blocks * 16;
+ const size_t proc_bytes = proc_blocks * BS;
- const auto& offsets = m_L->compute_offsets(m_offset, m_block_index, proc_blocks);
+ const auto& offsets = m_L->compute_offsets(m_offset, m_block_index, proc_blocks, BS);
xor_buf(buffer, offsets.data(), proc_bytes);
m_cipher->decrypt_n(buffer, buffer, proc_blocks);
@@ -346,13 +412,16 @@ void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks)
size_t OCB_Decryption::process(uint8_t buf[], size_t sz)
{
- BOTAN_ASSERT(sz % 16 == 0, "Invalid OCB input size");
- decrypt(buf, sz / 16);
+ const size_t BS = m_cipher->block_size();
+ BOTAN_ASSERT(sz % BS == 0, "Invalid OCB input size");
+ decrypt(buf, sz / BS);
return sz;
}
void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
{
+ const size_t BS = m_cipher->block_size();
+
BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
const size_t sz = buffer.size() - offset;
uint8_t* buf = buffer.data() + offset;
@@ -363,20 +432,20 @@ void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
if(remaining)
{
- const size_t final_full_blocks = remaining / 16;
- const size_t final_bytes = remaining - (final_full_blocks * 16);
+ const size_t final_full_blocks = remaining / BS;
+ const size_t final_bytes = remaining - (final_full_blocks * BS);
decrypt(buf, final_full_blocks);
if(final_bytes)
{
- BOTAN_ASSERT(final_bytes < 16, "Only a partial block left");
+ BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
uint8_t* remainder = &buf[remaining - final_bytes];
m_offset ^= m_L->star(); // Offset_*
- secure_vector<uint8_t> pad(16);
+ secure_vector<uint8_t> pad(BS);
m_cipher->encrypt(m_offset, pad); // P_*
xor_buf(remainder, pad.data(), final_bytes);
@@ -386,7 +455,7 @@ void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
}
}
- secure_vector<uint8_t> checksum(16);
+ secure_vector<uint8_t> checksum(BS);
// fold checksum
for(size_t i = 0; i != m_checksum.size(); ++i)
diff --git a/src/lib/modes/aead/ocb/ocb.h b/src/lib/modes/aead/ocb/ocb.h
index 174812ee3..69f5fde60 100644
--- a/src/lib/modes/aead/ocb/ocb.h
+++ b/src/lib/modes/aead/ocb/ocb.h
@@ -22,6 +22,9 @@ class L_computer;
*
* @see "The OCB Authenticated-Encryption Algorithm" RFC 7253
* https://tools.ietf.org/html/rfc7253
+* @see "OCB For Block Ciphers Without 128-Bit Blocks"
+* (draft-krovetz-ocb-wide-d3) for the extension of OCB to
+* block ciphers with larger block sizes.
* @see Free Licenses http://www.cs.ucdavis.edu/~rogaway/ocb/license.htm
* @see OCB home page http://www.cs.ucdavis.edu/~rogaway/ocb
*/
@@ -47,7 +50,7 @@ class BOTAN_DLL OCB_Mode : public AEAD_Mode
~OCB_Mode();
protected:
/**
- * @param cipher the 128-bit block cipher to use
+ * @param cipher the block cipher to use
* @param tag_size is how big the auth tag will be
*/
OCB_Mode(BlockCipher* cipher, size_t tag_size);
@@ -68,7 +71,7 @@ class BOTAN_DLL OCB_Mode : public AEAD_Mode
secure_vector<uint8_t> update_nonce(const uint8_t nonce[], size_t nonce_len);
- size_t m_tag_size = 0;
+ const size_t m_tag_size = 0;
secure_vector<uint8_t> m_last_nonce;
secure_vector<uint8_t> m_stretch;
};
@@ -77,7 +80,7 @@ class BOTAN_DLL OCB_Encryption final : public OCB_Mode
{
public:
/**
- * @param cipher the 128-bit block cipher to use
+ * @param cipher the block cipher to use
* @param tag_size is how big the auth tag will be
*/
OCB_Encryption(BlockCipher* cipher, size_t tag_size = 16) :
@@ -99,7 +102,7 @@ class BOTAN_DLL OCB_Decryption final : public OCB_Mode
{
public:
/**
- * @param cipher the 128-bit block cipher to use
+ * @param cipher the block cipher to use
* @param tag_size is how big the auth tag will be
*/
OCB_Decryption(BlockCipher* cipher, size_t tag_size = 16) :
diff --git a/src/tests/data/aead/ocb.vec b/src/tests/data/aead/ocb.vec
index f0fbb3646..b2c4e4744 100644
--- a/src/tests/data/aead/ocb.vec
+++ b/src/tests/data/aead/ocb.vec
@@ -248,3 +248,32 @@ Nonce = BBAA9988776655443322110D
AD = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627
In = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627
Out = 1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FDAC4F02AA
+
+# Generated by Botan, unconfirmed result
+[Threefish-512/OCB(32)]
+Key = 1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE8841792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884
+Nonce = D5CA91748410C1751FF8A2F61825
+AD = C5CD9D1850C141E358649994EE701B68
+In = 2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973
+Out = 45EEFFF01CDA61695EA24B036074491FE61B96C94337F0F947FB4E10E679A9F2A825DF8CEA530A2784E5640A768DE536C76A79157E
+
+[SHACAL2/OCB(32)]
+# Generated by Botan, unconfirmed result
+Key = 4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5
+Nonce = BBAA9988776655443322110D
+AD = C5CD9D1850C141E358649994EE701B68
+In = FE80690BEE8A485D11F32965BC9D2A328CF761B6902EF764462AD86498CA6B97
+Out = 0407C5404170DB1A74B7AB712A8FC7D459B3E4412C7ADF632545C05E50FB0C2FE97A92A81371E7F7C04AFA10C68375A31923EDAB327DB776DBBB99ED3318424E
+
+# Generated by OCB reference code calling OpenSSL SHA256_Transform
+Key = 6F98263502C983D78BC3F7B5208D488DC036F7BC1438AB55620CF8FB98767D070FA43C116DBFE9F883E0ADA36DF5302E2C27EA405F9595C1A18DBC3A043A4113
+Nonce = BE3FA1AB2F040615988F275402796F0A614BB4D6E4974FB7BCDB685F8B64
+AD = 1B08E7DCA97599E379D3258CB1A3B0FCE0
+In = DE7E244B3D0D43C0EDF0635BE6948912BB7352
+Out = 2FACBA6F0A62331845ACAB0F60EBA59BD2E4F4BC83A79FC73D2A0B5191C7492798BBDE2476B9D249247D1BD4B8F167333852B3
+
+Key = 8182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0
+Nonce = F0F1
+AD =
+In =
+Out = 13EAF2583F2E24339182D3423D56759F0E05ABDD4682DBF9B1901CCCEC4FD639
diff --git a/src/tests/data/ocb_wide.vec b/src/tests/data/ocb_wide.vec
new file mode 100644
index 000000000..0bed695ef
--- /dev/null
+++ b/src/tests/data/ocb_wide.vec
@@ -0,0 +1,60 @@
+
+Key = 8182838485868788898A8B8C8D8E8F909192939495969798
+Nonce = F0F1
+AD =
+In =
+Out = B9CACBDCDDCECFFF75C0B9AA97808DE6FBCCC1D2AFB8B482
+
+Key = 9192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8
+Nonce = F1F2
+AD = 05060708090A0B0C0D0E0F10
+In = 0102030405060708090A0B0C
+Out = 4EB0BAACA698928B86E59F81B3474B5F53676B404B225E622E1206EADEA2B6BB4F736646
+
+Key = A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8
+Nonce = F2F3
+AD = 060708090A0B0C0D0E0F101112131415161718191A1B1C1D
+In = 02030405060708090A0B0C0D0E0F10111213141516171819
+Out = A8B5AEA7B4E9E2A2E803081982C7C46D36B380908BCEEE0775898DA1B5C9CDFF8522E6DA3E52461A7E92A69B7F132636
+
+Key = B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8
+Nonce = F3F4
+AD = 0708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A
+In = 030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526
+Out = F8F1CEBB841D227D911D024669E08D903E0768250AA3CFF7BC466C3E14D6FC8A3D3ED4A70295B8FBD6416C3B8BA5482AFF18576CA0672A468B4C021C
+
+Key = C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8
+Nonce = F4F5
+AD = 08090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637
+In = 0405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233
+Out = CEFB901163563D53D5AC8764164388D091C40FEAB8ED24B7ECF1824B415C2FC95706359ED4A97B2B720FDC711B66B2915C8BC62509DE93E236965BFB57C04D57F96EE347EB7CF322
+
+Key = 8182838485868788898A8B8C8D8E8F909192939495969798
+Nonce = F0F1F2F3F4F5F6F7F8F9FAFB
+AD =
+In =
+Out = F00F1A7125DACF832FD0F50E6B44615C9996D209B29DF961
+
+Key = 9192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8
+Nonce = F1F2F3F4F5F6F7F8F9FAFBFC
+AD = 05060708090A0B0C0D0E0F10
+In = 0102030405060708090A0B0C
+Out = 9AFC5E331177D5B4A34506C8670BAFC0E4882C7F6E82C72BD79BDF9E5AD6D4C830BC7A8B
+
+Key = A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8
+Nonce = F2F3F4F5F6F7F8F9FAFBFCFD
+AD = 060708090A0B0C0D0E0F101112131415161718191A1B1C1D
+In = 02030405060708090A0B0C0D0E0F10111213141516171819
+Out = 92A7C0C02A1F6E245762A3C3885DAFF1FAED6ACB4514475A5995C61B5E92C504AF63D449CD41F4F2F6F9EAF60B8017A0
+
+Key = B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8
+Nonce = F3F4F5F6F7F8F9FAFBFCFDFE
+AD = 0708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A
+In = 030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526
+Out = 2070BEDB155997DD86DE55F27CD45AA8223B16313C31BCC0D347CC7EF551DA4A27BCB1806D9418BB37C64AFB91272D0D193F32BCB7B081A150505FBB
+
+Key = C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8
+Nonce = F4F5F6F7F8F9FAFBFCFDFEFF
+AD = 08090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637
+In = 0405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233
+Out = 70E46BCD56C24D0FBC2A280C3C2A26A9AAB9A0979B8B81C952EE799774C85F953E809AF6FEC0D5524972730C380017EF889E94B2D0C6CC75879291B49B8E86F9D4C7D6EE29383576
diff --git a/src/tests/data/ocb_wide_long.vec b/src/tests/data/ocb_wide_long.vec
new file mode 100644
index 000000000..9fdb0b8a2
--- /dev/null
+++ b/src/tests/data/ocb_wide_long.vec
@@ -0,0 +1,15 @@
+
+[Toy128]
+Output = 0D099181BE37171BF94582877D6D4693
+
+[Toy192]
+Output = BFC53A29EABF2774369F3611DED760AE33338B607E5A8E40
+
+[Toy256]
+Output = 623C27E137975E25BEF2F8441CB5BDEAE8E0F1E158515193900BBD20D1A7AFF7
+
+[Toy512]
+Output = 6748655A0A83543D8AA6287AE9FFC37C9A433332DDFD4E8B42F94D741944D440
+
+[SHACAL2]
+Output = DC4AA181A65BD11EAA23D0881A20740B7DBA53C9DE2474DB3C3EF04770DFAD99
diff --git a/src/tests/test_ocb.cpp b/src/tests/test_ocb.cpp
index ede15fb82..b9af9ba9c 100644
--- a/src/tests/test_ocb.cpp
+++ b/src/tests/test_ocb.cpp
@@ -9,6 +9,7 @@
#if defined(BOTAN_HAS_AEAD_OCB)
#include <botan/ocb.h>
#include <botan/loadstor.h>
+ #include <botan/internal/poly_dbl.h>
#endif
namespace Botan_Tests {
@@ -17,6 +18,242 @@ namespace {
#if defined(BOTAN_HAS_AEAD_OCB)
+// Toy cipher used for wide block tests
+
+class OCB_Wide_Test_Block_Cipher : public Botan::BlockCipher
+ {
+ public:
+ OCB_Wide_Test_Block_Cipher(size_t bs) : m_bs(bs) {}
+
+ std::string name() const override { return "OCB_ToyCipher"; }
+ size_t block_size() const override { return m_bs; }
+ void clear() override { m_key.clear(); }
+
+ Botan::BlockCipher* clone() const { return new OCB_Wide_Test_Block_Cipher(m_bs); }
+
+ void key_schedule(const uint8_t key[], size_t length) override
+ {
+ m_key.assign(key, key + length);
+ }
+
+ Botan::Key_Length_Specification key_spec() const override
+ {
+ return Botan::Key_Length_Specification(m_bs);
+ }
+
+ void encrypt_n(const uint8_t in[], uint8_t out[],
+ size_t blocks) const override
+ {
+ while(blocks)
+ {
+ Botan::copy_mem(out, in, m_bs);
+ Botan::poly_double_n(out, m_bs);
+
+ for(size_t i = 0; i != m_bs; ++i)
+ out[i] ^= m_key[i];
+
+ blocks--;
+ in += block_size();
+ out += block_size();
+ }
+ }
+
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+ {
+ while(blocks)
+ {
+ for(size_t i = 0; i != m_bs; ++i)
+ out[i] = in[i] ^ m_key[i];
+
+ const uint8_t bottom_carry = in[m_bs-1] & 0x01;
+
+ if(bottom_carry)
+ {
+ if(m_bs == 16 || m_bs == 24)
+ {
+ out[m_bs-1] ^= 0x87;
+ }
+ else if(m_bs == 32)
+ {
+ out[m_bs-2] ^= 0x4;
+ out[m_bs-1] ^= 0x25;
+ }
+ else if(m_bs == 64)
+ {
+ out[m_bs-2] ^= 0x1;
+ out[m_bs-1] ^= 0x25;
+ }
+ else
+ throw Test_Error("Bad OCB test block size");
+ }
+
+ uint8_t carry = bottom_carry << 7;
+
+ for(size_t i = 0; i != m_bs; ++i)
+ {
+ uint8_t temp = out[i];
+ out[i] = (temp >> 1) | carry;
+ carry = (temp & 0x1) << 7;
+ }
+
+ blocks--;
+ in += block_size();
+ out += block_size();
+ }
+ }
+ private:
+ size_t m_bs;
+ std::vector<uint8_t> m_key;
+ };
+
+class OCB_Wide_KAT_Tests : public Text_Based_Test
+ {
+ public:
+ OCB_Wide_KAT_Tests()
+ : Text_Based_Test("ocb_wide.vec", "Key,Nonce,AD,In,Out") {}
+
+ Test::Result run_one_test(const std::string&, const VarMap& vars) override
+ {
+ Test::Result result("OCB wide block KAT");
+
+ const std::vector<uint8_t> key = get_req_bin(vars, "Key");
+ const std::vector<uint8_t> nonce = get_req_bin(vars, "Nonce");
+ const std::vector<uint8_t> ad = get_req_bin(vars, "AD");
+ const std::vector<uint8_t> input = get_req_bin(vars, "In");
+ const std::vector<uint8_t> expected = get_req_bin(vars, "Out");
+
+ const size_t bs = key.size();
+ Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+
+ Botan::OCB_Encryption enc(new OCB_Wide_Test_Block_Cipher(bs), std::min<size_t>(bs, 32));
+ enc.set_key(key);
+ enc.set_ad(ad);
+ enc.start(nonce);
+ enc.finish(buf);
+ result.test_eq("Ciphertext matches", buf, expected);
+
+ Botan::OCB_Decryption dec(new OCB_Wide_Test_Block_Cipher(bs), std::min<size_t>(bs, 32));
+ dec.set_key(key);
+ dec.set_ad(ad);
+ dec.start(nonce);
+ dec.finish(buf);
+ result.test_eq("Decryption correct", buf, input);
+
+ return result;
+ }
+ };
+
+BOTAN_REGISTER_TEST("ocb_wide", OCB_Wide_KAT_Tests);
+
+class OCB_Wide_Long_KAT_Tests : public Text_Based_Test
+ {
+ public:
+ OCB_Wide_Long_KAT_Tests()
+ : Text_Based_Test("ocb_wide_long.vec", "Output") {}
+
+ Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
+ {
+ Test::Result result("OCB wide block long test");
+
+ const std::vector<uint8_t> expected = get_req_bin(vars, "Output");
+
+ std::unique_ptr<Botan::BlockCipher> cipher;
+ size_t bs = 0;
+
+ if(algo == "SHACAL2")
+ {
+ cipher = Botan::BlockCipher::create_or_throw("SHACAL2");
+ bs = 32;
+ }
+ else
+ {
+ if(algo == "Toy128")
+ bs = 16;
+ else if(algo == "Toy192")
+ bs = 24;
+ else if(algo == "Toy256")
+ bs = 32;
+ else if(algo == "Toy512")
+ bs = 64;
+ else
+ throw Test_Error("Unknown cipher for OCB wide block long test");
+ cipher.reset(new OCB_Wide_Test_Block_Cipher(bs));
+ }
+
+ Botan::OCB_Encryption enc(cipher.release(), std::min<size_t>(bs, 32));
+
+ /*
+ Y, string of length min(B, 256) bits
+
+ Y is defined as follows.
+
+ K = (0xA0 || 0xA1 || 0xA2 || ...)[1..B]
+ C = <empty string>
+ for i = 0 to 127 do
+ S = (0x50 || 0x51 || 0x52 || ...)[1..8i]
+ N = num2str(3i+1,16)
+ C = C || OCB-ENCRYPT(K,N,S,S)
+ N = num2str(3i+2,16)
+ C = C || OCB-ENCRYPT(K,N,<empty string>,S)
+ N = num2str(3i+3,16)
+ C = C || OCB-ENCRYPT(K,N,S,<empty string>)
+ end for
+ N = num2str(385,16)
+ Y = OCB-ENCRYPT(K,N,C,<empty string>)
+ */
+
+ std::vector<uint8_t> key(bs);
+ for(size_t i = 0; i != bs; ++i)
+ key[i] = 0xA0 + i;
+
+ enc.set_key(key);
+
+ const std::vector<uint8_t> empty;
+ std::vector<uint8_t> N(2);
+ std::vector<uint8_t> C;
+
+ for(size_t i = 0; i != 128; ++i)
+ {
+ std::vector<uint8_t> S(i);
+ for(size_t j = 0; j != S.size(); ++j)
+ S[j] = 0x50 + j;
+
+ Botan::store_be(static_cast<uint16_t>(3 * i + 1), &N[0]);
+
+ ocb_encrypt(result, C, enc, N, S, S);
+ Botan::store_be(static_cast<uint16_t>(3 * i + 2), &N[0]);
+ ocb_encrypt(result, C, enc, N, S, empty);
+ Botan::store_be(static_cast<uint16_t>(3 * i + 3), &N[0]);
+ ocb_encrypt(result, C, enc, N, empty, S);
+ }
+
+ Botan::store_be(static_cast<uint16_t>(385), &N[0]);
+ std::vector<uint8_t> final_result;
+ ocb_encrypt(result, final_result, enc, N, empty, C);
+
+ result.test_eq("correct value", final_result, expected);
+
+ return result;
+ }
+
+ private:
+ void ocb_encrypt(Test::Result& /*result*/,
+ std::vector<uint8_t>& output_to,
+ Botan::OCB_Encryption& enc,
+ const std::vector<uint8_t>& nonce,
+ const std::vector<uint8_t>& pt,
+ const std::vector<uint8_t>& ad)
+ {
+ enc.set_associated_data(ad.data(), ad.size());
+ enc.start(nonce.data(), nonce.size());
+ Botan::secure_vector<uint8_t> buf(pt.begin(), pt.end());
+ enc.finish(buf, 0);
+ output_to.insert(output_to.end(), buf.begin(), buf.end());
+ }
+ };
+
+BOTAN_REGISTER_TEST("ocb_long_wide", OCB_Wide_Long_KAT_Tests);
+
class OCB_Long_KAT_Tests : public Text_Based_Test
{
public:
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index da1f874cf..930d7c623 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -993,7 +993,10 @@ std::vector<Test::Result> Text_Based_Test::run()
if(result.tests_failed())
{
- result.test_note("Test #" + std::to_string(test_cnt) + " failed");
+ if(header.empty())
+ result.test_note("Test #" + std::to_string(test_cnt) + " failed");
+ else
+ result.test_note("Test #" + std::to_string(test_cnt) + " " + header + " failed");
}
results.push_back(result);
}