1 /** 2 * EAX Mode 3 * 4 * Copyright: 5 * (C) 1999-2007,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.eax; 12 13 import botan.constants; 14 static if (BOTAN_HAS_AEAD_EAX): 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.mac.cmac; 21 import botan.stream.ctr; 22 import botan.utils.parsing; 23 import botan.utils.xor_buf; 24 import botan.utils.mem_ops; 25 import std.algorithm; 26 27 /** 28 * EAX base class 29 */ 30 abstract class EAXMode : AEADMode, Transformation 31 { 32 public: 33 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) 34 { 35 if (!validNonceLength(nonce_len)) 36 throw new InvalidIVLength(name, nonce_len); 37 38 m_nonce_mac[] = eaxPrf(0, this.blockSize(), *m_cmac, nonce, nonce_len)[]; 39 40 m_ctr.setIv(m_nonce_mac.ptr, m_nonce_mac.length); 41 42 for (size_t i = 0; i != this.blockSize() - 1; ++i) 43 m_cmac.update(0); 44 m_cmac.update(2); 45 46 return SecureVector!ubyte(); 47 } 48 49 50 override void setAssociatedData(const(ubyte)* ad, size_t length) 51 { 52 m_ad_mac[] = eaxPrf(1, this.blockSize(), *m_cmac, ad, length)[]; 53 } 54 55 override @property string name() const 56 { 57 return (m_cipher.name ~ "/EAX"); 58 } 59 60 override size_t updateGranularity() const 61 { 62 return 8 * m_cipher.parallelBytes(); 63 } 64 65 override KeyLengthSpecification keySpec() const 66 { 67 return m_cipher.keySpec(); 68 } 69 70 // EAX supports arbitrary nonce lengths 71 override bool validNonceLength(size_t) const { return true; } 72 73 override size_t tagSize() const { return m_tag_size; } 74 75 override void clear() 76 { 77 m_cipher.clear(); 78 m_ctr.clear(); 79 m_cmac.clear(); 80 zeroise(m_ad_mac); 81 zeroise(m_nonce_mac); 82 } 83 84 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 85 86 protected: 87 final override void keySchedule(const(ubyte)* key, size_t length) 88 { 89 /* 90 * These could share the key schedule, which is one nice part of EAX, 91 * but it's much easier to ignore that here... 92 */ 93 m_ctr.setKey(key, length); 94 m_cmac.setKey(key, length); 95 96 m_ad_mac = eaxPrf(1, this.blockSize(), *m_cmac, null, 0); 97 } 98 99 /** 100 * Params: 101 * cipher = the cipher to use 102 * tag_size = is how big the auth tag will be 103 */ 104 this(BlockCipher cipher, size_t tag_size) 105 { 106 m_tag_size = tag_size ? tag_size : cipher.blockSize(); 107 m_cipher = cipher; 108 m_ctr = new CTRBE(m_cipher.clone()); 109 m_cmac = new CMAC(m_cipher.clone()); 110 if (m_tag_size < 8 || m_tag_size > m_cmac.outputLength) 111 throw new InvalidArgument(name ~ ": Bad tag size " ~ to!string(tag_size)); 112 } 113 114 final size_t blockSize() const { return m_cipher.blockSize(); } 115 116 size_t m_tag_size; 117 118 Unique!BlockCipher m_cipher; 119 Unique!StreamCipher m_ctr; 120 Unique!MessageAuthenticationCode m_cmac; 121 122 SecureVector!ubyte m_ad_mac; 123 124 SecureVector!ubyte m_nonce_mac; 125 } 126 127 /** 128 * EAX Encryption 129 */ 130 final class EAXEncryption : EAXMode, Transformation 131 { 132 public: 133 /** 134 * Params: 135 * cipher = a 128-bit block cipher 136 * tag_size = is how big the auth tag will be 137 */ 138 this(BlockCipher cipher, size_t tag_size = 0) 139 { 140 super(cipher, tag_size); 141 } 142 143 override size_t outputLength(size_t input_length) const 144 { return input_length + tagSize(); } 145 146 override size_t minimumFinalSize() const { return 0; } 147 148 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 149 { 150 assert(buffer.length >= offset, "Offset is sane"); 151 const size_t sz = buffer.length - offset; 152 ubyte* buf = buffer.ptr + offset; 153 154 m_ctr.cipher(buf, buf, sz); 155 m_cmac.update(buf, sz); 156 } 157 158 override void finish(ref SecureVector!ubyte buffer, size_t offset) 159 { 160 update(buffer, offset); 161 162 SecureVector!ubyte data_mac = m_cmac.finished(); 163 xorBuf(data_mac, m_nonce_mac, data_mac.length); 164 xorBuf(data_mac, m_ad_mac, data_mac.length); 165 166 buffer ~= data_mac[0 .. tagSize()]; 167 } 168 169 // Interface fallthrough 170 override string provider() const { return "core"; } 171 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 172 override size_t updateGranularity() const { return super.updateGranularity(); } 173 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 174 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 175 override @property string name() const { return super.name; } 176 override void clear() { return super.clear(); } 177 } 178 179 /** 180 * EAX Decryption 181 */ 182 final class EAXDecryption : EAXMode, Transformation 183 { 184 public: 185 /** 186 * Params: 187 * cipher = a 128-bit block cipher 188 * tag_size = is how big the auth tag will be 189 */ 190 this(BlockCipher cipher, size_t tag_size = 0) 191 { 192 super(cipher, tag_size); 193 } 194 195 override size_t outputLength(size_t input_length) const 196 { 197 assert(input_length > tagSize(), "Sufficient input"); 198 return input_length - tagSize(); 199 } 200 201 override size_t minimumFinalSize() const { return tagSize(); } 202 203 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 204 { 205 assert(buffer.length >= offset, "Offset is sane"); 206 const size_t sz = buffer.length - offset; 207 ubyte* buf = buffer.ptr + offset; 208 209 m_cmac.update(buf, sz); 210 m_ctr.cipher(buf, buf, sz); 211 } 212 213 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 214 { 215 assert(buffer.length >= offset, "Offset is sane"); 216 const size_t sz = buffer.length - offset; 217 ubyte* buf = buffer.ptr + offset; 218 219 assert(sz >= tagSize(), "Have the tag as part of final input"); 220 221 const size_t remaining = sz - tagSize(); 222 223 if (remaining) 224 { 225 m_cmac.update(buf, remaining); 226 m_ctr.cipher(buf, buf, remaining); 227 } 228 229 const(ubyte)* included_tag = &buf[remaining]; 230 231 SecureVector!ubyte mac = m_cmac.finished(); 232 mac ^= m_nonce_mac; 233 mac ^= m_ad_mac; 234 235 if (!sameMem(mac.ptr, included_tag, tagSize())) 236 throw new IntegrityFailure("EAX tag check failed"); 237 238 buffer.resize(offset + remaining); 239 } 240 241 // Interface fallthrough 242 override string provider() const { return "core"; } 243 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 244 override size_t updateGranularity() const { return super.updateGranularity(); } 245 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 246 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 247 override @property string name() const { return super.name; } 248 override void clear() { return super.clear(); } 249 } 250 251 252 /* 253 * EAX MAC-based PRF 254 */ 255 SecureVector!ubyte eaxPrf(ubyte tag, size_t block_size, 256 MessageAuthenticationCode mac, 257 const(ubyte)* input, 258 size_t length) 259 { 260 foreach (size_t i; 0 .. (block_size - 1)) 261 mac.update(0); 262 mac.update(tag); 263 mac.update(input, length); 264 return mac.finished(); 265 }