Botan  1.11.15
src/lib/modes/aead/ccm/ccm.cpp
Go to the documentation of this file.
00001 /*
00002 * CCM Mode Encryption
00003 * (C) 2013 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/ccm.h>
00010 #include <botan/parsing.h>
00011 
00012 namespace Botan {
00013 
00014 BOTAN_REGISTER_BLOCK_CIPHER_MODE_LEN2(CCM_Encryption, CCM_Decryption, 16, 3);
00015 
00016 /*
00017 * CCM_Mode Constructor
00018 */
00019 CCM_Mode::CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L) :
00020    m_tag_size(tag_size),
00021    m_L(L),
00022    m_cipher(cipher)
00023    {
00024    if(m_cipher->block_size() != BS)
00025       throw std::invalid_argument(m_cipher->name() + " cannot be used with CCM mode");
00026 
00027    if(L < 2 || L > 8)
00028       throw std::invalid_argument("Invalid CCM L value " + std::to_string(L));
00029 
00030    if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0)
00031       throw std::invalid_argument("invalid CCM tag length " + std::to_string(tag_size));
00032    }
00033 
00034 void CCM_Mode::clear()
00035    {
00036    m_cipher.reset();
00037    m_msg_buf.clear();
00038    m_ad_buf.clear();
00039    }
00040 
00041 std::string CCM_Mode::name() const
00042    {
00043    return (m_cipher->name() + "/CCM(" + std::to_string(tag_size()) + "," + std::to_string(L())) + ")";
00044    }
00045 
00046 bool CCM_Mode::valid_nonce_length(size_t n) const
00047    {
00048    return (n == (15-L()));
00049    }
00050 
00051 size_t CCM_Mode::default_nonce_length() const
00052    {
00053    return (15-L());
00054    }
00055 
00056 size_t CCM_Mode::update_granularity() const
00057    {
00058    /*
00059    This value does not particularly matter as regardless CCM_Mode::update
00060    buffers all input, so in theory this could be 1. However as for instance
00061    Transform_Filter creates update_granularity() byte buffers, use a
00062    somewhat large size to avoid bouncing on a tiny buffer.
00063    */
00064    return m_cipher->parallel_bytes();
00065    }
00066 
00067 Key_Length_Specification CCM_Mode::key_spec() const
00068    {
00069    return m_cipher->key_spec();
00070    }
00071 
00072 void CCM_Mode::key_schedule(const byte key[], size_t length)
00073    {
00074    m_cipher->set_key(key, length);
00075    }
00076 
00077 void CCM_Mode::set_associated_data(const byte ad[], size_t length)
00078    {
00079    m_ad_buf.clear();
00080 
00081    if(length)
00082       {
00083       // FIXME: support larger AD using length encoding rules
00084       BOTAN_ASSERT(length < (0xFFFF - 0xFF), "Supported CCM AD length");
00085 
00086       m_ad_buf.push_back(get_byte<u16bit>(0, length));
00087       m_ad_buf.push_back(get_byte<u16bit>(1, length));
00088       m_ad_buf += std::make_pair(ad, length);
00089       while(m_ad_buf.size() % BS)
00090          m_ad_buf.push_back(0); // pad with zeros to full block size
00091       }
00092    }
00093 
00094 secure_vector<byte> CCM_Mode::start_raw(const byte nonce[], size_t nonce_len)
00095    {
00096    if(!valid_nonce_length(nonce_len))
00097       throw Invalid_IV_Length(name(), nonce_len);
00098 
00099    m_nonce.assign(nonce, nonce + nonce_len);
00100    m_msg_buf.clear();
00101 
00102    return secure_vector<byte>();
00103    }
00104 
00105 void CCM_Mode::update(secure_vector<byte>& buffer, size_t offset)
00106    {
00107    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
00108    const size_t sz = buffer.size() - offset;
00109    byte* buf = &buffer[offset];
00110 
00111    m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
00112    buffer.resize(offset); // truncate msg
00113    }
00114 
00115 void CCM_Mode::encode_length(size_t len, byte out[])
00116    {
00117    const size_t len_bytes = L();
00118 
00119    BOTAN_ASSERT(len_bytes < sizeof(size_t), "Length field fits");
00120 
00121    for(size_t i = 0; i != len_bytes; ++i)
00122       out[len_bytes-1-i] = get_byte(sizeof(size_t)-1-i, len);
00123 
00124    BOTAN_ASSERT((len >> (len_bytes*8)) == 0, "Message length fits in field");
00125    }
00126 
00127 void CCM_Mode::inc(secure_vector<byte>& C)
00128    {
00129    for(size_t i = 0; i != C.size(); ++i)
00130       if(++C[C.size()-i-1])
00131          break;
00132    }
00133 
00134 secure_vector<byte> CCM_Mode::format_b0(size_t sz)
00135    {
00136    secure_vector<byte> B0(BS);
00137 
00138    const byte b_flags = (m_ad_buf.size() ? 64 : 0) + (((tag_size()/2)-1) << 3) + (L()-1);
00139 
00140    B0[0] = b_flags;
00141    copy_mem(&B0[1], &m_nonce[0], m_nonce.size());
00142    encode_length(sz, &B0[m_nonce.size()+1]);
00143 
00144    return B0;
00145    }
00146 
00147 secure_vector<byte> CCM_Mode::format_c0()
00148    {
00149    secure_vector<byte> C(BS);
00150 
00151    const byte a_flags = L()-1;
00152 
00153    C[0] = a_flags;
00154    copy_mem(&C[1], &m_nonce[0], m_nonce.size());
00155 
00156    return C;
00157    }
00158 
00159 void CCM_Encryption::finish(secure_vector<byte>& buffer, size_t offset)
00160    {
00161    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
00162 
00163    buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
00164 
00165    const size_t sz = buffer.size() - offset;
00166    byte* buf = &buffer[offset];
00167 
00168    const secure_vector<byte>& ad = ad_buf();
00169    BOTAN_ASSERT(ad.size() % BS == 0, "AD is block size multiple");
00170 
00171    const BlockCipher& E = cipher();
00172 
00173    secure_vector<byte> T(BS);
00174    E.encrypt(format_b0(sz), T);
00175 
00176    for(size_t i = 0; i != ad.size(); i += BS)
00177       {
00178       xor_buf(&T[0], &ad[i], BS);
00179       E.encrypt(T);
00180       }
00181 
00182    secure_vector<byte> C = format_c0();
00183    secure_vector<byte> S0(BS);
00184    E.encrypt(C, S0);
00185    inc(C);
00186 
00187    secure_vector<byte> X(BS);
00188 
00189    const byte* buf_end = &buf[sz];
00190 
00191    while(buf != buf_end)
00192       {
00193       const size_t to_proc = std::min<size_t>(BS, buf_end - buf);
00194 
00195       xor_buf(&T[0], buf, to_proc);
00196       E.encrypt(T);
00197 
00198       E.encrypt(C, X);
00199       xor_buf(buf, &X[0], to_proc);
00200       inc(C);
00201 
00202       buf += to_proc;
00203       }
00204 
00205    T ^= S0;
00206 
00207    buffer += std::make_pair(&T[0], tag_size());
00208    }
00209 
00210 void CCM_Decryption::finish(secure_vector<byte>& buffer, size_t offset)
00211    {
00212    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
00213 
00214    buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
00215 
00216    const size_t sz = buffer.size() - offset;
00217    byte* buf = &buffer[offset];
00218 
00219    BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
00220 
00221    const secure_vector<byte>& ad = ad_buf();
00222    BOTAN_ASSERT(ad.size() % BS == 0, "AD is block size multiple");
00223 
00224    const BlockCipher& E = cipher();
00225 
00226    secure_vector<byte> T(BS);
00227    E.encrypt(format_b0(sz - tag_size()), T);
00228 
00229    for(size_t i = 0; i != ad.size(); i += BS)
00230       {
00231       xor_buf(&T[0], &ad[i], BS);
00232       E.encrypt(T);
00233       }
00234 
00235    secure_vector<byte> C = format_c0();
00236 
00237    secure_vector<byte> S0(BS);
00238    E.encrypt(C, S0);
00239    inc(C);
00240 
00241    secure_vector<byte> X(BS);
00242 
00243    const byte* buf_end = &buf[sz - tag_size()];
00244 
00245    while(buf != buf_end)
00246       {
00247       const size_t to_proc = std::min<size_t>(BS, buf_end - buf);
00248 
00249       E.encrypt(C, X);
00250       xor_buf(buf, &X[0], to_proc);
00251       inc(C);
00252 
00253       xor_buf(&T[0], buf, to_proc);
00254       E.encrypt(T);
00255 
00256       buf += to_proc;
00257       }
00258 
00259    T ^= S0;
00260 
00261    if(!same_mem(&T[0], buf_end, tag_size()))
00262       throw Integrity_Failure("CCM tag check failed");
00263 
00264    buffer.resize(buffer.size() - tag_size());
00265    }
00266 
00267 }