Botan
1.11.15
|
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 }