diff options
author | Jack Lloyd <[email protected]> | 2017-09-19 11:45:56 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-09-19 11:45:56 -0400 |
commit | c1476d8014f462a1e572396467995f6e596d67b2 (patch) | |
tree | 400f9913152663fa37f6470cad52494092d9f214 /src/lib/modes/aead/ocb | |
parent | 050c02433e810b0516130ea0d436226f25cacc2b (diff) | |
parent | 92245ad040b8f0e08a4a57137be5739e5c7bfbdc (diff) |
Merge GH #1205 Support large blocks in OCB mode
Diffstat (limited to 'src/lib/modes/aead/ocb')
-rw-r--r-- | src/lib/modes/aead/ocb/ocb.cpp | 185 | ||||
-rw-r--r-- | src/lib/modes/aead/ocb/ocb.h | 11 |
2 files changed, 134 insertions, 62 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) : |