1 /** 2 * XTS mode, from IEEE P1619 3 * 4 * Copyright: 5 * (C) 2009,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.xts; 12 13 import botan.constants; 14 static if (BOTAN_HAS_MODE_XTS): 15 16 import botan.modes.cipher_mode; 17 import botan.block.block_cipher; 18 import botan.modes.xts; 19 import botan.utils.loadstor; 20 import botan.utils.xor_buf; 21 import botan.utils.rounding; 22 import botan.utils.mem_ops; 23 import std.algorithm : min; 24 25 /** 26 * IEEE P1619 XTS Mode 27 */ 28 abstract class XTSMode : CipherMode, Transformation 29 { 30 public: 31 override @property string name() const 32 { 33 return cipher().name ~ "/XTS"; 34 } 35 36 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) 37 { 38 if (!validNonceLength(nonce_len)) 39 throw new InvalidIVLength(name, nonce_len); 40 41 copyMem(m_tweak.ptr, nonce, nonce_len); 42 m_tweak_cipher.encrypt(m_tweak.ptr); 43 44 updateTweak(0); 45 46 return SecureVector!ubyte(); 47 } 48 49 override size_t updateGranularity() const 50 { 51 return cipher().parallelBytes(); 52 } 53 54 override size_t minimumFinalSize() const 55 { 56 return cipher().blockSize() + 1; 57 } 58 59 override KeyLengthSpecification keySpec() const 60 { 61 return cipher().keySpec().multiple(2); 62 } 63 64 override size_t defaultNonceLength() const 65 { 66 return cipher().blockSize(); 67 } 68 69 override bool validNonceLength(size_t n) const 70 { 71 return cipher().blockSize() == n; 72 } 73 74 override void clear() 75 { 76 m_cipher.clear(); 77 m_tweak_cipher.clear(); 78 zeroise(m_tweak); 79 } 80 81 override bool authenticated() const { return true; } 82 protected: 83 this(BlockCipher cipher) 84 { 85 m_cipher = cipher; 86 if (m_cipher.blockSize() != 8 && m_cipher.blockSize() != 16) 87 throw new InvalidArgument("Bad cipher for XTS: " ~ m_cipher.name); 88 89 m_tweak_cipher = m_cipher.clone(); 90 m_tweak.resize(updateGranularity()); 91 } 92 93 final ubyte* tweak() const { return m_tweak.ptr; } 94 95 final BlockCipher cipher() const { return cast()*m_cipher; } 96 97 final void updateTweak(size_t which) 98 { 99 const size_t BS = m_tweak_cipher.blockSize(); 100 101 if (which > 0) 102 polyDouble(m_tweak.ptr, &m_tweak[(which-1)*BS], BS); 103 104 const size_t blocks_in_tweak = updateGranularity() / BS; 105 106 for (size_t i = 1; i < blocks_in_tweak; ++i) 107 polyDouble(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS); 108 } 109 110 final override void keySchedule(const(ubyte)* key, size_t length) 111 { 112 const size_t key_half = length / 2; 113 114 if (length % 2 == 1 || !m_cipher.validKeylength(key_half)) 115 throw new InvalidKeyLength(name, length); 116 117 m_cipher.setKey(key, key_half); 118 m_tweak_cipher.setKey(&key[key_half], key_half); 119 } 120 121 private: 122 Unique!BlockCipher m_cipher, m_tweak_cipher; 123 SecureVector!ubyte m_tweak; 124 } 125 126 /** 127 * IEEE P1619 XTS Encryption 128 */ 129 final class XTSEncryption : XTSMode, Transformation 130 { 131 public: 132 this(BlockCipher cipher) 133 { 134 super(cipher); 135 } 136 137 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 138 { 139 assert(buffer.length >= offset, "Offset is sane"); 140 const size_t sz = buffer.length - offset; 141 ubyte* buf = buffer.ptr + offset; 142 143 const size_t BS = cipher().blockSize(); 144 145 assert(sz % BS == 0, "Input is full blocks"); 146 size_t blocks = sz / BS; 147 148 const size_t blocks_in_tweak = updateGranularity() / BS; 149 150 while (blocks) 151 { 152 const size_t to_proc = min(blocks, blocks_in_tweak); 153 const size_t to_proc_bytes = to_proc * BS; 154 155 xorBuf(buf, tweak(), to_proc_bytes); 156 cipher().encryptN(buf, buf, to_proc); 157 xorBuf(buf, tweak(), to_proc_bytes); 158 159 buf += to_proc * BS; 160 blocks -= to_proc; 161 162 updateTweak(to_proc); 163 } 164 } 165 166 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 167 { 168 assert(buffer.length >= offset, "Offset is sane"); 169 const size_t sz = buffer.length - offset; 170 ubyte* buf = buffer.ptr + offset; 171 172 assert(sz >= minimumFinalSize(), "Have sufficient final input"); 173 174 const size_t BS = cipher().blockSize(); 175 176 if (sz % BS == 0) 177 { 178 update(buffer, offset); 179 } 180 else 181 { 182 // steal ciphertext 183 const size_t full_blocks = ((sz / BS) - 1) * BS; 184 const size_t final_bytes = sz - full_blocks; 185 assert(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); 186 187 SecureVector!ubyte last = SecureVector!ubyte(buf[full_blocks .. full_blocks + final_bytes]); 188 buffer.resize(full_blocks + offset); 189 update(buffer, offset); 190 191 xorBuf(last, tweak(), BS); 192 cipher().encrypt(last); 193 xorBuf(last, tweak(), BS); 194 195 foreach (size_t i; 0 .. (final_bytes - BS)) 196 { 197 last[i] ^= last[i + BS]; 198 last[i + BS] ^= last[i]; 199 last[i] ^= last[i + BS]; 200 } 201 202 xorBuf(last, tweak() + BS, BS); 203 cipher().encrypt(last); 204 xorBuf(last, tweak() + BS, BS); 205 206 buffer ~= last; 207 } 208 } 209 210 override size_t outputLength(size_t input_length) const 211 { 212 return roundUp(input_length, cipher().blockSize()); 213 } 214 215 // Interface fallthrough 216 override string provider() const { return "core"; } 217 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 218 override size_t updateGranularity() const { return super.updateGranularity(); } 219 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 220 override @property string name() const { return super.name; } 221 override void clear() { return super.clear(); } 222 override size_t minimumFinalSize() const { return super.minimumFinalSize(); } 223 override bool validNonceLength(size_t n) const { 224 return super.validNonceLength(n); 225 } 226 } 227 228 /** 229 * IEEE P1619 XTS Decryption 230 */ 231 final class XTSDecryption : XTSMode, Transformation 232 { 233 public: 234 this(BlockCipher cipher) 235 { 236 super(cipher); 237 } 238 239 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 240 { 241 assert(buffer.length >= offset, "Offset is sane"); 242 const size_t sz = buffer.length - offset; 243 ubyte* buf = buffer.ptr + offset; 244 245 const size_t BS = cipher().blockSize(); 246 247 assert(sz % BS == 0, "Input is full blocks"); 248 size_t blocks = sz / BS; 249 250 const size_t blocks_in_tweak = updateGranularity() / BS; 251 252 while (blocks) 253 { 254 const size_t to_proc = min(blocks, blocks_in_tweak); 255 const size_t to_proc_bytes = to_proc * BS; 256 257 xorBuf(buf, tweak(), to_proc_bytes); 258 cipher().decryptN(buf, buf, to_proc); 259 xorBuf(buf, tweak(), to_proc_bytes); 260 261 buf += to_proc * BS; 262 blocks -= to_proc; 263 264 updateTweak(to_proc); 265 } 266 } 267 268 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 269 { 270 assert(buffer.length >= offset, "Offset is sane"); 271 const size_t sz = buffer.length - offset; 272 ubyte* buf = buffer.ptr + offset; 273 274 assert(sz >= minimumFinalSize(), "Have sufficient final input"); 275 276 const size_t BS = cipher().blockSize(); 277 278 if (sz % BS == 0) 279 { 280 update(buffer, offset); 281 } 282 else 283 { 284 // steal ciphertext 285 const size_t full_blocks = ((sz / BS) - 1) * BS; 286 const size_t final_bytes = sz - full_blocks; 287 assert(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); 288 289 SecureVector!ubyte last = SecureVector!ubyte(buf[full_blocks .. full_blocks + final_bytes]); 290 buffer.resize(full_blocks + offset); 291 update(buffer, offset); 292 293 xorBuf(last, tweak() + BS, BS); 294 cipher().decrypt(last); 295 xorBuf(last, tweak() + BS, BS); 296 297 foreach (size_t i; 0 .. (final_bytes - BS)) 298 { 299 last[i] ^= last[i + BS]; 300 last[i + BS] ^= last[i]; 301 last[i] ^= last[i + BS]; 302 } 303 304 xorBuf(last, tweak(), BS); 305 cipher().decrypt(last); 306 xorBuf(last, tweak(), BS); 307 308 buffer ~= last; 309 } 310 } 311 312 override size_t outputLength(size_t input_length) const 313 { 314 // might be less 315 return input_length; 316 } 317 318 // Interface fallthrough 319 override string provider() const { return "core"; } 320 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 321 override size_t updateGranularity() const { return super.updateGranularity(); } 322 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 323 override @property string name() const { return super.name; } 324 override void clear() { return super.clear(); } 325 override size_t minimumFinalSize() const { return super.minimumFinalSize(); } 326 override bool validNonceLength(size_t n) const { 327 return super.validNonceLength(n); 328 } 329 } 330 331 332 private: 333 334 void polyDouble128(ubyte* output, const(ubyte)* input) 335 { 336 ulong X0 = loadLittleEndian!ulong(input, 0); 337 ulong X1 = loadLittleEndian!ulong(input, 1); 338 339 const bool carry = (X1 >> 63); 340 341 X1 = (X1 << 1) | (X0 >> 63); 342 X0 = (X0 << 1); 343 344 if (carry) 345 X0 ^= 0x87; 346 347 storeLittleEndian(output, X0, X1); 348 } 349 350 void polyDouble64(ubyte* output, const(ubyte)* input) 351 { 352 ulong X = loadLittleEndian!ulong(input, 0); 353 const bool carry = (X >> 63); 354 X <<= 1; 355 if (carry) 356 X ^= 0x1B; 357 storeLittleEndian(X, output); 358 } 359 360 void polyDouble(ubyte* output, const(ubyte)* input, size_t size) 361 { 362 if (size == 8) 363 polyDouble64(output, input); 364 else 365 polyDouble128(output, input); 366 }