Botan  1.11.15
src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
Go to the documentation of this file.
00001 /*
00002 * ChaCha20Poly1305 AEAD
00003 * (C) 2014 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/internal/mode_utils.h>
00009 #include <botan/chacha20poly1305.h>
00010 
00011 namespace Botan {
00012 
00013 BOTAN_REGISTER_TRANSFORM_NOARGS(ChaCha20Poly1305_Encryption);
00014 BOTAN_REGISTER_TRANSFORM_NOARGS(ChaCha20Poly1305_Decryption);
00015 
00016 bool ChaCha20Poly1305_Mode::valid_nonce_length(size_t n) const
00017    {
00018    return (n == 8 || n == 12);
00019    }
00020 
00021 void ChaCha20Poly1305_Mode::clear()
00022    {
00023    m_chacha.reset();
00024    m_poly1305.reset();
00025    m_ad.clear();
00026    m_ctext_len = 0;
00027    }
00028 
00029 void ChaCha20Poly1305_Mode::key_schedule(const byte key[], size_t length)
00030    {
00031    if(!m_chacha.get())
00032       m_chacha.reset(make_a<StreamCipher>("ChaCha"));
00033    m_chacha->set_key(key, length);
00034    }
00035 
00036 void ChaCha20Poly1305_Mode::set_associated_data(const byte ad[], size_t length)
00037    {
00038    if(m_ctext_len)
00039       throw std::runtime_error("Too late to set AD for ChaCha20Poly1305");
00040    m_ad.assign(ad, ad + length);
00041    }
00042 
00043 void ChaCha20Poly1305_Mode::update_len(size_t len)
00044    {
00045    byte len8[8] = { 0 };
00046    store_le(static_cast<u64bit>(len), len8);
00047    m_poly1305->update(len8, 8);
00048    }
00049 
00050 secure_vector<byte> ChaCha20Poly1305_Mode::start_raw(const byte nonce[], size_t nonce_len)
00051    {
00052    if(!valid_nonce_length(nonce_len))
00053       throw Invalid_IV_Length(name(), nonce_len);
00054 
00055    m_ctext_len = 0;
00056    m_nonce_len = nonce_len;
00057 
00058    m_chacha->set_iv(nonce, nonce_len);
00059 
00060    secure_vector<byte> zeros(64);
00061    m_chacha->encrypt(zeros);
00062 
00063    if(!m_poly1305.get())
00064       m_poly1305.reset(make_a<MessageAuthenticationCode>("Poly1305"));
00065    m_poly1305->set_key(&zeros[0], 32);
00066    // Remainder of output is discard
00067 
00068    m_poly1305->update(m_ad);
00069 
00070    if(cfrg_version())
00071       {
00072       std::vector<byte> padding(16 - m_ad.size() % 16);
00073       m_poly1305->update(padding);
00074       }
00075    else
00076       {
00077       update_len(m_ad.size());
00078       }
00079 
00080    return secure_vector<byte>();
00081    }
00082 
00083 void ChaCha20Poly1305_Encryption::update(secure_vector<byte>& buffer, size_t offset)
00084    {
00085    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
00086    const size_t sz = buffer.size() - offset;
00087    byte* buf = &buffer[offset];
00088 
00089    m_chacha->cipher1(buf, sz);
00090    m_poly1305->update(buf, sz); // poly1305 of ciphertext
00091    m_ctext_len += sz;
00092    }
00093 
00094 void ChaCha20Poly1305_Encryption::finish(secure_vector<byte>& buffer, size_t offset)
00095    {
00096    update(buffer, offset);
00097    if(cfrg_version())
00098       {
00099       std::vector<byte> padding(16 - m_ctext_len % 16);
00100       m_poly1305->update(padding);
00101       update_len(m_ad.size());
00102       }
00103    update_len(m_ctext_len);
00104 
00105    const secure_vector<byte> mac = m_poly1305->final();
00106    buffer += std::make_pair(&mac[0], tag_size());
00107    m_ctext_len = 0;
00108    }
00109 
00110 void ChaCha20Poly1305_Decryption::update(secure_vector<byte>& buffer, size_t offset)
00111    {
00112    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
00113    const size_t sz = buffer.size() - offset;
00114    byte* buf = &buffer[offset];
00115 
00116    m_poly1305->update(buf, sz); // poly1305 of ciphertext
00117    m_chacha->cipher1(buf, sz);
00118    m_ctext_len += sz;
00119    }
00120 
00121 void ChaCha20Poly1305_Decryption::finish(secure_vector<byte>& buffer, size_t offset)
00122    {
00123    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
00124    const size_t sz = buffer.size() - offset;
00125    byte* buf = &buffer[offset];
00126 
00127    BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
00128 
00129    const size_t remaining = sz - tag_size();
00130 
00131    if(remaining)
00132       {
00133       m_poly1305->update(buf, remaining); // poly1305 of ciphertext
00134       m_chacha->cipher1(buf, remaining);
00135       m_ctext_len += remaining;
00136       }
00137 
00138    if(cfrg_version())
00139       {
00140       for(size_t i = 0; i != 16 - m_ctext_len % 16; ++i)
00141          m_poly1305->update(0);
00142       update_len(m_ad.size());
00143       }
00144 
00145    update_len(m_ctext_len);
00146    const secure_vector<byte> mac = m_poly1305->final();
00147 
00148    const byte* included_tag = &buf[remaining];
00149 
00150    m_ctext_len = 0;
00151 
00152    if(!same_mem(&mac[0], included_tag, tag_size()))
00153       throw Integrity_Failure("ChaCha20Poly1305 tag check failed");
00154    buffer.resize(offset + remaining);
00155    }
00156 
00157 }