Botan 3.3.0
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/safeint.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 if(!BOTAN_CHECKED_MUL(n, size).has_value()) [[unlikely]] {
23 return nullptr;
24 }
25
26 void* ptr = std::calloc(n, size); // NOLINT(*-no-malloc)
27
28 /*
29 * Return null rather than throwing here as we are being called by a
30 * C library and it may not be possible for an exception to unwind
31 * the call stack from here. The compression library is expecting a
32 * function written in C and a null return on error, which it will
33 * send upwards to the compression wrappers.
34 */
35
36 if(ptr) {
37 m_current_allocs[ptr] = n * size;
38 }
39
40 return ptr;
41}
42
43void Compression_Alloc_Info::do_free(void* ptr) {
44 if(ptr) {
45 auto i = m_current_allocs.find(ptr);
46
47 if(i == m_current_allocs.end()) {
48 throw Internal_Error("Compression_Alloc_Info::free got pointer not allocated by us");
49 }
50
51 secure_scrub_memory(ptr, i->second);
52 std::free(ptr); // NOLINT(*-no-malloc)
53 m_current_allocs.erase(i);
54 }
55}
56
58 m_stream.reset();
59}
60
61void Stream_Compression::start(size_t level) {
62 m_stream = make_stream(level);
63}
64
65void Stream_Compression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
66 BOTAN_ASSERT(m_stream, "Initialized");
67 BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
68
69 // bzip doesn't like being called with no input and BZ_RUN
70 if(buf.size() == offset && flags == m_stream->run_flag()) {
71 return;
72 }
73
74 if(m_buffer.size() < buf.size() + offset) {
75 m_buffer.resize(buf.size() + offset);
76 }
77
78 // If the output buffer has zero length, .data() might return nullptr. This would
79 // make some compression algorithms (notably those provided by zlib) fail.
80 // Any small positive value works fine, but we choose 32 as it is the smallest power
81 // of two that is large enough to hold all the headers and trailers of the common
82 // formats, preventing further resizings to make room for output data.
83 if(m_buffer.empty()) {
84 m_buffer.resize(32);
85 }
86
87 m_stream->next_in(buf.data() + offset, buf.size() - offset);
88 m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
89
90 while(true) {
91 const bool stream_end = m_stream->run(flags);
92
93 if(stream_end) {
94 BOTAN_ASSERT(m_stream->avail_in() == 0, "After stream is done, no input remains to be processed");
95 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
96 break;
97 } else if(m_stream->avail_out() == 0) {
98 const size_t added = 8 + m_buffer.size();
99 m_buffer.resize(m_buffer.size() + added);
100 m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
101 } else if(m_stream->avail_in() == 0) {
102 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
103 break;
104 }
105 }
106
107 copy_mem(m_buffer.data(), buf.data(), offset);
108 buf.swap(m_buffer);
109}
110
111void Stream_Compression::update(secure_vector<uint8_t>& buf, size_t offset, bool flush) {
112 BOTAN_ASSERT(m_stream, "Initialized");
113 process(buf, offset, flush ? m_stream->flush_flag() : m_stream->run_flag());
114}
115
117 BOTAN_ASSERT(m_stream, "Initialized");
118 process(buf, offset, m_stream->finish_flag());
119 clear();
120}
121
123 m_stream.reset();
124}
125
126void Stream_Decompression::start() {
127 m_stream = make_stream();
128}
129
130void Stream_Decompression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
131 BOTAN_ASSERT(m_stream, "Initialized");
132 BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
133
134 if(m_buffer.size() < buf.size() + offset) {
135 m_buffer.resize(buf.size() + offset);
136 }
137
138 m_stream->next_in(buf.data() + offset, buf.size() - offset);
139 m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
140
141 while(true) {
142 const bool stream_end = m_stream->run(flags);
143
144 if(stream_end) {
145 if(m_stream->avail_in() == 0) // all data consumed?
146 {
147 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
148 clear();
149 break;
150 }
151
152 // More data follows: try to process as a following stream
153 const size_t read = (buf.size() - offset) - m_stream->avail_in();
154 start();
155 m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read);
156 }
157
158 if(m_stream->avail_out() == 0) {
159 const size_t added = 8 + m_buffer.size();
160 m_buffer.resize(m_buffer.size() + added);
161 m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
162 } else if(m_stream->avail_in() == 0) {
163 m_buffer.resize(m_buffer.size() - m_stream->avail_out());
164 break;
165 }
166 }
167
168 copy_mem(m_buffer.data(), buf.data(), offset);
169 buf.swap(m_buffer);
170}
171
173 process(buf, offset, m_stream->run_flag());
174}
175
177 if(buf.size() != offset || m_stream.get()) {
178 process(buf, offset, m_stream->finish_flag());
179 }
180
181 if(m_stream) {
182 throw Invalid_State(fmt("{} finished but not at stream end", name()));
183 }
184}
185
186} // 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:87
ErrorType
Definition exceptn.h:20
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
#define BOTAN_CHECKED_MUL(x, y)
Definition safeint.h:74