summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp')
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp179
1 files changed, 179 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
new file mode 100644
index 0000000000..4b9c5b7204
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
@@ -0,0 +1,179 @@
+/*
+* GCM Mode Encryption
+* (C) 2013,2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gcm.h>
+#include <botan/ghash.h>
+#include <botan/block_cipher.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+/*
+* GCM_Mode Constructor
+*/
+GCM_Mode::GCM_Mode(BlockCipher* cipher, size_t tag_size) :
+ m_tag_size(tag_size),
+ m_cipher_name(cipher->name()),
+ m_ctr(new CTR_BE(cipher, 4)),
+ m_ghash(new GHASH)
+ {
+ if(cipher->block_size() != GCM_BS)
+ throw Invalid_Argument("Invalid block cipher for GCM");
+
+ /* We allow any of the values 128, 120, 112, 104, or 96 bits as a tag size */
+ /* 64 bit tag is still supported but deprecated and will be removed in the future */
+ if(m_tag_size != 8 && (m_tag_size < 12 || m_tag_size > 16))
+ throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(m_tag_size));
+ }
+
+GCM_Mode::~GCM_Mode() { /* for unique_ptr */ }
+
+void GCM_Mode::clear()
+ {
+ m_ctr->clear();
+ m_ghash->clear();
+ reset();
+ }
+
+void GCM_Mode::reset()
+ {
+ m_ghash->reset();
+ }
+
+std::string GCM_Mode::name() const
+ {
+ return (m_cipher_name + "/GCM(" + std::to_string(tag_size()) + ")");
+ }
+
+std::string GCM_Mode::provider() const
+ {
+ return m_ghash->provider();
+ }
+
+size_t GCM_Mode::update_granularity() const
+ {
+ return GCM_BS * std::max<size_t>(2, BOTAN_BLOCK_CIPHER_PAR_MULT);
+ }
+
+bool GCM_Mode::valid_nonce_length(size_t len) const
+ {
+ // GCM does not support empty nonces
+ return (len > 0);
+ }
+
+Key_Length_Specification GCM_Mode::key_spec() const
+ {
+ return m_ctr->key_spec();
+ }
+
+void GCM_Mode::key_schedule(const uint8_t key[], size_t keylen)
+ {
+ m_ctr->set_key(key, keylen);
+
+ const std::vector<uint8_t> zeros(GCM_BS);
+ m_ctr->set_iv(zeros.data(), zeros.size());
+
+ secure_vector<uint8_t> H(GCM_BS);
+ m_ctr->encipher(H);
+ m_ghash->set_key(H);
+ }
+
+void GCM_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+ {
+ m_ghash->set_associated_data(ad, ad_len);
+ }
+
+void GCM_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(m_y0.size() != GCM_BS)
+ m_y0.resize(GCM_BS);
+
+ clear_mem(m_y0.data(), m_y0.size());
+
+ if(nonce_len == 12)
+ {
+ copy_mem(m_y0.data(), nonce, nonce_len);
+ m_y0[15] = 1;
+ }
+ else
+ {
+ m_ghash->nonce_hash(m_y0, nonce, nonce_len);
+ }
+
+ m_ctr->set_iv(m_y0.data(), m_y0.size());
+
+ clear_mem(m_y0.data(), m_y0.size());
+ m_ctr->encipher(m_y0);
+
+ m_ghash->start(m_y0.data(), m_y0.size());
+ clear_mem(m_y0.data(), m_y0.size());
+ }
+
+size_t GCM_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+ m_ctr->cipher(buf, buf, sz);
+ m_ghash->update(buf, sz);
+ return sz;
+ }
+
+void GCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ m_ctr->cipher(buf, buf, sz);
+ m_ghash->update(buf, sz);
+
+ uint8_t mac[16] = { 0 };
+ m_ghash->final(mac, tag_size());
+ buffer += std::make_pair(mac, tag_size());
+ }
+
+size_t GCM_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+ m_ghash->update(buf, sz);
+ m_ctr->cipher(buf, buf, sz);
+ return sz;
+ }
+
+void GCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ if(sz < tag_size())
+ throw Decoding_Error("Insufficient input for GCM decryption, tag missing");
+
+ const size_t remaining = sz - tag_size();
+
+ // handle any final input before the tag
+ if(remaining)
+ {
+ m_ghash->update(buf, remaining);
+ m_ctr->cipher(buf, buf, remaining);
+ }
+
+ uint8_t mac[16] = { 0 };
+ m_ghash->final(mac, tag_size());
+
+ const uint8_t* included_tag = &buffer[remaining+offset];
+
+ if(!constant_time_compare(mac, included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("GCM tag check failed");
+
+ buffer.resize(offset + remaining);
+ }
+
+}