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