1 /** 2 * SIV 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.siv; 12 13 import botan.constants; 14 static if (BOTAN_HAS_AEAD_SIV): 15 import botan.modes.aead.aead; 16 import botan.block.block_cipher; 17 import botan.stream.stream_cipher; 18 import botan.mac.mac; 19 import botan.mac.cmac; 20 import botan.stream.ctr; 21 import botan.utils.parsing; 22 import botan.utils.xor_buf; 23 import std.algorithm; 24 25 /** 26 * Base class for SIV encryption and decryption (@see RFC 5297) 27 */ 28 abstract class SIVMode : AEADMode, Transformation 29 { 30 public: 31 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) 32 { 33 if (!validNonceLength(nonce_len)) 34 throw new InvalidIVLength(name, nonce_len); 35 36 if (nonce_len) 37 m_nonce = m_cmac.process(nonce, nonce_len); 38 else 39 m_nonce.clear(); 40 41 m_msg_buf.clear(); 42 43 return SecureVector!ubyte(); 44 } 45 46 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 47 { 48 assert(buffer.length >= offset, "Offset is sane"); 49 //logTrace("Update: ", cast(ubyte[])buffer[]); 50 const size_t sz = buffer.length - offset; 51 ubyte* buf = buffer.ptr + offset; 52 m_msg_buf ~= buf[0 .. sz]; 53 buffer.resize(offset); // truncate msg 54 } 55 56 final void setAssociatedDataN(size_t n, const(ubyte)* ad, size_t length) 57 { 58 if (n >= m_ad_macs.length) 59 m_ad_macs.resize(n+1); 60 61 m_ad_macs[n] = m_cmac.process(ad, length); 62 } 63 64 override void setAssociatedData(const(ubyte)* ad, size_t ad_len) 65 { 66 setAssociatedDataN(0, ad, ad_len); 67 } 68 69 override @property string name() const 70 { 71 return m_name; 72 } 73 74 override size_t updateGranularity() const 75 { 76 /* 77 This value does not particularly matter as regardless update 78 buffers all input, so in theory this could be 1. However as for instance 79 TransformationFilter creates updateGranularity() ubyte buffers, use a 80 somewhat large size to avoid bouncing on a tiny buffer. 81 */ 82 return 128; 83 } 84 85 override KeyLengthSpecification keySpec() const 86 { 87 return m_cmac.keySpec().multiple(2); 88 } 89 90 override bool validNonceLength(size_t) const 91 { 92 return true; 93 } 94 95 override void clear() 96 { 97 m_ctr.clear(); 98 m_nonce.clear(); 99 m_msg_buf.clear(); 100 m_ad_macs.clear(); 101 } 102 103 override size_t tagSize() const { return 16; } 104 105 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 106 107 protected: 108 this(BlockCipher cipher) 109 { 110 m_name = cipher.name ~ "/SIV"; 111 m_ctr = new CTRBE(cipher.clone()); 112 m_cmac = new CMAC(cipher); 113 } 114 115 final StreamCipher ctr() { return *m_ctr; } 116 117 final void setCtrIv(SecureVector!ubyte V) 118 { 119 V[8] &= 0x7F; 120 V[12] &= 0x7F; 121 122 ctr().setIv(V.ptr, V.length); 123 } 124 125 final ref const(SecureVector!ubyte) msgBuf() { return m_msg_buf; } 126 127 final SecureVector!ubyte S2V(const(ubyte)* text, size_t text_len) 128 { 129 const ubyte[16] zero; 130 131 SecureVector!ubyte V = m_cmac.process(zero.ptr, 16); 132 133 for (size_t i = 0; i != m_ad_macs.length; ++i) 134 { 135 V = CMAC.polyDouble(V); 136 V ^= m_ad_macs[i]; 137 } 138 139 if (m_nonce.length > 0) 140 { 141 V = CMAC.polyDouble(V); 142 V ^= m_nonce; 143 } 144 145 if (text_len < 16) 146 { 147 V = CMAC.polyDouble(V); 148 xorBuf(V.ptr, text, text_len); 149 V[text_len] ^= 0x80; 150 return m_cmac.process(V); 151 } 152 153 m_cmac.update(text, text_len - 16); 154 xorBuf(V.ptr, text + text_len - 16, 16); 155 m_cmac.update(V); 156 157 return m_cmac.finished(); 158 } 159 protected: 160 161 final override void keySchedule(const(ubyte)* key, size_t length) 162 { 163 const size_t keylen = length / 2; 164 m_cmac.setKey(key, keylen); 165 m_ctr.setKey(key + keylen, keylen); 166 m_ad_macs.clear(); 167 } 168 169 private: 170 171 const string m_name; 172 173 Unique!StreamCipher m_ctr; 174 Unique!MessageAuthenticationCode m_cmac; 175 SecureVector!ubyte m_nonce, m_msg_buf; 176 Vector!( SecureArray!ubyte ) m_ad_macs; 177 } 178 179 /** 180 * SIV Encryption 181 */ 182 final class SIVEncryption : SIVMode, Transformation 183 { 184 public: 185 /** 186 * Params: 187 * cipher = a block cipher 188 */ 189 this(BlockCipher cipher) 190 { 191 super(cipher); 192 } 193 194 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 195 { 196 import std.algorithm : max; 197 assert(buffer.length >= offset, "Offset is sane"); 198 199 if (msgBuf().length > 0) 200 { 201 auto buffer2 = msgBuf().dup; 202 buffer2 ~= buffer; 203 buffer = buffer2.move; 204 } 205 206 SecureVector!ubyte V = S2V(buffer.ptr + offset, buffer.length - offset); 207 if (V.length > 0) { 208 auto buffer2 = V.dup; 209 buffer2 ~= buffer; 210 buffer = buffer2.move; 211 } 212 setCtrIv(V.dup); 213 ctr().cipher1(buffer.ptr + offset + V.length, buffer.length - offset - V.length); 214 } 215 216 override size_t outputLength(size_t input_length) const 217 { return input_length + tagSize(); } 218 219 override size_t minimumFinalSize() const { return 0; } 220 221 // Interface fallthrough 222 override string provider() const { return "core"; } 223 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 224 override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); } 225 override size_t updateGranularity() const { return super.updateGranularity(); } 226 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 227 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 228 override @property string name() const { return super.name; } 229 override void clear() { return super.clear(); } 230 } 231 232 /** 233 * SIV Decryption 234 */ 235 final class SIVDecryption : SIVMode, Transformation 236 { 237 public: 238 /** 239 * Params: 240 * cipher = a 128-bit block cipher 241 */ 242 this(BlockCipher cipher) 243 { 244 super(cipher); 245 } 246 247 override void finish(ref SecureVector!ubyte buffer, size_t offset) 248 { 249 import std.algorithm : max; 250 assert(buffer.length >= offset, "Offset is sane"); 251 252 if (msgBuf().length > 0) { 253 auto buffer2 = msgBuf().dup; 254 buffer2 ~= buffer; 255 buffer = buffer2.move; 256 } 257 258 const size_t sz = buffer.length - offset; 259 260 assert(sz >= tagSize(), "We have the tag"); 261 262 SecureVector!ubyte V = SecureVector!ubyte(buffer.ptr[offset .. offset + 16]); 263 264 setCtrIv(V.dup); 265 266 ctr().cipher(buffer.ptr + offset + V.length, buffer.ptr + offset, buffer.length - offset - V.length); 267 268 SecureVector!ubyte T = S2V(buffer.ptr + offset, buffer.length - offset - V.length); 269 270 if (T != V) 271 throw new IntegrityFailure("SIV tag check failed"); 272 273 buffer.resize(buffer.length - tagSize()); 274 } 275 276 override size_t outputLength(size_t input_length) const 277 { 278 assert(input_length > tagSize(), "Sufficient input"); 279 return input_length - tagSize(); 280 } 281 282 override size_t minimumFinalSize() const { return tagSize(); } 283 284 // Interface fallthrough 285 override string provider() const { return "core"; } 286 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 287 override void update(ref SecureVector!ubyte blocks, size_t offset = 0) { super.update(blocks, offset); } 288 override size_t updateGranularity() const { return super.updateGranularity(); } 289 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 290 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 291 override @property string name() const { return super.name; } 292 override void clear() { return super.clear(); } 293 }