1 /** 2 * ECB Mode 3 * 4 * Copyright: 5 * (C) 1999-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.ecb; 12 13 import botan.constants; 14 static if (BOTAN_HAS_MODE_ECB): 15 16 import botan.modes.cipher_mode; 17 import botan.block.block_cipher; 18 import botan.modes.mode_pad; 19 import botan.utils.loadstor; 20 import botan.utils.xor_buf; 21 import botan.utils.rounding; 22 import botan.utils.types; 23 24 /** 25 * ECB mode 26 */ 27 abstract class ECBMode : CipherMode, Transformation 28 { 29 public: 30 override SecureVector!ubyte startRaw(const(ubyte)*, size_t nonce_len) 31 { 32 if (!validNonceLength(nonce_len)) 33 throw new InvalidIVLength(name(), nonce_len); 34 35 return SecureVector!ubyte(); 36 } 37 38 override @property string name() const 39 { 40 return cipher().name ~ "/ECB/" ~ padding().name; 41 } 42 43 override size_t updateGranularity() const 44 { 45 return cipher().parallelBytes(); 46 } 47 48 override KeyLengthSpecification keySpec() const 49 { 50 return cipher().keySpec(); 51 } 52 53 override size_t defaultNonceLength() const 54 { 55 return 0; 56 } 57 58 override bool validNonceLength(size_t n) const 59 { 60 return (n == 0); 61 } 62 63 override void clear() 64 { 65 m_cipher.clear(); 66 } 67 68 override bool authenticated() const { return true; } 69 protected: 70 this(BlockCipher cipher, BlockCipherModePaddingMethod padding) 71 { 72 m_cipher = cipher; 73 m_padding = padding; 74 if (!m_padding.validBlocksize(m_cipher.blockSize())) 75 throw new InvalidArgument("Padding " ~ m_padding.name ~ " cannot be used with " ~ m_cipher.name ~ "/ECB"); 76 } 77 78 final BlockCipher cipher() const { return cast()*m_cipher; } 79 80 final const(BlockCipherModePaddingMethod) padding() const { return *m_padding; } 81 82 protected: 83 final override void keySchedule(const(ubyte)* key, size_t length) 84 { 85 m_cipher.setKey(key, length); 86 } 87 88 Unique!BlockCipher m_cipher; 89 Unique!BlockCipherModePaddingMethod m_padding; 90 } 91 92 /** 93 * ECB Encryption 94 */ 95 final class ECBEncryption : ECBMode, Transformation 96 { 97 public: 98 this(BlockCipher cipher, BlockCipherModePaddingMethod padding) 99 { 100 super(cipher, padding); 101 } 102 103 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 104 { 105 assert(buffer.length >= offset, "Offset is sane"); 106 const size_t sz = buffer.length - offset; 107 ubyte* buf = buffer.ptr + offset; 108 109 const size_t BS = cipher().blockSize(); 110 111 assert(sz % BS == 0, "ECB input is full blocks"); 112 const size_t blocks = sz / BS; 113 114 cipher().encryptN(buf, buf, blocks); 115 } 116 117 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 118 { 119 assert(buffer.length >= offset, "Offset is sane"); 120 const size_t sz = buffer.length - offset; 121 122 const size_t BS = cipher().blockSize(); 123 124 const size_t bytes_in_final_block = sz % BS; 125 126 padding().addPadding(buffer, bytes_in_final_block, BS); 127 128 if (buffer.length % BS) 129 throw new Exception("Did not pad to full block size in " ~ name); 130 131 update(buffer, offset); 132 } 133 134 override size_t outputLength(size_t input_length) const 135 { 136 return roundUp(input_length, cipher().blockSize()); 137 } 138 139 override size_t minimumFinalSize() const 140 { 141 return 0; 142 } 143 144 // Interface fallthrough 145 override string provider() const { return "core"; } 146 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 147 override size_t updateGranularity() const { return super.updateGranularity(); } 148 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 149 override @property string name() const { return super.name; } 150 override void clear() { return super.clear(); } 151 override bool validNonceLength(size_t n) const { 152 return super.validNonceLength(n); 153 } 154 } 155 156 /** 157 * ECB Decryption 158 */ 159 final class ECBDecryption : ECBMode, Transformation 160 { 161 public: 162 this(BlockCipher cipher, BlockCipherModePaddingMethod padding) 163 { 164 super(cipher, padding); 165 } 166 167 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 168 { 169 assert(buffer.length >= offset, "Offset is sane"); 170 const size_t sz = buffer.length - offset; 171 ubyte* buf = buffer.ptr + offset; 172 173 const size_t BS = cipher().blockSize(); 174 175 assert(sz % BS == 0, "Input is full blocks"); 176 size_t blocks = sz / BS; 177 178 cipher().decryptN(buf, buf, blocks); 179 } 180 181 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 182 { 183 assert(buffer.length >= offset, "Offset is sane"); 184 const size_t sz = buffer.length - offset; 185 186 const size_t BS = cipher().blockSize(); 187 188 if (sz == 0 || sz % BS) 189 throw new DecodingError(name ~ ": Ciphertext not a multiple of block size"); 190 191 update(buffer, offset); 192 193 const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.length-BS], BS); 194 buffer.resize(buffer.length - pad_bytes); // remove padding 195 } 196 197 override size_t outputLength(size_t input_length) const 198 { 199 return input_length; 200 } 201 202 override size_t minimumFinalSize() const 203 { 204 return cipher().blockSize(); 205 } 206 207 // Interface fallthrough 208 override string provider() const { return "core"; } 209 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 210 override size_t updateGranularity() const { return super.updateGranularity(); } 211 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 212 override @property string name() const { return super.name; } 213 override void clear() { return super.clear(); } 214 override bool validNonceLength(size_t n) const { 215 return super.validNonceLength(n); 216 } 217 }