1 /*
2 * Copyright (c) 2014, Vsevolod Stakhov
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <iostream>
31 #include <cerrno>
32 #include <cstring>
33 #include <arpa/inet.h>
34 #include "encrypt.h"
35 #include "nonce.h"
36 #include "aead.h"
37 #include "util.h"
38 #include "kdf.h"
39 #include "thread_pool.h"
40 #include "aligned_alloc.h"
41
42 namespace hpenc
43 {
44
45 class HPEncEncrypt::impl {
46 public:
47 std::unique_ptr<HPEncKDF> kdf;
48 std::vector <std::shared_ptr<HPencAead> > ciphers;
49 std::unique_ptr<HPEncNonce> nonce;
50 int fd_in, fd_out;
51 unsigned block_size;
52 std::vector<std::shared_ptr<aligned_vector> > in_bufs;
53 std::vector<std::shared_ptr<aligned_vector> > out_bufs;
54 HPEncHeader hdr;
55 bool encode;
56 bool random_mode;
57 std::unique_ptr<ThreadPool> pool;
58
impl(std::unique_ptr<HPEncKDF> && _kdf,const std::string & in,const std::string & out,AeadAlgorithm alg,unsigned _block_size,unsigned nthreads=0,bool _rmode=false)59 impl(std::unique_ptr<HPEncKDF> &&_kdf,
60 const std::string &in,
61 const std::string &out,
62 AeadAlgorithm alg,
63 unsigned _block_size,
64 unsigned nthreads = 0,
65 bool _rmode = false) : kdf(std::move(_kdf)), block_size(_block_size),
66 hdr(alg, _block_size), random_mode(_rmode)
67 {
68
69 if (!in.empty()) {
70 fd_in = open(in.c_str(), O_RDONLY);
71 if (fd_in == -1) {
72 std::cerr << "Cannot open input file '" << in << "': "
73 << ::strerror(errno) << std::endl;
74 }
75 }
76 else {
77 fd_in = STDIN_FILENO;
78 }
79
80 if (!out.empty()) {
81 fd_out = open(out.c_str(), O_WRONLY | O_TRUNC);
82 if (fd_out == -1) {
83 std::cerr << "Cannot open output file '" << out << "': "
84 << ::strerror(errno) << std::endl;
85 }
86 }
87 else {
88 fd_out = STDOUT_FILENO;
89 }
90
91 encode = false;
92 pool.reset(new ThreadPool(nthreads));
93 in_bufs.resize(pool->size());
94 out_bufs.resize(pool->size());
95 hdr.nonce = kdf->initialNonce();
96 auto klen = AeadKeyLengths[static_cast<int>(alg)];
97 auto key = kdf->genKey(klen);
98
99 for (auto i = 0U; i < pool->size(); i ++) {
100 auto cipher = std::make_shared<HPencAead>(alg, _rmode);
101 cipher->setKey(key);
102 if (!nonce) {
103 nonce.reset(new HPEncNonce(cipher->noncelen()));
104 }
105 ciphers.push_back(cipher);
106 in_bufs[i] = std::make_shared<aligned_vector>();
107 in_bufs[i]->resize(block_size);
108 out_bufs[i] = std::make_shared<aligned_vector>();
109 out_bufs[i]->resize(block_size + ciphers[0]->taglen());
110 }
111 }
112
~impl()113 virtual ~impl()
114 {
115 if (fd_in != -1) close(fd_in);
116 if (fd_out != -1) close(fd_out);
117 }
118
writeHeader()119 bool writeHeader()
120 {
121 return hdr.toFd(fd_out, encode);
122 }
123
writeBlock(ssize_t rd,aligned_vector * in_buf,aligned_vector * out_buf,const std::vector<byte> & n,std::shared_ptr<HPencAead> const & cipher)124 ssize_t writeBlock(ssize_t rd, aligned_vector *in_buf, aligned_vector *out_buf,
125 const std::vector<byte> &n, std::shared_ptr<HPencAead> const &cipher)
126 {
127 if (rd > 0) {
128 auto bs = htonl(rd);
129 auto tag = cipher->encrypt(reinterpret_cast<byte *>(&bs), sizeof(bs),
130 n.data(), n.size(), in_buf->data(), rd, out_buf->data());
131
132 if (!random_mode) {
133 if (!tag) {
134 return -1;
135 }
136
137 auto mac_pos = out_buf->data() + rd;
138 std::copy(tag->data, tag->data + tag->datalen, mac_pos);
139 return rd + tag->datalen;
140 }
141
142 return rd;
143
144 }
145 return -1;
146 }
147
readBlock(aligned_vector * io_buf)148 size_t readBlock(aligned_vector *io_buf)
149 {
150 return util::atomicRead(fd_in, io_buf->data(), block_size);
151 }
152 };
153
HPEncEncrypt(std::unique_ptr<HPEncKDF> && kdf,const std::string & in,const std::string & out,AeadAlgorithm alg,unsigned block_size,unsigned nthreads,bool random_mode)154 HPEncEncrypt::HPEncEncrypt(std::unique_ptr<HPEncKDF> &&kdf,
155 const std::string &in,
156 const std::string &out,
157 AeadAlgorithm alg,
158 unsigned block_size,
159 unsigned nthreads,
160 bool random_mode) :
161 pimpl(new impl(std::move(kdf), in, out, alg, block_size, nthreads, random_mode))
162 {
163 }
164
~HPEncEncrypt()165 HPEncEncrypt::~HPEncEncrypt()
166 {
167 }
168
encrypt(bool encode,unsigned count)169 void HPEncEncrypt::encrypt(bool encode, unsigned count)
170 {
171 pimpl->encode = encode;
172 bool last = false;
173 auto remain = count;
174
175 if (pimpl->random_mode || pimpl->writeHeader()) {
176 auto nblocks = 0U;
177 for (;;) {
178 auto blocks_read = 0;
179 std::vector< std::future<ssize_t> > results;
180 auto i = 0U;
181 for (auto &buf : pimpl->in_bufs) {
182 if (count > 0) {
183 if (remain == 0) {
184 last = true;
185 break;
186 }
187 remain --;
188 }
189
190 auto rd = pimpl->block_size;
191 if (!pimpl->random_mode) {
192 // For random mode we skip reading
193 rd = pimpl->readBlock(buf.get());
194 }
195
196 if (rd > 0) {
197 auto n = pimpl->nonce->incAndGet();
198 results.emplace_back(
199 pimpl->pool->enqueue(
200 &impl::writeBlock, pimpl.get(), rd, buf.get(),
201 pimpl->out_bufs[i].get(),
202 n, pimpl->ciphers[i]
203 ));
204 blocks_read ++;
205 }
206 else {
207 last = true;
208 break;
209 }
210 i ++;
211 }
212
213 i = 0;
214 for(auto && result: results) {
215 result.wait();
216 auto rd = result.get();
217 if (rd == -1) {
218 throw std::runtime_error("Cannot encrypt block");
219 }
220 else {
221 if (rd > 0) {
222 const auto &io_buf = pimpl->out_bufs[i].get();
223 if (encode) {
224 auto b64_out = util::base64Encode(io_buf->data(), rd);
225 if (util::atomicWrite(pimpl->fd_out,
226 reinterpret_cast<const byte *>(b64_out.data()),
227 b64_out.size()) == 0) {
228 if (pimpl->random_mode) {
229 // Assume that we are done
230 std::cerr << "Cannot write output: " <<
231 strerror(errno);
232 return;
233 }
234 else {
235 throw std::runtime_error("Cannot write "
236 "encrypted block");
237 }
238 }
239 }
240 else {
241 if (util::atomicWrite(pimpl->fd_out, io_buf->data(),
242 rd) == 0) {
243 if (pimpl->random_mode) {
244 // Assume that we are done
245 if (errno != ENOSPC && errno != EPIPE) {
246 std::cerr << "Cannot write output: " <<
247 strerror(errno) << std::endl;
248 }
249 return;
250 }
251 else {
252 throw std::runtime_error("Cannot write "
253 "encrypted block");
254 }
255 }
256 }
257 }
258 if (rd < pimpl->block_size) {
259 // We are done
260 return;
261 }
262 }
263 i++;
264 }
265
266 if (last) {
267 return;
268 }
269
270 if (++nblocks % rekey_blocks == 0) {
271 // Rekey all cipers
272 auto nkey = pimpl->kdf->genKey(pimpl->ciphers[0]->keylen());
273 for (auto const &cipher : pimpl->ciphers) {
274 cipher->setKey(nkey);
275 }
276 }
277 }
278 }
279 }
280
281 } /* namespace hpenc */
282