Botan 3.6.1
Crypto and TLS for C&
compress_utils.cpp
Go to the documentation of this file.
1/*
2* Compression Utils
3* (C) 2014,2016 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/compress_utils.h>
9
10#include <botan/exceptn.h>
11#include <botan/mem_ops.h>
12#include <botan/internal/fmt.h>
13#include <botan/internal/int_utils.h>
14#include <cstdlib>
15
16namespace Botan {
17
18Compression_Error::Compression_Error(const char* func_name, ErrorType type, int rc) :
19 Exception(fmt("Compression API {} failed with return code {}", func_name, rc)), m_type(type), m_rc(rc) {}
20
21void* Compression_Alloc_Info::do_malloc(size_t n, size_t size) {
22 // Precheck for integer overflow in the multiplication
23 // before passing to calloc, which may or may not check.
24 if(!checked_mul(n, size)) {
25 return nullptr;
26 }
27
28 void* ptr = std::calloc(n, size); // NOLINT(*-no-malloc)
29
30 /*
31 * Return null rather than throwing here as we are being called by a
32 * C library and it may not be possible for an exception to unwind
33 * the call stack from here. The compression library is expecting a
34 * function written in C and a null return on error, which it will
35 * send upwards to the compression wrappers.
36 */
37
38 if(ptr) {
39 m_current_allocs[ptr] = n * size;
40 }
41
42 return ptr;
43}
44
45void Compression_Alloc_Info::do_free(void* ptr) {
46 if(ptr) {
47 auto i = m_current_allocs.find(ptr);
48
49 if(i == m_current_allocs.end()) {
50 throw Internal_Error("Compression_Alloc_Info::free got pointer not allocated by us");
51 }
52
53 secure_scrub_memory(ptr, i->second);
54 std::free(ptr); // NOLINT(*-no-malloc)
55 m_current_allocs.erase(i);
56 }
57}
58
60 m_stream.reset();
61}
62
63void Stream_Compression::start(size_t level) {
64 m_stream = make_stream(level);
65}
66
67void Stream_Compression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
68 BOTAN_ASSERT(m_stream, "Initialized");
69 BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
70
71 // bzip doesn't like being called with no input and BZ_RUN
72 if(buf.size() == offset && flags == m_stream->run_flag()) {
73 return;
74 }
75
76 if(m_buffer.size() < buf.size() + offset) {
77 m_buffer.resize(buf.size() + offset);
78 }
79
80 // If the output buffer has zero length, .data() might return nullptr. This would
81 // make some compression algorithms (notably those provided by zlib) fail.
82 // Any small positive value works fine, but we choose 32 as it is the smallest power
83 // of two that is large enough to hold all the headers and trailers of the common
84 // formats, preventing further resizings to make room for output data.
85 if(m_buffer.empty()) {
86 m_buffer.resize(32);
87 }
88
89 m_stream->next_in(buf.data() + offset, buf.size() - offset);
90 m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
91
92 while(true) {
93 const bool stream_end = m_stream->run(flags);
94
95 if(stream_end) {
96 BOTAN_ASSERT(m_stream->avail_in() == 0, "After stream is done, no input remains to be processed");
97 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
98 break;
99 } else if(m_stream->avail_out() == 0) {
100 const size_t added = 8 + m_buffer.size();
101 m_buffer.resize(m_buffer.size() + added);
102 m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
103 } else if(m_stream->avail_in() == 0) {
104 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
105 break;
106 }
107 }
108
109 copy_mem(m_buffer.data(), buf.data(), offset);
110 buf.swap(m_buffer);
111}
112
113void Stream_Compression::update(secure_vector<uint8_t>& buf, size_t offset, bool flush) {
114 BOTAN_ASSERT(m_stream, "Initialized");
115 process(buf, offset, flush ? m_stream->flush_flag() : m_stream->run_flag());
116}
117
119 BOTAN_ASSERT(m_stream, "Initialized");
120 process(buf, offset, m_stream->finish_flag());
121 clear();
122}
123
125 m_stream.reset();
126}
127
128void Stream_Decompression::start() {
129 m_stream = make_stream();
130}
131
132void Stream_Decompression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
133 BOTAN_ASSERT(m_stream, "Initialized");
134 BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
135
136 if(m_buffer.size() < buf.size() + offset) {
137 m_buffer.resize(buf.size() + offset);
138 }
139
140 m_stream->next_in(buf.data() + offset, buf.size() - offset);
141 m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
142
143 while(true) {
144 const bool stream_end = m_stream->run(flags);
145
146 if(stream_end) {
147 if(m_stream->avail_in() == 0) {
148 // all data consumed
149 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
150 clear();
151 break;
152 }
153
154 // More data follows: try to process as a following stream
155 const size_t read = (buf.size() - offset) - m_stream->avail_in();
156 start();
157 m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read);
158 }
159
160 if(m_stream->avail_out() == 0) {
161 const size_t added = 8 + m_buffer.size();
162 m_buffer.resize(m_buffer.size() + added);
163 m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
164 } else if(m_stream->avail_in() == 0) {
165 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
166 break;
167 }
168 }
169
170 copy_mem(m_buffer.data(), buf.data(), offset);
171 buf.swap(m_buffer);
172}
173
175 process(buf, offset, m_stream->run_flag());
176}
177
179 if(buf.size() != offset || m_stream.get()) {
180 process(buf, offset, m_stream->finish_flag());
181 }
182
183 if(m_stream) {
184 throw Invalid_State(fmt("{} finished but not at stream end", name()));
185 }
186}
187
188} // namespace Botan
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
Compression_Error(const char *func_name, ErrorType type, int rc)
virtual std::string name() const =0
void update(secure_vector< uint8_t > &buf, size_t offset, bool flush) final
void finish(secure_vector< uint8_t > &buf, size_t offset) final
void update(secure_vector< uint8_t > &buf, size_t offset) final
void finish(secure_vector< uint8_t > &buf, size_t offset) final
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
void secure_scrub_memory(void *ptr, size_t n)
Definition os_utils.cpp:83
ErrorType
Definition exceptn.h:20
constexpr std::optional< T > checked_mul(T a, T b)
Definition int_utils.h:46
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:146