xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/crypto.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*-
2  * Copyright (c) 2009 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Alistair Crooks (agc@NetBSD.org)
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 /*
30  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
31  * All rights reserved.
32  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
33  * their moral rights under the UK Copyright Design and Patents Act 1988 to
34  * be recorded as the authors of this copyright work.
35  *
36  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
37  * use this file except in compliance with the License.
38  *
39  * You may obtain a copy of the License at
40  *     http://www.apache.org/licenses/LICENSE-2.0
41  *
42  * Unless required by applicable law or agreed to in writing, software
43  * distributed under the License is distributed on an "AS IS" BASIS,
44  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45  *
46  * See the License for the specific language governing permissions and
47  * limitations under the License.
48  */
49 #include "config.h"
50 
51 #ifdef HAVE_SYS_CDEFS_H
52 #include <sys/cdefs.h>
53 #endif
54 
55 #if defined(__NetBSD__)
56 __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
57 __RCSID("$NetBSD: crypto.c,v 1.28 2010/09/08 03:21:22 agc Exp $");
58 #endif
59 
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 
63 #ifdef HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66 
67 #include <string.h>
68 
69 #include "types.h"
70 #include "crypto.h"
71 #include "readerwriter.h"
72 #include "memory.h"
73 #include "netpgpdefs.h"
74 #include "signature.h"
75 
76 /**
77 \ingroup Core_MPI
78 \brief Decrypt and unencode MPI
79 \param buf Buffer in which to write decrypted unencoded MPI
80 \param buflen Length of buffer
81 \param encmpi
82 \param seckey
83 \return length of MPI
84 \note only RSA at present
85 */
86 int
87 __ops_decrypt_decode_mpi(uint8_t *buf,
88 				unsigned buflen,
89 				const BIGNUM *encmpi,
90 				const __ops_seckey_t *seckey)
91 {
92 	unsigned        mpisize;
93 	uint8_t		encmpibuf[NETPGP_BUFSIZ];
94 	uint8_t		mpibuf[NETPGP_BUFSIZ];
95 	int             i;
96 	int             n;
97 
98 	mpisize = (unsigned)BN_num_bytes(encmpi);
99 	/* MPI can't be more than 65,536 */
100 	if (mpisize > sizeof(encmpibuf)) {
101 		(void) fprintf(stderr, "mpisize too big %u\n", mpisize);
102 		return -1;
103 	}
104 	BN_bn2bin(encmpi, encmpibuf);
105 
106 	switch (seckey->pubkey.alg) {
107 	case OPS_PKA_RSA:
108 		if (__ops_get_debug_level(__FILE__)) {
109 			hexdump(stderr, "encrypted", encmpibuf, 16);
110 		}
111 		n = __ops_rsa_private_decrypt(mpibuf, encmpibuf,
112 					(unsigned)(BN_num_bits(encmpi) + 7) / 8,
113 					&seckey->key.rsa, &seckey->pubkey.key.rsa);
114 		if (n == -1) {
115 			(void) fprintf(stderr, "ops_rsa_private_decrypt failure\n");
116 			return -1;
117 		}
118 		if (__ops_get_debug_level(__FILE__)) {
119 			hexdump(stderr, "decrypted", mpibuf, 16);
120 		}
121 		if (n <= 0) {
122 			return -1;
123 		}
124 		/* Decode EME-PKCS1_V1_5 (RFC 2437). */
125 		if (mpibuf[0] != 0 || mpibuf[1] != 2) {
126 			return -1;
127 		}
128 		/* Skip the random bytes. */
129 		for (i = 2; i < n && mpibuf[i]; ++i) {
130 		}
131 		if (i == n || i < 10) {
132 			return -1;
133 		}
134 		/* Skip the zero */
135 		i += 1;
136 		/* this is the unencoded m buf */
137 		if ((unsigned) (n - i) <= buflen) {
138 			(void) memcpy(buf, mpibuf + i, (unsigned)(n - i)); /* XXX - Flexelint */
139 		}
140 		if (__ops_get_debug_level(__FILE__)) {
141 			hexdump(stderr, "decoded m", buf, (size_t)(n - i));
142 		}
143 		return n - i;
144 	case OPS_PKA_DSA:
145 	case OPS_PKA_ELGAMAL:
146 		(void) fprintf(stderr, "XXX - preliminary support for DSA/Elgamal\n");
147 		if (__ops_get_debug_level(__FILE__)) {
148 			hexdump(stderr, "encrypted", encmpibuf, 16);
149 		}
150 		n = __ops_elgamal_private_decrypt(mpibuf, encmpibuf,
151 					(unsigned)(BN_num_bits(encmpi) + 7) / 8,
152 					&seckey->key.elgamal, &seckey->pubkey.key.elgamal);
153 		if (n == -1) {
154 			(void) fprintf(stderr, "ops_elgamal_private_decrypt failure\n");
155 			return -1;
156 		}
157 		if (__ops_get_debug_level(__FILE__)) {
158 			hexdump(stderr, "decrypted", mpibuf, 16);
159 		}
160 		if (n <= 0) {
161 			return -1;
162 		}
163 		/* Decode EME-PKCS1_V1_5 (RFC 2437). */
164 		if (mpibuf[0] != 0 || mpibuf[1] != 2) {
165 			return -1;
166 		}
167 		/* Skip the random bytes. */
168 		for (i = 2; i < n && mpibuf[i]; ++i) {
169 		}
170 		if (i == n || i < 10) {
171 			return -1;
172 		}
173 		/* Skip the zero */
174 		i += 1;
175 		/* this is the unencoded m buf */
176 		if ((unsigned) (n - i) <= buflen) {
177 			(void) memcpy(buf, mpibuf + i, (unsigned)(n - i)); /* XXX - Flexelint */
178 		}
179 		if (__ops_get_debug_level(__FILE__)) {
180 			hexdump(stderr, "decoded m", buf, (size_t)(n - i));
181 		}
182 		return n - i;
183 	default:
184 		(void) fprintf(stderr, "pubkey algorithm wrong\n");
185 		return -1;
186 	}
187 }
188 
189 /**
190 \ingroup Core_MPI
191 \brief RSA-encrypt an MPI
192 */
193 unsigned
194 __ops_rsa_encrypt_mpi(const uint8_t *encoded_m_buf,
195 		    const size_t sz_encoded_m_buf,
196 		    const __ops_pubkey_t * pubkey,
197 		    __ops_pk_sesskey_params_t * skp)
198 {
199 
200 	uint8_t   encmpibuf[NETPGP_BUFSIZ];
201 	int             n;
202 
203 	if (sz_encoded_m_buf != (size_t)BN_num_bytes(pubkey->key.rsa.n)) {
204 		(void) fprintf(stderr, "sz_encoded_m_buf wrong\n");
205 		return 0;
206 	}
207 
208 	n = __ops_rsa_public_encrypt(encmpibuf, encoded_m_buf,
209 				sz_encoded_m_buf, &pubkey->key.rsa);
210 	if (n == -1) {
211 		(void) fprintf(stderr, "__ops_rsa_public_encrypt failure\n");
212 		return 0;
213 	}
214 
215 	if (n <= 0)
216 		return 0;
217 
218 	skp->rsa.encrypted_m = BN_bin2bn(encmpibuf, n, NULL);
219 
220 	if (__ops_get_debug_level(__FILE__)) {
221 		hexdump(stderr, "encrypted mpi", encmpibuf, 16);
222 	}
223 	return 1;
224 }
225 
226 static __ops_cb_ret_t
227 write_parsed_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
228 {
229 	const __ops_contents_t	*content = &pkt->u;
230 
231 	if (__ops_get_debug_level(__FILE__)) {
232 		printf("write_parsed_cb: ");
233 		__ops_print_packet(&cbinfo->printstate, pkt);
234 	}
235 	if (pkt->tag != OPS_PTAG_CT_UNARMOURED_TEXT && cbinfo->printstate.skipping) {
236 		puts("...end of skip");
237 		cbinfo->printstate.skipping = 0;
238 	}
239 	switch (pkt->tag) {
240 	case OPS_PTAG_CT_UNARMOURED_TEXT:
241 		printf("OPS_PTAG_CT_UNARMOURED_TEXT\n");
242 		if (!cbinfo->printstate.skipping) {
243 			puts("Skipping...");
244 			cbinfo->printstate.skipping = 1;
245 		}
246 		fwrite(content->unarmoured_text.data, 1,
247 		       content->unarmoured_text.length, stdout);
248 		break;
249 
250 	case OPS_PTAG_CT_PK_SESSION_KEY:
251 		return __ops_pk_sesskey_cb(pkt, cbinfo);
252 
253 	case OPS_GET_SECKEY:
254 		if (cbinfo->sshseckey) {
255 			*content->get_seckey.seckey = cbinfo->sshseckey;
256 			return OPS_KEEP_MEMORY;
257 		}
258 		return __ops_get_seckey_cb(pkt, cbinfo);
259 
260 	case OPS_GET_PASSPHRASE:
261 		return cbinfo->cryptinfo.getpassphrase(pkt, cbinfo);
262 
263 	case OPS_PTAG_CT_LITDATA_BODY:
264 		return __ops_litdata_cb(pkt, cbinfo);
265 
266 	case OPS_PTAG_CT_ARMOUR_HEADER:
267 	case OPS_PTAG_CT_ARMOUR_TRAILER:
268 	case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
269 	case OPS_PTAG_CT_COMPRESSED:
270 	case OPS_PTAG_CT_LITDATA_HEADER:
271 	case OPS_PTAG_CT_SE_IP_DATA_BODY:
272 	case OPS_PTAG_CT_SE_IP_DATA_HEADER:
273 	case OPS_PTAG_CT_SE_DATA_BODY:
274 	case OPS_PTAG_CT_SE_DATA_HEADER:
275 		/* Ignore these packets  */
276 		/* They're handled in __ops_parse_packet() */
277 		/* and nothing else needs to be done */
278 		break;
279 
280 	default:
281 		if (__ops_get_debug_level(__FILE__)) {
282 			fprintf(stderr, "Unexpected packet tag=%d (0x%x)\n",
283 				pkt->tag,
284 				pkt->tag);
285 		}
286 		break;
287 	}
288 
289 	return OPS_RELEASE_MEMORY;
290 }
291 
292 /**
293 \ingroup HighLevel_Crypto
294 Encrypt a file
295 \param infile Name of file to be encrypted
296 \param outfile Name of file to write to. If NULL, name is constructed from infile
297 \param pubkey Public Key to encrypt file for
298 \param use_armour Write armoured text, if set
299 \param allow_overwrite Allow output file to be overwrwritten if it exists
300 \return 1 if OK; else 0
301 */
302 unsigned
303 __ops_encrypt_file(__ops_io_t *io,
304 			const char *infile,
305 			const char *outfile,
306 			const __ops_key_t *key,
307 			const unsigned use_armour,
308 			const unsigned allow_overwrite)
309 {
310 	__ops_output_t	*output;
311 	__ops_memory_t	*inmem;
312 	int		 fd_out;
313 
314 	__OPS_USED(io);
315 	inmem = __ops_memory_new();
316 	if (!__ops_mem_readfile(inmem, infile)) {
317 		return 0;
318 	}
319 	fd_out = __ops_setup_file_write(&output, outfile, allow_overwrite);
320 	if (fd_out < 0) {
321 		__ops_memory_free(inmem);
322 		return 0;
323 	}
324 
325 	/* set armoured/not armoured here */
326 	if (use_armour) {
327 		__ops_writer_push_armor_msg(output);
328 	}
329 
330 	/* Push the encrypted writer */
331 	if (!__ops_push_enc_se_ip(output, key)) {
332 		__ops_memory_free(inmem);
333 		return 0;
334 	}
335 
336 	/* This does the writing */
337 	__ops_write(output, __ops_mem_data(inmem), (unsigned)__ops_mem_len(inmem));
338 
339 	/* tidy up */
340 	__ops_memory_free(inmem);
341 	__ops_teardown_file_write(output, fd_out);
342 
343 	return 1;
344 }
345 
346 /* encrypt the contents of the input buffer, and return the mem structure */
347 __ops_memory_t *
348 __ops_encrypt_buf(__ops_io_t *io,
349 			const void *input,
350 			const size_t insize,
351 			const __ops_key_t *pubkey,
352 			const unsigned use_armour)
353 {
354 	__ops_output_t	*output;
355 	__ops_memory_t	*outmem;
356 
357 	__OPS_USED(io);
358 	if (input == NULL) {
359 		(void) fprintf(io->errs,
360 			"__ops_encrypt_buf: null memory\n");
361 		return 0;
362 	}
363 
364 	__ops_setup_memory_write(&output, &outmem, insize);
365 
366 	/* set armoured/not armoured here */
367 	if (use_armour) {
368 		__ops_writer_push_armor_msg(output);
369 	}
370 
371 	/* Push the encrypted writer */
372 	__ops_push_enc_se_ip(output, pubkey);
373 
374 	/* This does the writing */
375 	__ops_write(output, input, (unsigned)insize);
376 
377 	/* tidy up */
378 	__ops_writer_close(output);
379 	__ops_output_delete(output);
380 
381 	return outmem;
382 }
383 
384 /**
385    \ingroup HighLevel_Crypto
386    \brief Decrypt a file.
387    \param infile Name of file to be decrypted
388    \param outfile Name of file to write to. If NULL, the filename is constructed from the input filename, following GPG conventions.
389    \param keyring Keyring to use
390    \param use_armour Expect armoured text, if set
391    \param allow_overwrite Allow output file to overwritten, if set.
392    \param getpassfunc Callback to use to get passphrase
393 */
394 
395 unsigned
396 __ops_decrypt_file(__ops_io_t *io,
397 			const char *infile,
398 			const char *outfile,
399 			__ops_keyring_t *secring,
400 			__ops_keyring_t *pubring,
401 			const unsigned use_armour,
402 			const unsigned allow_overwrite,
403 			const unsigned sshkeys,
404 			void *passfp,
405 			__ops_cbfunc_t *getpassfunc)
406 {
407 	__ops_stream_t	*parse = NULL;
408 	const int		 printerrors = 1;
409 	char			*filename = NULL;
410 	int			 fd_in;
411 	int			 fd_out;
412 
413 	/* setup for reading from given input file */
414 	fd_in = __ops_setup_file_read(io, &parse, infile,
415 				    NULL,
416 				    write_parsed_cb,
417 				    0);
418 	if (fd_in < 0) {
419 		perror(infile);
420 		return 0;
421 	}
422 	/* setup output filename */
423 	if (outfile) {
424 		fd_out = __ops_setup_file_write(&parse->cbinfo.output, outfile,
425 				allow_overwrite);
426 		if (fd_out < 0) {
427 			perror(outfile);
428 			__ops_teardown_file_read(parse, fd_in);
429 			return 0;
430 		}
431 	} else {
432 		const int	suffixlen = 4;
433 		const char     *suffix = infile + strlen(infile) - suffixlen;
434 		unsigned	filenamelen;
435 
436 		if (strcmp(suffix, ".gpg") == 0 ||
437 		    strcmp(suffix, ".asc") == 0) {
438 			filenamelen = (unsigned)(strlen(infile) - strlen(suffix));
439 			if ((filename = calloc(1, filenamelen + 1)) == NULL) {
440 				(void) fprintf(stderr, "can't allocate %" PRIsize "d bytes\n",
441 					(size_t)(filenamelen + 1));
442 				return 0;
443 			}
444 			(void) strncpy(filename, infile, filenamelen);
445 			filename[filenamelen] = 0x0;
446 		}
447 
448 		fd_out = __ops_setup_file_write(&parse->cbinfo.output,
449 					filename, allow_overwrite);
450 		if (fd_out < 0) {
451 			perror(filename);
452 			free(filename);
453 			__ops_teardown_file_read(parse, fd_in);
454 			return 0;
455 		}
456 	}
457 
458 	/* \todo check for suffix matching armour param */
459 
460 	/* setup for writing decrypted contents to given output file */
461 
462 	/* setup keyring and passphrase callback */
463 	parse->cbinfo.cryptinfo.secring = secring;
464 	parse->cbinfo.passfp = passfp;
465 	parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
466 	parse->cbinfo.cryptinfo.pubring = pubring;
467 	parse->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL;
468 
469 	/* Set up armour/passphrase options */
470 	if (use_armour) {
471 		__ops_reader_push_dearmour(parse);
472 	}
473 
474 	/* Do it */
475 	__ops_parse(parse, printerrors);
476 
477 	/* Unsetup */
478 	if (use_armour) {
479 		__ops_reader_pop_dearmour(parse);
480 	}
481 
482 	if (filename) {
483 		__ops_teardown_file_write(parse->cbinfo.output, fd_out);
484 		free(filename);
485 	}
486 	__ops_teardown_file_read(parse, fd_in);
487 	/* \todo cleardown crypt */
488 
489 	return 1;
490 }
491 
492 /* decrypt an area of memory */
493 __ops_memory_t *
494 __ops_decrypt_buf(__ops_io_t *io,
495 			const void *input,
496 			const size_t insize,
497 			__ops_keyring_t *secring,
498 			__ops_keyring_t *pubring,
499 			const unsigned use_armour,
500 			const unsigned sshkeys,
501 			void *passfp,
502 			__ops_cbfunc_t *getpassfunc)
503 {
504 	__ops_stream_t	*parse = NULL;
505 	__ops_memory_t	*outmem;
506 	__ops_memory_t	*inmem;
507 	const int	 printerrors = 1;
508 
509 	if (input == NULL) {
510 		(void) fprintf(io->errs,
511 			"__ops_encrypt_buf: null memory\n");
512 		return 0;
513 	}
514 
515 	inmem = __ops_memory_new();
516 	__ops_memory_add(inmem, input, insize);
517 
518 	/* set up to read from memory */
519 	__ops_setup_memory_read(io, &parse, inmem,
520 				    NULL,
521 				    write_parsed_cb,
522 				    0);
523 
524 	/* setup for writing decrypted contents to given output file */
525 	__ops_setup_memory_write(&parse->cbinfo.output, &outmem, insize);
526 
527 	/* setup keyring and passphrase callback */
528 	parse->cbinfo.cryptinfo.secring = secring;
529 	parse->cbinfo.cryptinfo.pubring = pubring;
530 	parse->cbinfo.passfp = passfp;
531 	parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
532 	parse->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL;
533 
534 	/* Set up armour/passphrase options */
535 	if (use_armour) {
536 		__ops_reader_push_dearmour(parse);
537 	}
538 
539 	/* Do it */
540 	__ops_parse(parse, printerrors);
541 
542 	/* Unsetup */
543 	if (use_armour) {
544 		__ops_reader_pop_dearmour(parse);
545 	}
546 
547 	/* tidy up */
548 	__ops_teardown_memory_read(parse, inmem);
549 	__ops_memory_release(inmem);
550 	free(inmem);
551 
552 	__ops_writer_close(parse->cbinfo.output);
553 	__ops_output_delete(parse->cbinfo.output);
554 
555 	return outmem;
556 }
557 
558