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