1 /* 2 * Compression Transform 3 * 4 * Copyright: 5 * (C) 2014 Jack Lloyd 6 * (C) 2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 12 module botan.compression.compress; 13 14 import botan.constants; 15 16 import botan.algo_base.transform; 17 import botan.utils.types; 18 import botan.utils.mem_ops; 19 public import botan.compression.compress_utils; 20 import botan.compression.zlib; 21 import botan.compression.lzma; 22 import botan.compression.bzip2; 23 24 abstract class CompressorTransform : Transformation 25 { 26 public: 27 override size_t updateGranularity() const { return 1; } 28 29 override size_t minimumFinalSize() const { return 0; } 30 31 override size_t defaultNonceLength() const { return 0; } 32 33 override bool validNonceLength(size_t nonce_len) const { return nonce_len == 0; } 34 35 abstract void flush(ref SecureVector!ubyte buf, size_t offset = 0) { update(buf, offset); } 36 37 override size_t outputLength(size_t) const 38 { 39 throw new Exception(name() ~ " output length indeterminate"); 40 } 41 } 42 43 interface CompressionStream 44 { 45 public: 46 void nextIn(ubyte* b, size_t len); 47 48 void nextOut(ubyte* b, size_t len); 49 50 size_t availIn() const; 51 52 size_t availOut() const; 53 54 uint runFlag() const; 55 uint flushFlag() const; 56 uint finishFlag() const; 57 58 bool run(uint flags); 59 } 60 61 abstract class StreamCompression : CompressorTransform, Transformation 62 { 63 public: 64 void update(ref SecureVector!ubyte buf, size_t offset = 0) { process(buf, offset, m_stream.runFlag()); } 65 66 override void flush(ref SecureVector!ubyte buf, size_t offset = 0) { process(buf, offset, m_stream.flushFlag()); } 67 68 void finish(ref SecureVector!ubyte buf, size_t offset = 0) 69 { 70 process(buf, offset, m_stream.finishFlag()); 71 clear(); 72 } 73 74 override size_t updateGranularity() const { return 1; } 75 override size_t minimumFinalSize() const { return 0; } 76 override size_t defaultNonceLength() const { return 0; } 77 override bool validNonceLength(size_t nonce_len) const { return nonce_len == 0; } 78 override size_t outputLength(size_t input_length) const { return super.outputLength(input_length); } 79 80 void clear() { m_stream.free(); } 81 82 protected: 83 abstract CompressionStream makeStream() const; 84 85 override SecureVector!ubyte startRaw(const(ubyte)* data, size_t nonce_len) 86 { 87 if (!validNonceLength(nonce_len)) 88 throw new InvalidIVLength(name(), nonce_len); 89 90 m_stream = makeStream(); 91 return SecureVector!ubyte(); 92 } 93 94 void process(ref SecureVector!ubyte buf, size_t offset, uint flags) 95 { 96 assert(m_stream, "Initialized"); 97 assert(buf.length >= offset, "Offset is sane"); 98 99 if (m_buffer.length < buf.length + offset) 100 m_buffer.resize(buf.length + offset); 101 102 m_stream.nextIn(buf.ptr + offset, buf.length - offset); 103 m_stream.nextOut(m_buffer.ptr + offset, m_buffer.length - offset); 104 105 while(true) 106 { 107 m_stream.run(flags); 108 109 if (m_stream.availOut() == 0) 110 { 111 const size_t added = 8 + m_buffer.length; 112 m_buffer.resize(m_buffer.length + added); 113 m_stream.nextOut(&m_buffer[m_buffer.length - added], added); 114 } 115 else if (m_stream.availIn() == 0) 116 { 117 m_buffer.resize(m_buffer.length - m_stream.availOut()); 118 break; 119 } 120 } 121 122 copyMem(m_buffer.ptr + 0, buf.ptr + 0, offset); 123 buf.swap(m_buffer); 124 } 125 126 127 SecureVector!ubyte m_buffer; 128 Unique!CompressionStream m_stream; 129 } 130 131 abstract class StreamDecompression : CompressorTransform, Transformation 132 { 133 public: 134 void update(ref SecureVector!ubyte buf, size_t offset = 0) 135 { 136 process(buf, offset, m_stream.runFlag()); 137 } 138 139 void finish(ref SecureVector!ubyte buf, size_t offset = 0) 140 { 141 if (buf.length != offset || m_stream.get()) 142 process(buf, offset, m_stream.finishFlag()); 143 144 if (m_stream.get()) 145 throw new Exception(name() ~ " finished but not at stream end"); 146 } 147 148 void clear() { m_stream.free(); } 149 override size_t updateGranularity() const { return 1; } 150 override size_t minimumFinalSize() const { return 0; } 151 override size_t defaultNonceLength() const { return 0; } 152 override bool validNonceLength(size_t nonce_len) const { return nonce_len == 0; } 153 override size_t outputLength(size_t input_length) const { return super.outputLength(input_length); } 154 155 protected: 156 override SecureVector!ubyte startRaw(const(ubyte)* data, size_t nonce_len) 157 { 158 if (!validNonceLength(nonce_len)) 159 throw new InvalidIVLength(name(), nonce_len); 160 161 m_stream = makeStream(); 162 163 return SecureVector!ubyte(); 164 } 165 166 void process(ref SecureVector!ubyte buf, size_t offset, uint flags) 167 { 168 assert(m_stream, "Initialized"); 169 assert(buf.length >= offset, "Offset is sane"); 170 171 if (m_buffer.length < buf.length + offset) 172 m_buffer.resize(buf.length + offset); 173 174 m_stream.nextIn(buf.ptr + offset, buf.length - offset); 175 m_stream.nextOut(m_buffer.ptr + offset, m_buffer.length - offset); 176 177 while(true) 178 { 179 const bool stream_end = m_stream.run(flags); 180 181 if (stream_end) 182 { 183 if (m_stream.availIn() == 0) // all data consumed? 184 { 185 m_buffer.resize(m_buffer.length - m_stream.availOut()); 186 clear(); 187 break; 188 } 189 190 // More data follows: try to process as a following stream 191 const size_t read = (buf.length - offset) - m_stream.availIn(); 192 start(); 193 m_stream.nextIn(buf.ptr + offset + read, buf.length - offset - read); 194 } 195 196 if (m_stream.availOut() == 0) 197 { 198 const size_t added = 8 + m_buffer.length; 199 m_buffer.resize(m_buffer.length + added); 200 m_stream.nextOut(&m_buffer[m_buffer.length - added], added); 201 } 202 else if (m_stream.availIn() == 0) 203 { 204 m_buffer.resize(m_buffer.length - m_stream.availOut()); 205 break; 206 } 207 } 208 209 copyMem(m_buffer.ptr, buf.ptr, offset); 210 buf.swap(m_buffer); 211 } 212 213 abstract CompressionStream makeStream() const; 214 215 SecureVector!ubyte m_buffer; 216 Unique!CompressionStream m_stream; 217 } 218 219 220 CompressorTransform makeCompressor(in string type, size_t level) 221 { 222 static if (BOTAN_HAS_ZLIB) 223 224 { 225 if (type == "zlib") 226 return new ZlibCompression(level); 227 if (type == "deflate") 228 return new DeflateCompression(level); 229 } 230 231 static if (BOTAN_HAS_BZIP2) 232 { 233 if (type == "bzip2") 234 return new Bzip2Compression(level); 235 } 236 237 static if (BOTAN_HAS_LZMA) 238 { 239 if (type == "lzma") 240 return new LZMACompression(level); 241 } 242 243 throw new Exception("Unknown compression type " ~ type); 244 } 245 246 CompressorTransform makeDecompressor(in string type) 247 { 248 static if (BOTAN_HAS_ZLIB) 249 { 250 if (type == "zlib") 251 return new ZlibDecompression(); 252 if (type == "deflate") 253 return new DeflateDecompression(); 254 } 255 256 static if (BOTAN_HAS_BZIP2) 257 { 258 if (type == "bzip2") 259 return new Bzip2Decompression; 260 } 261 262 static if (BOTAN_HAS_LZMA) 263 { 264 if (type == "lzma") 265 return new LZMADecompression; 266 } 267 268 throw new Exception("Unknown compression type " ~ type); 269 } 270 271 static if (BOTAN_HAS_TESTS && !SKIP_COMPRESSION_TEST) unittest { 272 logDebug("Testing compress.d ..."); 273 { 274 static if (BOTAN_HAS_ZLIB) { 275 Unique!CompressorTransform comp = makeCompressor("zlib", 9); // level 1-9 276 SecureVector!ubyte buf; 277 SecureVector!ubyte verif; 278 buf ~= "Some message"; 279 verif = buf.dup; 280 comp.start(); 281 comp.finish(buf); 282 283 Unique!CompressorTransform dec = makeDecompressor("zlib"); 284 dec.start(); 285 dec.finish(buf); 286 assert(buf == verif); 287 logDebug("Zlib ... PASSED"); 288 } 289 } 290 { 291 static if (BOTAN_HAS_ZLIB) { 292 Unique!CompressorTransform comp = makeCompressor("deflate", 9); // level 1-9 293 SecureVector!ubyte buf; 294 SecureVector!ubyte verif; 295 buf ~= "Some message"; 296 verif = buf.dup; 297 comp.start(); 298 comp.finish(buf); 299 300 Unique!CompressorTransform dec = makeDecompressor("deflate"); 301 dec.start(); 302 dec.finish(buf); 303 assert(buf == verif); 304 logDebug("Deflate ... PASSED"); 305 } 306 } 307 { 308 static if (BOTAN_HAS_BZIP2) { 309 Unique!CompressorTransform comp = makeCompressor("bzip2", 9); // level 1-9 310 SecureVector!ubyte buf; 311 SecureVector!ubyte verif; 312 buf ~= "Some message"; 313 verif = buf.dup; 314 comp.start(); 315 comp.finish(buf); 316 317 Unique!CompressorTransform dec = makeDecompressor("bzip2"); 318 dec.start(); 319 dec.finish(buf); 320 assert(buf == verif); 321 logDebug("Bzip2 ... PASSED"); 322 } 323 } 324 { 325 static if (BOTAN_HAS_LZMA) { 326 Unique!CompressorTransform comp = makeCompressor("lzma", 9); // level 1-9 327 SecureVector!ubyte buf; 328 SecureVector!ubyte verif; 329 buf ~= "Some message "; 330 verif = buf.dup; 331 comp.start(); 332 comp.finish(buf); 333 Unique!CompressorTransform dec = makeDecompressor("lzma"); 334 dec.start(); 335 dec.finish(buf); 336 assert(buf == verif); 337 logDebug("LZMA ... PASSED"); 338 } 339 } 340 }