diff options
-rwxr-xr-x | configure.py | 2 | ||||
-rw-r--r-- | doc/credits.rst | 5 | ||||
-rw-r--r-- | doc/license.rst | 1 | ||||
-rw-r--r-- | doc/relnotes/1_11_0.rst | 4 | ||||
-rw-r--r-- | src/filters/lzma/info.txt | 11 | ||||
-rw-r--r-- | src/filters/lzma/lzma.cpp | 326 | ||||
-rw-r--r-- | src/filters/lzma/lzma.h | 74 |
7 files changed, 422 insertions, 1 deletions
diff --git a/configure.py b/configure.py index ef89e91cc..7b8941429 100755 --- a/configure.py +++ b/configure.py @@ -341,7 +341,7 @@ def process_command_line(args): mods_group.add_option('--no-autoload', action='store_true', default=False, help='disable automatic loading') - for mod in ['sqlite3', 'openssl', 'gnump', 'bzip2', 'zlib']: + for mod in ['sqlite3', 'openssl', 'gnump', 'bzip2', 'zlib', 'lzma']: mods_group.add_option('--with-%s' % (mod), help='add support for using %s' % (mod), diff --git a/doc/credits.rst b/doc/credits.rst index 076498756..d1cafa603 100644 --- a/doc/credits.rst +++ b/doc/credits.rst @@ -78,3 +78,8 @@ snail-mail address (S). W: http://www.flexsecure.de/ D: GF(p) arithmetic, CVC, Shanks-Tonelli algorithm S: Darmstadt, Germany + + N: Vojtech Kral + E: [email protected] + D: LZMA compression module + S: Czech Republic diff --git a/doc/license.rst b/doc/license.rst index 55b74ba13..83e1873f3 100644 --- a/doc/license.rst +++ b/doc/license.rst @@ -23,6 +23,7 @@ Botan (http://botan.randombit.net/) is distributed under these terms:: 2007 Christoph Ludwig 2007 Patrick Sona 2010 Olivier de Gaalon + 2012 Vojtech Kral All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/doc/relnotes/1_11_0.rst b/doc/relnotes/1_11_0.rst index 1d3909dfe..32af46774 100644 --- a/doc/relnotes/1_11_0.rst +++ b/doc/relnotes/1_11_0.rst @@ -37,6 +37,10 @@ using a best-fit allocator and all metadata held outside the mmap'ed range, in an effort to make best use of the very limited amount of memory current Linux kernels allow unpriveledged users to lock. +A filter using LZMA was contributed by Vojtech Kral. It is available +if LZMA support was enabled at compilation time by passing +``--with-lzma`` to ``configure.py``. + :rfc:`5915` adds some extended information which can be included in ECC private keys which the ECC key decoder did not expect, causing an exception when such a key was loaded. In particular, recent versions diff --git a/src/filters/lzma/info.txt b/src/filters/lzma/info.txt new file mode 100644 index 000000000..d9e924eef --- /dev/null +++ b/src/filters/lzma/info.txt @@ -0,0 +1,11 @@ +define COMPRESSOR_LZMA + +load_on request + +<libs> +all -> lzma +</libs> + +<requires> +filters +</requires> diff --git a/src/filters/lzma/lzma.cpp b/src/filters/lzma/lzma.cpp new file mode 100644 index 000000000..0bcda0343 --- /dev/null +++ b/src/filters/lzma/lzma.cpp @@ -0,0 +1,326 @@ +/* +* Lzma Compressor +* (C) 2001 Peter J Jones +* 2001-2007 Jack Lloyd +* 2006 Matt Johnston +* 2012 Vojtech Kral +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/lzma.h> +#include <botan/exceptn.h> + +#include <cstring> +#include <map> +#include <lzma.h> + +namespace Botan { + +namespace { + +/* +* Allocation Information for Lzma +*/ +class Lzma_Alloc_Info + { + public: + std::map<void*, size_t> current_allocs; + }; + +/* +* Allocation Function for Lzma +*/ +void* lzma_malloc(void *opaque, size_t /*nmemb*/, size_t size) + { + Lzma_Alloc_Info* info = static_cast<Lzma_Alloc_Info*>(opaque); + void* ptr = std::malloc(size); // It is guaranteed by liblzma doc that nmemb is always set to 1 + info->current_allocs[ptr] = size; + return ptr; + } + +/* +* Allocation Function for Lzma +*/ +void lzma_free(void *opaque, void *ptr) + { + if(!ptr) return; // liblzma sometimes does pass zero ptr + + Lzma_Alloc_Info* info = static_cast<Lzma_Alloc_Info*>(opaque); + auto i = info->current_allocs.find(ptr); + if(i == info->current_allocs.end()) + throw Invalid_Argument("lzma_free: Got pointer not allocated by us"); + + std::memset(ptr, 0, i->second); + std::free(ptr); + } + +} + +/** +* Wrapper Type for lzma_stream +*/ +class Lzma_Stream + { + public: + /** + * Underlying stream + */ + lzma_stream stream; + + /** + * Constructor + */ + Lzma_Stream() : + stream(LZMA_STREAM_INIT) + { + stream.allocator = new lzma_allocator; + stream.allocator->alloc = lzma_malloc; + stream.allocator->free = lzma_free; + stream.allocator->opaque = new Lzma_Alloc_Info; + } + + /** + * Destructor + */ + ~Lzma_Stream() + { + Lzma_Alloc_Info* info = static_cast<Lzma_Alloc_Info*>(stream.allocator->opaque); + delete info; + delete stream.allocator; + std::memset(&stream, 0, sizeof(lzma_stream)); + } + }; + +/* +* Lzma_Compression Constructor +*/ +Lzma_Compression::Lzma_Compression(size_t l) : + level((l >= 9) ? 9 : l), + buffer(DEFAULT_BUFFERSIZE), + lzma(0) + { + } + +/* +* Start Compressing with Lzma +*/ +void Lzma_Compression::start_msg() + { + clear(); + lzma = new Lzma_Stream; + + lzma_ret ret = lzma_easy_encoder(&(lzma->stream), level, LZMA_CHECK_CRC64); + + if(ret == LZMA_MEM_ERROR) + throw Memory_Exhaustion(); + else if(ret != LZMA_OK) + throw Invalid_Argument("Bad setting in lzma_easy_encoder"); + } + +/* +* Compress Input with Lzma +*/ +void Lzma_Compression::write(const byte input[], size_t length) + { + lzma->stream.next_in = static_cast<const uint8_t*>(input); + lzma->stream.avail_in = length; + + while(lzma->stream.avail_in != 0) + { + lzma->stream.next_out = static_cast<uint8_t*>(&buffer[0]); + lzma->stream.avail_out = buffer.size(); + + lzma_ret ret = lzma_code(&(lzma->stream), LZMA_RUN); + + if(ret == LZMA_MEM_ERROR) + throw Memory_Exhaustion(); + else if (ret != LZMA_OK) + throw std::runtime_error("Lzma compression: Error writing"); + + send(&buffer[0], buffer.size() - lzma->stream.avail_out); + } + } + +/* +* Finish Compressing with Lzma +*/ +void Lzma_Compression::end_msg() + { + lzma->stream.next_in = 0; + lzma->stream.avail_in = 0; + + int ret = LZMA_OK; + while(ret != LZMA_STREAM_END) + { + lzma->stream.next_out = reinterpret_cast<uint8_t*>(&buffer[0]); + lzma->stream.avail_out = buffer.size(); + + ret = lzma_code(&(lzma->stream), LZMA_FINISH); + send(&buffer[0], buffer.size() - lzma->stream.avail_out); + } + + clear(); + } + +/* +* Flush the Lzma Compressor +*/ +void Lzma_Compression::flush() + { + lzma->stream.next_in = 0; + lzma->stream.avail_in = 0; + + while(true) + { + lzma->stream.next_out = reinterpret_cast<uint8_t*>(&buffer[0]); + lzma->stream.avail_out = buffer.size(); + + lzma_ret ret = lzma_code(&(lzma->stream), LZMA_FULL_FLUSH); + + if(ret == LZMA_MEM_ERROR) + throw Memory_Exhaustion(); + else if (ret != LZMA_OK && ret != LZMA_STREAM_END) + throw std::runtime_error("Lzma compression: Error flushing"); + + send(&buffer[0], buffer.size() - lzma->stream.avail_out); + + if(lzma->stream.avail_out == buffer.size()) + break; + } + } + +/* +* Clean up Compression Context +*/ +void Lzma_Compression::clear() + { + zeroise(buffer); + + if(lzma) + { + lzma_end(&(lzma->stream)); + delete lzma; + lzma = 0; + } + } + +/* +* Lzma_Decompression Constructor +*/ +Lzma_Decompression::Lzma_Decompression() : + buffer(DEFAULT_BUFFERSIZE), + lzma(0), + no_writes(true) + { + } + +/* +* Start Decompressing with Lzma +*/ +void Lzma_Decompression::start_msg() + { + clear(); + lzma = new Lzma_Stream; + + lzma_ret ret = lzma_stream_decoder(&(lzma->stream), UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED); + + if(ret == LZMA_MEM_ERROR) + throw Memory_Exhaustion(); + else if(ret != LZMA_OK) + throw Invalid_Argument("Bad setting in lzma_stream_decoder"); + } + +/* +* Decompress Input with Lzma +*/ +void Lzma_Decompression::write(const byte input_arr[], size_t length) + { + if(length) no_writes = false; + + const uint8_t* input = reinterpret_cast<const uint8_t*>(input_arr); + + lzma->stream.next_in = input; + lzma->stream.avail_in = length; + + while(lzma->stream.avail_in != 0) + { + lzma->stream.next_out = reinterpret_cast<uint8_t*>(&buffer[0]); + lzma->stream.avail_out = buffer.size(); + + lzma_ret ret = lzma_code(&(lzma->stream), LZMA_RUN); + + if(ret != LZMA_OK && ret != LZMA_STREAM_END) + { + clear(); + if(ret == LZMA_DATA_ERROR) + throw Decoding_Error("Lzma_Decompression: Data integrity error"); + else if(ret == LZMA_MEM_ERROR) + throw Memory_Exhaustion(); + else + throw std::runtime_error("Lzma decompression: Unknown error"); + } + + send(&buffer[0], buffer.size() - lzma->stream.avail_out); + + if(ret == LZMA_STREAM_END) + { + size_t read_from_block = length - lzma->stream.avail_in; + start_msg(); + + lzma->stream.next_in = input + read_from_block; + lzma->stream.avail_in = length - read_from_block; + + input += read_from_block; + length -= read_from_block; + } + } + } + +/* +* Finish Decompressing with Lzma +*/ +void Lzma_Decompression::end_msg() + { + if(no_writes) return; + lzma->stream.next_in = 0; + lzma->stream.avail_in = 0; + + int ret = LZMA_OK; + + while(ret != LZMA_STREAM_END) + { + lzma->stream.next_out = reinterpret_cast<uint8_t*>(&buffer[0]); + lzma->stream.avail_out = buffer.size(); + ret = lzma_code(&(lzma->stream), LZMA_FINISH); + + if(ret != LZMA_OK && ret != LZMA_STREAM_END) + { + clear(); + throw Decoding_Error("Lzma_Decompression: Error finalizing"); + } + + send(&buffer[0], buffer.size() - lzma->stream.avail_out); + } + + clear(); + } + +/* +* Clean up Decompression Context +*/ +void Lzma_Decompression::clear() + { + zeroise(buffer); + + no_writes = true; + + if(lzma) + { + lzma_end(&(lzma->stream)); + delete lzma; + lzma = 0; + } + } + +} diff --git a/src/filters/lzma/lzma.h b/src/filters/lzma/lzma.h new file mode 100644 index 000000000..33e12d580 --- /dev/null +++ b/src/filters/lzma/lzma.h @@ -0,0 +1,74 @@ +/* +* Lzma Compressor +* (C) 2001 Peter J Jones +* 2001-2007 Jack Lloyd +* 2012 Vojtech Kral +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_LZMA_H__ +#define BOTAN_LZMA_H__ + +#include <botan/filter.h> + +namespace Botan { + +/** +* Lzma Compression Filter +*/ +class BOTAN_DLL Lzma_Compression : public Filter + { + public: + std::string name() const { return "Lzma_Compression"; } + + void write(const byte input[], size_t length); + void start_msg(); + void end_msg(); + + /** + * Flush the compressor + */ + void flush(); + + /** + * @param level how much effort to use on compressing (0 to 9); + * higher levels are slower but tend to give better + * compression + */ + Lzma_Compression(size_t level = 6); + + ~Lzma_Compression() { clear(); } + private: + void clear(); + const size_t level; + + secure_vector<byte> buffer; + class Lzma_Stream* lzma; + }; + +/** +* Lzma Decompression Filter +*/ +class BOTAN_DLL Lzma_Decompression : public Filter + { + public: + std::string name() const { return "Lzma_Decompression"; } + + void write(const byte input[], size_t length); + void start_msg(); + void end_msg(); + + Lzma_Decompression(); + ~Lzma_Decompression() { clear(); } + private: + void clear(); + + secure_vector<byte> buffer; + class Lzma_Stream* lzma; + bool no_writes; + }; + +} + +#endif |