1 /** 2 * CCM Mode 3 * 4 * Copyright: 5 * (C) 2013 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.modes.aead.ccm; 12 13 import botan.constants; 14 static if (BOTAN_HAS_AEAD_CCM): 15 16 import botan.modes.aead.aead; 17 import botan.block.block_cipher; 18 import botan.stream.stream_cipher; 19 import botan.mac.mac; 20 import botan.utils.parsing; 21 import botan.utils.xor_buf; 22 import botan.utils.get_byte; 23 import std.conv : to; 24 import std.algorithm; 25 import botan.codec.hex; 26 27 /** 28 * Base class for CCM encryption and decryption 29 * @see RFC 3610 30 */ 31 abstract class CCMMode : AEADMode, Transformation 32 { 33 public: 34 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) 35 { 36 if (!validNonceLength(nonce_len)) 37 throw new InvalidIVLength(name, nonce_len); 38 39 m_nonce[] = nonce[0 .. nonce_len]; 40 m_msg_buf.clear(); 41 42 return SecureVector!ubyte(); 43 } 44 45 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 46 { 47 assert(buffer.length >= offset, "Offset is sane"); 48 const size_t sz = buffer.length - offset; 49 ubyte* buf = buffer.ptr + offset; 50 51 m_msg_buf ~= buf[0 .. sz]; 52 logDebug("m_msg_buf: ", hexEncode(m_msg_buf.ptr, m_msg_buf.length)); 53 buffer.length = offset; // truncate msg 54 } 55 56 override void setAssociatedData(const(ubyte)* ad, size_t length) 57 { 58 m_ad_buf.clear(); 59 if (length) 60 { 61 // FIXME: support larger AD using length encoding rules 62 assert(length < (0xFFFF - 0xFF), "Supported CCM AD length"); 63 64 m_ad_buf.pushBack(get_byte(0, cast(ushort) length)); 65 m_ad_buf.pushBack(get_byte(1, cast(ushort) length)); 66 m_ad_buf ~= ad[0 .. length]; 67 while (m_ad_buf.length % BS) 68 m_ad_buf.pushBack(cast(ubyte)0); // pad with zeros to full block size 69 } 70 } 71 72 override @property string name() const 73 { 74 return (m_cipher.name ~ "/CCM(" ~ to!string(tagSize()) ~ "," ~ to!string(L())) ~ ")"; 75 } 76 77 size_t updateGranularity() const 78 { 79 /* 80 This value does not particularly matter as regardless update 81 buffers all input, so in theory this could be 1. However as for instance 82 TransformationFilter creates updateGranularity() ubyte buffers, use a 83 somewhat large size to avoid bouncing on a tiny buffer. 84 */ 85 return m_cipher.parallelBytes(); 86 } 87 88 89 override KeyLengthSpecification keySpec() const 90 { 91 return m_cipher.keySpec(); 92 } 93 94 override bool validNonceLength(size_t n) const 95 { 96 return (n == (15-L())); 97 } 98 99 override size_t defaultNonceLength() const 100 { 101 return (15-L()); 102 } 103 104 override void clear() 105 { 106 m_cipher.clear(); 107 m_msg_buf.clear(); 108 m_ad_buf.clear(); 109 } 110 111 override size_t tagSize() const { return m_tag_size; } 112 113 protected: 114 __gshared immutable size_t BS = 16; // intrinsic to CCM definition 115 116 /* 117 * CCMMode Constructor 118 */ 119 this(BlockCipher cipher, size_t tag_size, size_t L) 120 { 121 m_tag_size = tag_size; 122 m_L = L; 123 m_cipher = cipher; 124 if (m_cipher.blockSize() != BS) 125 throw new InvalidArgument(m_cipher.name ~ " cannot be used with CCM mode"); 126 127 if (L < 2 || L > 8) 128 throw new InvalidArgument("Invalid CCM L value " ~ to!string(L)); 129 130 if (tag_size < 4 || tag_size > 16 || tag_size % 2 != 0) 131 throw new InvalidArgument("invalid CCM tag length " ~ to!string(tag_size)); 132 } 133 134 final size_t L() const { return m_L; } 135 136 final BlockCipher cipher() const { return cast()*m_cipher; } 137 138 final void encodeLength(size_t len, ubyte* output) 139 { 140 const size_t len_bytes = L(); 141 142 assert(len_bytes < (size_t).sizeof, "Length field fits"); 143 144 foreach (size_t i; 0 .. len_bytes) 145 output[len_bytes-1-i] = get_byte((size_t).sizeof-1-i, len); 146 147 assert((len >> (len_bytes*8)) == 0, "Message length fits in field"); 148 } 149 150 final void inc(ref SecureVector!ubyte C) 151 { 152 for (size_t i = 0; i != C.length; ++i) 153 if (++(C.ptr[C.length-i-1])) 154 break; 155 } 156 157 final ref const(SecureVector!ubyte) adBuf() const { return m_ad_buf; } 158 159 final ref const(SecureVector!ubyte) msgBuf() const { return m_msg_buf; } 160 161 final SecureVector!ubyte formatB0(size_t sz) 162 { 163 SecureVector!ubyte B0 = SecureVector!ubyte(BS); 164 165 const ubyte b_flags = cast(ubyte) ((m_ad_buf.length ? 64 : 0) + (((tagSize()/2)-1) << 3) + (L()-1)); 166 167 B0[0] = b_flags; 168 assert(B0.length >= m_nonce.length); 169 copyMem(&B0[1], m_nonce.ptr, m_nonce.length); 170 encodeLength(sz, &B0[m_nonce.length+1]); 171 172 return B0.move; 173 } 174 175 final SecureVector!ubyte formatC0() 176 { 177 SecureVector!ubyte C = SecureVector!ubyte(BS); 178 179 const ubyte a_flags = cast(ubyte)(L()-1); 180 181 C[0] = a_flags; 182 assert(C.length >= m_nonce.length + 1); 183 copyMem(&C[1], m_nonce.ptr, m_nonce.length); 184 185 return C.move; 186 } 187 188 final override void keySchedule(const(ubyte)* key, size_t length) 189 { 190 m_cipher.setKey(key, length); 191 } 192 193 protected: 194 const size_t m_tag_size; 195 const size_t m_L; 196 197 Unique!BlockCipher m_cipher; 198 SecureVector!ubyte m_nonce, m_msg_buf, m_ad_buf; 199 } 200 201 /** 202 * CCM Encryption 203 */ 204 final class CCMEncryption : CCMMode, Transformation 205 { 206 public: 207 /** 208 * Params: 209 * cipher = a 128-bit block cipher 210 * tag_size = is how big the auth tag will be (even values 211 * between 4 and 16 are accepted) 212 * L = length of L parameter. The total message length 213 * must be less than 2**L bytes, and the nonce is 15-L bytes. 214 */ 215 this(BlockCipher cipher, size_t tag_size = 16, size_t L = 3) 216 { 217 super(cipher, tag_size, L); 218 } 219 220 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 221 { 222 import std.algorithm : max; 223 assert(buffer.length >= offset, "Offset is sane"); 224 buffer.resize(max(buffer.length, offset + msgBuf().length)); 225 buffer[offset .. offset + msgBuf().length] = msgBuf()[0 .. msgBuf().length]; 226 const size_t sz = buffer.length - offset; 227 ubyte* buf = buffer.ptr + offset; 228 229 const SecureVector!ubyte* ad = &adBuf(); 230 assert(ad.length % BS == 0, "AD is block size multiple"); 231 232 BlockCipher E = cipher(); 233 234 SecureVector!ubyte T = SecureVector!ubyte(BS); 235 E.encrypt(formatB0(sz), T); 236 237 for (size_t i = 0; i != ad.length; i += BS) 238 { 239 xorBuf(T.ptr, &(*ad)[i], BS); 240 E.encrypt(T); 241 } 242 243 SecureVector!ubyte C = formatC0(); 244 SecureVector!ubyte S0 = SecureVector!ubyte(BS); 245 E.encrypt(C, S0); 246 inc(C); 247 SecureVector!ubyte X = SecureVector!ubyte(BS); 248 249 const(ubyte)* buf_end = &buf[sz]; 250 251 while (buf != buf_end) 252 { 253 const size_t to_proc = std.algorithm.min(BS, buf_end - buf); 254 255 xorBuf(T.ptr, buf, to_proc); 256 E.encrypt(T); 257 E.encrypt(C, X); 258 xorBuf(buf, X.ptr, to_proc); 259 inc(C); 260 buf += to_proc; 261 } 262 T ^= S0; 263 264 buffer ~= T.ptr[0 .. tagSize()]; 265 } 266 267 override size_t outputLength(size_t input_length) const 268 { return input_length + tagSize(); } 269 270 override size_t minimumFinalSize() const { return 0; } 271 272 // Interface fallthrough 273 override string provider() const { return "core"; } 274 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 275 override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); } 276 override size_t updateGranularity() const { return super.updateGranularity(); } 277 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 278 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 279 override @property string name() const { return super.name; } 280 override void clear() { return super.clear(); } 281 282 } 283 284 /** 285 * CCM Decryption 286 */ 287 final class CCMDecryption : CCMMode, Transformation 288 { 289 public: 290 /** 291 * Params: 292 * cipher = a 128-bit block cipher 293 * tag_size = is how big the auth tag will be (even values 294 * between 4 and 16 are accepted) 295 * L = length of L parameter. The total message length 296 * must be less than 2**L bytes, and the nonce is 15-L bytes. 297 */ 298 this(BlockCipher cipher, size_t tag_size = 16, size_t L = 3) 299 { 300 super(cipher, tag_size, L); 301 } 302 303 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 304 { 305 import std.algorithm : max; 306 assert(buffer.length >= offset, "Offset is sane"); 307 buffer.resize(max(buffer.length, offset + msgBuf().length)); 308 buffer.ptr[offset .. offset + msgBuf().length] = msgBuf().ptr[0 .. msgBuf().length]; 309 310 const size_t sz = buffer.length - offset; 311 ubyte* buf = buffer.ptr + offset; 312 313 assert(sz >= tagSize(), "We have the tag"); 314 315 const SecureVector!ubyte* ad = &adBuf(); 316 assert(ad.length % BS == 0, "AD is block size multiple"); 317 318 BlockCipher E = cipher(); 319 320 SecureVector!ubyte T = SecureVector!ubyte(BS); 321 E.encrypt(formatB0(sz - tagSize()), T); 322 323 for (size_t i = 0; i != ad.length; i += BS) 324 { 325 xorBuf(T.ptr, &(*ad)[i], BS); 326 E.encrypt(T); 327 } 328 329 SecureVector!ubyte C = formatC0(); 330 331 SecureVector!ubyte S0 = SecureVector!ubyte(BS); 332 E.encrypt(C, S0); 333 inc(C); 334 335 SecureVector!ubyte X = SecureVector!ubyte(BS); 336 337 const(ubyte)* buf_end = &buf[sz - tagSize()]; 338 339 while (buf < buf_end) 340 { 341 const size_t to_proc = std.algorithm.min(BS, buf_end - buf); 342 343 E.encrypt(C, X); 344 xorBuf(buf, X.ptr, to_proc); 345 inc(C); 346 347 xorBuf(T.ptr, buf, to_proc); 348 E.encrypt(T); 349 350 buf += to_proc; 351 } 352 353 T ^= S0; 354 355 if (!sameMem(T.ptr, buf_end, tagSize())) 356 throw new IntegrityFailure("CCM tag check failed"); 357 358 buffer.resize(buffer.length - tagSize()); 359 } 360 361 override size_t outputLength(size_t input_length) const 362 { 363 assert(input_length > tagSize(), "Sufficient input"); 364 return input_length - tagSize(); 365 } 366 367 override size_t minimumFinalSize() const { return tagSize(); } 368 369 // Interface fallthrough 370 override string provider() const { return "core"; } 371 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 372 override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); } 373 override size_t updateGranularity() const { return super.updateGranularity(); } 374 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 375 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 376 override @property string name() const { return super.name; } 377 override void clear() { return super.clear(); } 378 }