xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/crypto.c (revision 93bf6008f8b7982c1d1a9486e4a4a0e687fe36eb)
1 /*
2  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
3  * All rights reserved.
4  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
5  * their moral rights under the UK Copyright Design and Patents Act 1988 to
6  * be recorded as the authors of this copyright work.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9  * use this file except in compliance with the License.
10  *
11  * You may obtain a copy of the License at
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 #include "config.h"
22 
23 #include "crypto.h"
24 
25 #include "readerwriter.h"
26 #include "memory.h"
27 #include "parse_local.h"
28 #include "netpgpdefs.h"
29 
30 #ifdef HAVE_ASSERT_H
31 #include <assert.h>
32 #endif
33 
34 #include <string.h>
35 #include <fcntl.h>
36 
37 /**
38 \ingroup Core_MPI
39 \brief Decrypt and unencode MPI
40 \param buf Buffer in which to write decrypted unencoded MPI
41 \param buflen Length of buffer
42 \param encmpi
43 \param skey
44 \return length of MPI
45 \note only RSA at present
46 */
47 int
48 __ops_decrypt_and_unencode_mpi(unsigned char *buf, unsigned buflen, const BIGNUM * encmpi,
49 			     const __ops_secret_key_t * skey)
50 {
51 	unsigned char   encmpibuf[NETPGP_BUFSIZ];
52 	unsigned char   mpibuf[NETPGP_BUFSIZ];
53 	unsigned        mpisize;
54 	int             n;
55 	int             i;
56 
57 	mpisize = BN_num_bytes(encmpi);
58 	/* MPI can't be more than 65,536 */
59 	assert(mpisize <= sizeof(encmpibuf));
60 	BN_bn2bin(encmpi, encmpibuf);
61 
62 	assert(skey->public_key.algorithm == OPS_PKA_RSA);
63 
64 	if (__ops_get_debug_level(__FILE__)) {
65 		fprintf(stderr, "\nDECRYPTING\n");
66 		fprintf(stderr, "encrypted data     : ");
67 		for (i = 0; i < 16; i++) {
68 			fprintf(stderr, "%2x ", encmpibuf[i]);
69 		}
70 		fprintf(stderr, "\n");
71 	}
72 	n = __ops_rsa_private_decrypt(mpibuf, encmpibuf, (BN_num_bits(encmpi) + 7) / 8,
73 				 &skey->key.rsa, &skey->public_key.key.rsa);
74 	assert(n != -1);
75 
76 	if (__ops_get_debug_level(__FILE__)) {
77 		fprintf(stderr, "decrypted encoded m buf     : ");
78 		for (i = 0; i < 16; i++)
79 			fprintf(stderr, "%2x ", mpibuf[i]);
80 		fprintf(stderr, "\n");
81 	}
82 	if (n <= 0)
83 		return -1;
84 
85 	if (__ops_get_debug_level(__FILE__)) {
86 		printf(" decrypted=%d ", n);
87 		hexdump(mpibuf, n, "");
88 		printf("\n");
89 	}
90 	/* Decode EME-PKCS1_V1_5 (RFC 2437). */
91 
92 	if (mpibuf[0] != 0 || mpibuf[1] != 2)
93 		return false;
94 
95 	/* Skip the random bytes. */
96 	for (i = 2; i < n && mpibuf[i]; ++i);
97 
98 	if (i == n || i < 10)
99 		return false;
100 
101 	/* Skip the zero */
102 	++i;
103 
104 	/* this is the unencoded m buf */
105 	if ((unsigned) (n - i) <= buflen)
106 		(void) memcpy(buf, mpibuf + i, n - i);
107 
108 	if (__ops_get_debug_level(__FILE__)) {
109 		int             j;
110 
111 		printf("decoded m buf:\n");
112 		for (j = 0; j < n - i; j++)
113 			printf("%2x ", buf[j]);
114 		printf("\n");
115 	}
116 	return n - i;
117 }
118 
119 /**
120 \ingroup Core_MPI
121 \brief RSA-encrypt an MPI
122 */
123 bool
124 __ops_rsa_encrypt_mpi(const unsigned char *encoded_m_buf,
125 		    const size_t sz_encoded_m_buf,
126 		    const __ops_public_key_t * pkey,
127 		    __ops_pk_session_key_parameters_t * skp)
128 {
129 
130 	unsigned char   encmpibuf[NETPGP_BUFSIZ];
131 	int             n = 0;
132 
133 	assert(sz_encoded_m_buf == (size_t) BN_num_bytes(pkey->key.rsa.n));
134 
135 	n = __ops_rsa_public_encrypt(encmpibuf, encoded_m_buf, sz_encoded_m_buf, &pkey->key.rsa);
136 	assert(n != -1);
137 
138 	if (n <= 0)
139 		return false;
140 
141 	skp->rsa.encrypted_m = BN_bin2bn(encmpibuf, n, NULL);
142 
143 	if (__ops_get_debug_level(__FILE__)) {
144 		int             i;
145 		fprintf(stderr, "encrypted mpi buf     : ");
146 		for (i = 0; i < 16; i++) {
147 			fprintf(stderr, "%2x ", encmpibuf[i]);
148 		}
149 		fprintf(stderr, "\n");
150 	}
151 	return true;
152 }
153 
154 static          __ops_parse_cb_return_t
155 callback_write_parsed(const __ops_parser_content_t * contents, __ops_parse_cb_info_t * cbinfo);
156 
157 /**
158 \ingroup HighLevel_Crypto
159 Encrypt a file
160 \param input_filename Name of file to be encrypted
161 \param output_filename Name of file to write to. If NULL, name is constructed from input_filename
162 \param pub_key Public Key to encrypt file for
163 \param use_armour Write armoured text, if set
164 \param allow_overwrite Allow output file to be overwrwritten if it exists
165 \return true if OK; else false
166 */
167 bool
168 __ops_encrypt_file(const char *input_filename, const char *output_filename, const __ops_keydata_t * pub_key, const bool use_armour, const bool allow_overwrite)
169 {
170 	int             fd_in = 0;
171 	int             fd_out = 0;
172 
173 	__ops_create_info_t *create;
174 
175 	unsigned char  *buf;
176 	size_t          bufsz;
177 	int             done;
178 
179 #ifdef O_BINARY
180 	fd_in = open(input_filename, O_RDONLY | O_BINARY);
181 #else
182 	fd_in = open(input_filename, O_RDONLY);
183 #endif
184 	if (fd_in < 0) {
185 		perror(input_filename);
186 		return false;
187 	}
188 	fd_out = __ops_setup_file_write(&create, output_filename, allow_overwrite);
189 	if (fd_out < 0)
190 		return false;
191 
192 	/* set armoured/not armoured here */
193 	if (use_armour)
194 		__ops_writer_push_armoured_message(create);
195 
196 	/* Push the encrypted writer */
197 	__ops_writer_push_encrypt_se_ip(create, pub_key);
198 
199 	/* Do the writing */
200 
201 	buf = NULL;
202 	bufsz = 16;
203 	done = 0;
204 	for (;;) {
205 		int             n = 0;
206 
207 		buf = realloc(buf, done + bufsz);
208 
209 		n = read(fd_in, buf + done, bufsz);
210 		if (!n)
211 			break;
212 		assert(n >= 0);
213 		done += n;
214 	}
215 
216 	/* This does the writing */
217 	__ops_write(buf, done, create);
218 
219 	/* tidy up */
220 	close(fd_in);
221 	free(buf);
222 	__ops_teardown_file_write(create, fd_out);
223 
224 	return true;
225 }
226 
227 /**
228    \ingroup HighLevel_Crypto
229    \brief Decrypt a file.
230    \param input_filename Name of file to be decrypted
231    \param output_filename Name of file to write to. If NULL, the filename is constructed from the input filename, following GPG conventions.
232    \param keyring Keyring to use
233    \param use_armour Expect armoured text, if set
234    \param allow_overwrite Allow output file to overwritten, if set.
235    \param cb_get_passphrase Callback to use to get passphrase
236 */
237 
238 bool
239 __ops_decrypt_file(const char *input_filename, const char *output_filename, __ops_keyring_t * keyring, const bool use_armour, const bool allow_overwrite, __ops_parse_cb_t * cb_get_passphrase)
240 {
241 	int             fd_in = 0;
242 	int             fd_out = 0;
243 	char           *myfilename = NULL;
244 	__ops_parse_info_t *parse = NULL;
245 
246 	/* setup for reading from given input file */
247 	fd_in = __ops_setup_file_read(&parse, input_filename,
248 				    NULL,
249 				    callback_write_parsed,
250 				    false);
251 	if (fd_in < 0) {
252 		perror(input_filename);
253 		return false;
254 	}
255 	/* setup output filename */
256 
257 	if (output_filename) {
258 		fd_out = __ops_setup_file_write(&parse->cbinfo.cinfo, output_filename, allow_overwrite);
259 
260 		if (fd_out < 0) {
261 			perror(output_filename);
262 			__ops_teardown_file_read(parse, fd_in);
263 			return false;
264 		}
265 	} else {
266 		int             suffixlen = 4;
267 		const char     *defaultsuffix = ".decrypted";
268 		const char     *suffix = input_filename + strlen(input_filename) - suffixlen;
269 		if (strcmp(suffix, ".gpg") == 0 ||
270 		    strcmp(suffix, ".asc") == 0) {
271 			myfilename = calloc(1, strlen(input_filename) - suffixlen + 1);
272 			strncpy(myfilename, input_filename, strlen(input_filename) - suffixlen);
273 		} else {
274 			unsigned        filenamelen = strlen(input_filename) + strlen(defaultsuffix) + 1;
275 
276 			myfilename = calloc(1, filenamelen);
277 			snprintf(myfilename, filenamelen, "%s%s", input_filename, defaultsuffix);
278 		}
279 
280 		fd_out = __ops_setup_file_write(&parse->cbinfo.cinfo, myfilename, allow_overwrite);
281 
282 		if (fd_out < 0) {
283 			perror(myfilename);
284 			free(myfilename);
285 			__ops_teardown_file_read(parse, fd_in);
286 			return false;
287 		}
288 		free(myfilename);
289 	}
290 
291 	/* \todo check for suffix matching armour param */
292 
293 	/* setup for writing decrypted contents to given output file */
294 
295 	/* setup keyring and passphrase callback */
296 	parse->cbinfo.cryptinfo.keyring = keyring;
297 	parse->cbinfo.cryptinfo.cb_get_passphrase = cb_get_passphrase;
298 
299 	/* Set up armour/passphrase options */
300 
301 	if (use_armour)
302 		__ops_reader_push_dearmour(parse);
303 
304 	/* Do it */
305 
306 	__ops_parse_and_print_errors(parse);
307 
308 	/* Unsetup */
309 
310 	if (use_armour)
311 		__ops_reader_pop_dearmour(parse);
312 
313 	__ops_teardown_file_write(parse->cbinfo.cinfo, fd_out);
314 	__ops_teardown_file_read(parse, fd_in);
315 	/* \todo cleardown crypt */
316 
317 	return true;
318 }
319 
320 static          __ops_parse_cb_return_t
321 callback_write_parsed(const __ops_parser_content_t *contents, __ops_parse_cb_info_t * cbinfo)
322 {
323 	const __ops_parser_content_union_t *content = &contents->u;
324 	static bool skipping;
325 
326 	OPS_USED(cbinfo);
327 
328 	if (__ops_get_debug_level(__FILE__)) {
329 		printf("callback_write_parsed: ");
330 		__ops_print_packet(contents);
331 	}
332 	if (contents->tag != OPS_PTAG_CT_UNARMOURED_TEXT && skipping) {
333 		puts("...end of skip");
334 		skipping = false;
335 	}
336 	switch (contents->tag) {
337 	case OPS_PTAG_CT_UNARMOURED_TEXT:
338 		printf("OPS_PTAG_CT_UNARMOURED_TEXT\n");
339 		if (!skipping) {
340 			puts("Skipping...");
341 			skipping = true;
342 		}
343 		fwrite(content->unarmoured_text.data, 1,
344 		       content->unarmoured_text.length, stdout);
345 		break;
346 
347 	case OPS_PTAG_CT_PK_SESSION_KEY:
348 		return pk_session_key_cb(contents, cbinfo);
349 
350 	case OPS_PARSER_CMD_GET_SECRET_KEY:
351 		return get_secret_key_cb(contents, cbinfo);
352 
353 	case OPS_PARSER_CMD_GET_SK_PASSPHRASE:
354 		/*
355 		 * return
356 		 * get_secret_key_cb(contents,cbinfo);
357 		 */
358 		return cbinfo->cryptinfo.cb_get_passphrase(contents, cbinfo);
359 
360 	case OPS_PTAG_CT_LITERAL_DATA_BODY:
361 		return literal_data_cb(contents, cbinfo);
362 
363 	case OPS_PTAG_CT_ARMOUR_HEADER:
364 	case OPS_PTAG_CT_ARMOUR_TRAILER:
365 	case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
366 	case OPS_PTAG_CT_COMPRESSED:
367 	case OPS_PTAG_CT_LITERAL_DATA_HEADER:
368 	case OPS_PTAG_CT_SE_IP_DATA_BODY:
369 	case OPS_PTAG_CT_SE_IP_DATA_HEADER:
370 	case OPS_PTAG_CT_SE_DATA_BODY:
371 	case OPS_PTAG_CT_SE_DATA_HEADER:
372 
373 		/* Ignore these packets  */
374 		/* They're handled in __ops_parse_packet() */
375 		/* and nothing else needs to be done */
376 		break;
377 
378 	default:
379 		/* return callback_general(contents,cbinfo); */
380 		if (__ops_get_debug_level(__FILE__)) {
381 			fprintf(stderr, "Unexpected packet tag=%d (0x%x)\n",
382 				contents->tag,
383 				contents->tag);
384 		}
385 		break;
386 	}
387 
388 	return OPS_RELEASE_MEMORY;
389 }
390