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 "util.h"
27 #include "encrypt.h"
28 #include "decrypt.h"
29 #include "kdf.h"
30 #include "nonce.h"
31 #include <unistd.h>
32 #include <iostream>
33 #include <cstdlib>
34 #include <sodium.h>
35
36 using namespace hpenc;
37
usage(char ** argv)38 static void usage(char **argv)
39 {
40 std::cerr << "Usage: " << argv[0] << " [-h] [-d] [-a algorithm] [-k key|-p] [-l]"
41 << "[-b block_size] [-B] [-r] [-c count] [psk]"
42 << std::endl
43 << "Available options: " << std::endl
44 << " -d Decrypt data" << std::endl
45 << " -a <algorithm> Use specified algorithm: chacha20," << std::endl
46 << " aes-128 or aes-256 or tiaoxin" << std::endl
47 << " -k <key> 52 bytes hex encoded pre-shared key" << std::endl
48 << " -p Read password from the terminal instead of key" << std::endl
49 << " -l Use legacy pbkdf method" << std::endl
50 << " -b <block_size> Block size to use (default: 4K)" << std::endl
51 << " -B Base64 output/input" << std::endl
52 << " -r Act as pseudo-random generator" << std:: endl
53 << " -c <count> Process <count> of blocks (default: no limit)" << std::endl;
54 ::exit(EXIT_FAILURE);
55 }
56
57 static AeadAlgorithm
parseAlg(const std::string & arg)58 parseAlg(const std::string &arg)
59 {
60 if (arg.find("chacha") != std::string::npos) {
61 return AeadAlgorithm::CHACHA20_POLY_1305;
62 }
63 else if (arg.find("tiaoxin") != std::string::npos) {
64 return AeadAlgorithm::TIAOXIN_346;
65 }
66 else if (arg.find("256") != std::string::npos) {
67 return AeadAlgorithm::AES_GCM_256;
68 }
69
70 return AeadAlgorithm::AES_GCM_128;
71 }
72
main(int argc,char ** argv)73 int main(int argc, char **argv)
74 {
75 AeadAlgorithm alg = AeadAlgorithm::AES_GCM_128;
76 char opt;
77 unsigned block_size = 4096;
78 char *err_str;
79 std::unique_ptr<SessionKey> psk;
80 bool encode = false;
81 bool decrypt = false;
82 bool random_mode = false;
83 bool password = false;
84 bool legacy_pbkdf = false;
85 unsigned count = 0;
86 unsigned nthreads = 0;
87
88 while ((opt = ::getopt(argc, argv, "ha:b:plk:Bdn:rc:")) != -1) {
89 switch (opt) {
90 case 'h':
91 usage(argv);
92 break;
93 case 'a':
94 alg = parseAlg(optarg);
95 break;
96 case 'b':
97 block_size = ::strtoul(optarg, &err_str, 10);
98 if (err_str && *err_str != '\0') {
99 switch (*err_str) {
100 case 'K':
101 case 'k':
102 block_size *= 1024;
103 break;
104 case 'M':
105 case 'm':
106 block_size *= 1024 * 1024;
107 break;
108 default:
109 usage(argv);
110 break;
111 }
112 }
113 break;
114 case 'k':
115 {
116 if (psk) {
117 std::cerr << "Key/password has been already specified"
118 << std::endl;
119 exit(EXIT_FAILURE);
120 }
121 std::string key_base32(optarg);
122 auto decoded = util::base32DecodeKey(key_base32);
123 if (!decoded || decoded->size() != master_key_length) {
124 usage(argv);
125 }
126 psk = std::move(decoded);
127 break;
128 }
129 case 'B':
130 encode = true;
131 break;
132 case 'd':
133 decrypt = true;
134 break;
135 case 'n':
136 nthreads = strtoul(optarg, NULL, 10);
137 break;
138 case 'r':
139 random_mode = true;
140 break;
141 case 'c':
142 count = strtoul(optarg, &err_str, 10);
143 if (err_str && *err_str != '\0') {
144 switch (*err_str) {
145 case 'K':
146 case 'k':
147 count *= 1024;
148 break;
149 case 'M':
150 case 'm':
151 count *= 1024 * 1024;
152 break;
153 default:
154 usage(argv);
155 break;
156 }
157 }
158 break;
159 case 'p':
160 {
161 password = true;
162 if (psk) {
163 std::cerr << "Key/password has been already specified" <<
164 std::endl;
165 exit(EXIT_FAILURE);
166 }
167 auto passwd = util::readPassphrase();
168 if (!passwd || passwd->size() == 0) {
169 std::cerr << "Password is invalid" << std::endl;
170 exit(EXIT_FAILURE);
171 }
172 if (passwd->size() < 6) {
173 // XXX: validate it more precisely
174 std::cerr << "Password is too short " <<
175 "(6 characters at least are required)" << std::endl;
176 exit(EXIT_FAILURE);
177 }
178 std::swap(psk, passwd);
179 break;
180 }
181 case 'l':
182 legacy_pbkdf = true;
183 break;
184 }
185 }
186
187 argv += optind;
188 argc -= optind;
189
190 if (sodium_init() != 0) {
191 std::cerr << "Cannot init libsodium" << std::endl;
192 exit(EXIT_FAILURE);
193 }
194
195 if (!psk) {
196 if (decrypt) {
197 std::cerr << "Cannot decrypt without key" << std::endl;
198 exit(EXIT_FAILURE);
199 }
200 psk = util::genPSK(alg);
201
202 if (!psk) {
203 std::cerr << "Cannot open /dev/urandom" << std::endl;
204 exit(EXIT_FAILURE);
205 }
206
207 if (!random_mode) {
208 std::cerr << "Random key: " << util::base32EncodeKey(psk.get())
209 << std::endl;
210 }
211 // Just print key
212 if (argc == 1 && std::string(argv[0]).find("psk") != std::string::npos) {
213 exit(EXIT_SUCCESS);
214 }
215 }
216
217 try {
218 auto kdf = util::make_unique<HPEncKDF>(std::move(psk), nullptr, password,
219 legacy_pbkdf);
220
221 if (decrypt) {
222 auto decrypter = util::make_unique<HPEncDecrypt>(std::move(kdf),
223 std::string(""), std::string(""));
224 decrypter->decrypt(encode, count);
225 }
226 else {
227 auto encrypter = util::make_unique<HPEncEncrypt>(std::move(kdf),
228 std::string(""), std::string(""),
229 alg, block_size, nthreads, random_mode);
230 encrypter->encrypt(encode, count);
231 }
232 }
233 catch (std::exception &e) {
234 std::cerr << "Failure: " << e.what() << std::endl;
235 exit(EXIT_FAILURE);
236 }
237
238 return 0;
239 }
240