xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/crypto.c (revision 179b12252ecaf3553d9c2b7458ce62b6a2203d0c)
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.27 2010/08/15 07:52:26 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 - no support for DSA/Elgamal yet\n");
147 		return 0;
148 	default:
149 		(void) fprintf(stderr, "pubkey algorithm wrong\n");
150 		return -1;
151 	}
152 }
153 
154 /**
155 \ingroup Core_MPI
156 \brief RSA-encrypt an MPI
157 */
158 unsigned
159 __ops_rsa_encrypt_mpi(const uint8_t *encoded_m_buf,
160 		    const size_t sz_encoded_m_buf,
161 		    const __ops_pubkey_t * pubkey,
162 		    __ops_pk_sesskey_params_t * skp)
163 {
164 
165 	uint8_t   encmpibuf[NETPGP_BUFSIZ];
166 	int             n;
167 
168 	if (sz_encoded_m_buf != (size_t)BN_num_bytes(pubkey->key.rsa.n)) {
169 		(void) fprintf(stderr, "sz_encoded_m_buf wrong\n");
170 		return 0;
171 	}
172 
173 	n = __ops_rsa_public_encrypt(encmpibuf, encoded_m_buf,
174 				sz_encoded_m_buf, &pubkey->key.rsa);
175 	if (n == -1) {
176 		(void) fprintf(stderr, "__ops_rsa_public_encrypt failure\n");
177 		return 0;
178 	}
179 
180 	if (n <= 0)
181 		return 0;
182 
183 	skp->rsa.encrypted_m = BN_bin2bn(encmpibuf, n, NULL);
184 
185 	if (__ops_get_debug_level(__FILE__)) {
186 		hexdump(stderr, "encrypted mpi", encmpibuf, 16);
187 	}
188 	return 1;
189 }
190 
191 static __ops_cb_ret_t
192 write_parsed_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
193 {
194 	const __ops_contents_t	*content = &pkt->u;
195 
196 	if (__ops_get_debug_level(__FILE__)) {
197 		printf("write_parsed_cb: ");
198 		__ops_print_packet(&cbinfo->printstate, pkt);
199 	}
200 	if (pkt->tag != OPS_PTAG_CT_UNARMOURED_TEXT && cbinfo->printstate.skipping) {
201 		puts("...end of skip");
202 		cbinfo->printstate.skipping = 0;
203 	}
204 	switch (pkt->tag) {
205 	case OPS_PTAG_CT_UNARMOURED_TEXT:
206 		printf("OPS_PTAG_CT_UNARMOURED_TEXT\n");
207 		if (!cbinfo->printstate.skipping) {
208 			puts("Skipping...");
209 			cbinfo->printstate.skipping = 1;
210 		}
211 		fwrite(content->unarmoured_text.data, 1,
212 		       content->unarmoured_text.length, stdout);
213 		break;
214 
215 	case OPS_PTAG_CT_PK_SESSION_KEY:
216 		return __ops_pk_sesskey_cb(pkt, cbinfo);
217 
218 	case OPS_GET_SECKEY:
219 		return __ops_get_seckey_cb(pkt, cbinfo);
220 
221 	case OPS_GET_PASSPHRASE:
222 		return cbinfo->cryptinfo.getpassphrase(pkt, cbinfo);
223 
224 	case OPS_PTAG_CT_LITDATA_BODY:
225 		return __ops_litdata_cb(pkt, cbinfo);
226 
227 	case OPS_PTAG_CT_ARMOUR_HEADER:
228 	case OPS_PTAG_CT_ARMOUR_TRAILER:
229 	case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
230 	case OPS_PTAG_CT_COMPRESSED:
231 	case OPS_PTAG_CT_LITDATA_HEADER:
232 	case OPS_PTAG_CT_SE_IP_DATA_BODY:
233 	case OPS_PTAG_CT_SE_IP_DATA_HEADER:
234 	case OPS_PTAG_CT_SE_DATA_BODY:
235 	case OPS_PTAG_CT_SE_DATA_HEADER:
236 		/* Ignore these packets  */
237 		/* They're handled in __ops_parse_packet() */
238 		/* and nothing else needs to be done */
239 		break;
240 
241 	default:
242 		if (__ops_get_debug_level(__FILE__)) {
243 			fprintf(stderr, "Unexpected packet tag=%d (0x%x)\n",
244 				pkt->tag,
245 				pkt->tag);
246 		}
247 		break;
248 	}
249 
250 	return OPS_RELEASE_MEMORY;
251 }
252 
253 /**
254 \ingroup HighLevel_Crypto
255 Encrypt a file
256 \param infile Name of file to be encrypted
257 \param outfile Name of file to write to. If NULL, name is constructed from infile
258 \param pubkey Public Key to encrypt file for
259 \param use_armour Write armoured text, if set
260 \param allow_overwrite Allow output file to be overwrwritten if it exists
261 \return 1 if OK; else 0
262 */
263 unsigned
264 __ops_encrypt_file(__ops_io_t *io,
265 			const char *infile,
266 			const char *outfile,
267 			const __ops_key_t *key,
268 			const unsigned use_armour,
269 			const unsigned allow_overwrite)
270 {
271 	__ops_output_t	*output;
272 	__ops_memory_t	*inmem;
273 	int		 fd_out;
274 
275 	__OPS_USED(io);
276 	inmem = __ops_memory_new();
277 	if (!__ops_mem_readfile(inmem, infile)) {
278 		return 0;
279 	}
280 	fd_out = __ops_setup_file_write(&output, outfile, allow_overwrite);
281 	if (fd_out < 0) {
282 		__ops_memory_free(inmem);
283 		return 0;
284 	}
285 
286 	/* set armoured/not armoured here */
287 	if (use_armour) {
288 		__ops_writer_push_armor_msg(output);
289 	}
290 
291 	/* Push the encrypted writer */
292 	if (!__ops_push_enc_se_ip(output, key)) {
293 		__ops_memory_free(inmem);
294 		return 0;
295 	}
296 
297 	/* This does the writing */
298 	__ops_write(output, __ops_mem_data(inmem), (unsigned)__ops_mem_len(inmem));
299 
300 	/* tidy up */
301 	__ops_memory_free(inmem);
302 	__ops_teardown_file_write(output, fd_out);
303 
304 	return 1;
305 }
306 
307 /* encrypt the contents of the input buffer, and return the mem structure */
308 __ops_memory_t *
309 __ops_encrypt_buf(__ops_io_t *io,
310 			const void *input,
311 			const size_t insize,
312 			const __ops_key_t *pubkey,
313 			const unsigned use_armour)
314 {
315 	__ops_output_t	*output;
316 	__ops_memory_t	*outmem;
317 
318 	__OPS_USED(io);
319 	if (input == NULL) {
320 		(void) fprintf(io->errs,
321 			"__ops_encrypt_buf: null memory\n");
322 		return 0;
323 	}
324 
325 	__ops_setup_memory_write(&output, &outmem, insize);
326 
327 	/* set armoured/not armoured here */
328 	if (use_armour) {
329 		__ops_writer_push_armor_msg(output);
330 	}
331 
332 	/* Push the encrypted writer */
333 	__ops_push_enc_se_ip(output, pubkey);
334 
335 	/* This does the writing */
336 	__ops_write(output, input, (unsigned)insize);
337 
338 	/* tidy up */
339 	__ops_writer_close(output);
340 	__ops_output_delete(output);
341 
342 	return outmem;
343 }
344 
345 /**
346    \ingroup HighLevel_Crypto
347    \brief Decrypt a file.
348    \param infile Name of file to be decrypted
349    \param outfile Name of file to write to. If NULL, the filename is constructed from the input filename, following GPG conventions.
350    \param keyring Keyring to use
351    \param use_armour Expect armoured text, if set
352    \param allow_overwrite Allow output file to overwritten, if set.
353    \param getpassfunc Callback to use to get passphrase
354 */
355 
356 unsigned
357 __ops_decrypt_file(__ops_io_t *io,
358 			const char *infile,
359 			const char *outfile,
360 			__ops_keyring_t *secring,
361 			__ops_keyring_t *pubring,
362 			const unsigned use_armour,
363 			const unsigned allow_overwrite,
364 			void *passfp,
365 			__ops_cbfunc_t *getpassfunc)
366 {
367 	__ops_stream_t	*parse = NULL;
368 	const int		 printerrors = 1;
369 	char			*filename = NULL;
370 	int			 fd_in;
371 	int			 fd_out;
372 
373 	/* setup for reading from given input file */
374 	fd_in = __ops_setup_file_read(io, &parse, infile,
375 				    NULL,
376 				    write_parsed_cb,
377 				    0);
378 	if (fd_in < 0) {
379 		perror(infile);
380 		return 0;
381 	}
382 	/* setup output filename */
383 	if (outfile) {
384 		fd_out = __ops_setup_file_write(&parse->cbinfo.output, outfile,
385 				allow_overwrite);
386 		if (fd_out < 0) {
387 			perror(outfile);
388 			__ops_teardown_file_read(parse, fd_in);
389 			return 0;
390 		}
391 	} else {
392 		const int	suffixlen = 4;
393 		const char     *suffix = infile + strlen(infile) - suffixlen;
394 		unsigned	filenamelen;
395 
396 		if (strcmp(suffix, ".gpg") == 0 ||
397 		    strcmp(suffix, ".asc") == 0) {
398 			filenamelen = (unsigned)(strlen(infile) - strlen(suffix));
399 			if ((filename = calloc(1, filenamelen + 1)) == NULL) {
400 				(void) fprintf(stderr, "can't allocate %" PRIsize "d bytes\n",
401 					(size_t)(filenamelen + 1));
402 				return 0;
403 			}
404 			(void) strncpy(filename, infile, filenamelen);
405 			filename[filenamelen] = 0x0;
406 		}
407 
408 		fd_out = __ops_setup_file_write(&parse->cbinfo.output,
409 					filename, allow_overwrite);
410 		if (fd_out < 0) {
411 			perror(filename);
412 			free(filename);
413 			__ops_teardown_file_read(parse, fd_in);
414 			return 0;
415 		}
416 	}
417 
418 	/* \todo check for suffix matching armour param */
419 
420 	/* setup for writing decrypted contents to given output file */
421 
422 	/* setup keyring and passphrase callback */
423 	parse->cbinfo.cryptinfo.secring = secring;
424 	parse->cbinfo.passfp = passfp;
425 	parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
426 	parse->cbinfo.cryptinfo.pubring = pubring;
427 
428 	/* Set up armour/passphrase options */
429 	if (use_armour) {
430 		__ops_reader_push_dearmour(parse);
431 	}
432 
433 	/* Do it */
434 	__ops_parse(parse, printerrors);
435 
436 	/* Unsetup */
437 	if (use_armour) {
438 		__ops_reader_pop_dearmour(parse);
439 	}
440 
441 	if (filename) {
442 		__ops_teardown_file_write(parse->cbinfo.output, fd_out);
443 		free(filename);
444 	}
445 	__ops_teardown_file_read(parse, fd_in);
446 	/* \todo cleardown crypt */
447 
448 	return 1;
449 }
450 
451 /* decrypt an area of memory */
452 __ops_memory_t *
453 __ops_decrypt_buf(__ops_io_t *io,
454 			const void *input,
455 			const size_t insize,
456 			__ops_keyring_t *secring,
457 			__ops_keyring_t *pubring,
458 			const unsigned use_armour,
459 			void *passfp,
460 			__ops_cbfunc_t *getpassfunc)
461 {
462 	__ops_stream_t	*parse = NULL;
463 	__ops_memory_t	*outmem;
464 	__ops_memory_t	*inmem;
465 	const int	 printerrors = 1;
466 
467 	if (input == NULL) {
468 		(void) fprintf(io->errs,
469 			"__ops_encrypt_buf: null memory\n");
470 		return 0;
471 	}
472 
473 	inmem = __ops_memory_new();
474 	__ops_memory_add(inmem, input, insize);
475 
476 	/* set up to read from memory */
477 	__ops_setup_memory_read(io, &parse, inmem,
478 				    NULL,
479 				    write_parsed_cb,
480 				    0);
481 
482 	/* setup for writing decrypted contents to given output file */
483 	__ops_setup_memory_write(&parse->cbinfo.output, &outmem, insize);
484 
485 	/* setup keyring and passphrase callback */
486 	parse->cbinfo.cryptinfo.secring = secring;
487 	parse->cbinfo.cryptinfo.pubring = pubring;
488 	parse->cbinfo.passfp = passfp;
489 	parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
490 
491 	/* Set up armour/passphrase options */
492 	if (use_armour) {
493 		__ops_reader_push_dearmour(parse);
494 	}
495 
496 	/* Do it */
497 	__ops_parse(parse, printerrors);
498 
499 	/* Unsetup */
500 	if (use_armour) {
501 		__ops_reader_pop_dearmour(parse);
502 	}
503 
504 	/* tidy up */
505 	__ops_teardown_memory_read(parse, inmem);
506 	__ops_memory_release(inmem);
507 	free(inmem);
508 
509 	__ops_writer_close(parse->cbinfo.output);
510 	__ops_output_delete(parse->cbinfo.output);
511 
512 	return outmem;
513 }
514 
515