xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/crypto.c (revision da9817918ec7e88db2912a2882967c7570a83f47)
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.16 2009/06/09 00:51:02 agc Exp $");
58 #endif
59 
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 
63 #ifdef HAVE_FCNTL_H
64 #include <fcntl.h>
65 #endif
66 
67 #ifdef HAVE_UNISTD_H
68 #include <unistd.h>
69 #endif
70 
71 #include <string.h>
72 
73 #include "types.h"
74 #include "crypto.h"
75 #include "readerwriter.h"
76 #include "memory.h"
77 #include "netpgpdefs.h"
78 #include "signature.h"
79 
80 /**
81 \ingroup Core_MPI
82 \brief Decrypt and unencode MPI
83 \param buf Buffer in which to write decrypted unencoded MPI
84 \param buflen Length of buffer
85 \param encmpi
86 \param seckey
87 \return length of MPI
88 \note only RSA at present
89 */
90 int
91 __ops_decrypt_decode_mpi(unsigned char *buf,
92 				unsigned buflen,
93 				const BIGNUM *encmpi,
94 				const __ops_seckey_t *seckey)
95 {
96 	unsigned char   encmpibuf[NETPGP_BUFSIZ];
97 	unsigned char   mpibuf[NETPGP_BUFSIZ];
98 	unsigned        mpisize;
99 	int             n;
100 	int             i;
101 
102 	mpisize = BN_num_bytes(encmpi);
103 	/* MPI can't be more than 65,536 */
104 	if (mpisize > sizeof(encmpibuf)) {
105 		(void) fprintf(stderr, "mpisize too big %u\n", mpisize);
106 		return -1;
107 	}
108 	BN_bn2bin(encmpi, encmpibuf);
109 
110 	if (seckey->pubkey.alg != OPS_PKA_RSA) {
111 		(void) fprintf(stderr, "pubkey algorithm wrong\n");
112 		return -1;
113 	}
114 
115 	if (__ops_get_debug_level(__FILE__)) {
116 		(void) fprintf(stderr, "\nDECRYPTING\n");
117 		(void) fprintf(stderr, "encrypted data     : ");
118 		for (i = 0; i < 16; i++) {
119 			(void) fprintf(stderr, "%2x ", encmpibuf[i]);
120 		}
121 		(void) fprintf(stderr, "\n");
122 	}
123 	n = __ops_rsa_private_decrypt(mpibuf, encmpibuf,
124 				(unsigned)(BN_num_bits(encmpi) + 7) / 8,
125 				&seckey->key.rsa, &seckey->pubkey.key.rsa);
126 	if (n == -1) {
127 		(void) fprintf(stderr, "ops_rsa_private_decrypt failure\n");
128 		return -1;
129 	}
130 
131 	if (__ops_get_debug_level(__FILE__)) {
132 		(void) fprintf(stderr, "decrypted encoded m buf     : ");
133 		for (i = 0; i < 16; i++) {
134 			(void) fprintf(stderr, "%2x ", mpibuf[i]);
135 		}
136 		(void) fprintf(stderr, "\n");
137 	}
138 	if (n <= 0) {
139 		return -1;
140 	}
141 
142 	if (__ops_get_debug_level(__FILE__)) {
143 		printf(" decrypted=%d ", n);
144 		hexdump(stdout, mpibuf, (unsigned)n, "");
145 		printf("\n");
146 	}
147 	/* Decode EME-PKCS1_V1_5 (RFC 2437). */
148 
149 	if (mpibuf[0] != 0 || mpibuf[1] != 2) {
150 		return -1;
151 	}
152 
153 	/* Skip the random bytes. */
154 	for (i = 2; i < n && mpibuf[i]; ++i) {
155 	}
156 
157 	if (i == n || i < 10) {
158 		return -1;
159 	}
160 
161 	/* Skip the zero */
162 	i += 1;
163 
164 	/* this is the unencoded m buf */
165 	if ((unsigned) (n - i) <= buflen) {
166 		(void) memcpy(buf, mpibuf + i, (unsigned)(n - i));
167 	}
168 
169 	if (__ops_get_debug_level(__FILE__)) {
170 		int             j;
171 
172 		printf("decoded m buf:\n");
173 		for (j = 0; j < n - i; j++)
174 			printf("%2x ", buf[j]);
175 		printf("\n");
176 	}
177 	return n - i;
178 }
179 
180 /**
181 \ingroup Core_MPI
182 \brief RSA-encrypt an MPI
183 */
184 unsigned
185 __ops_rsa_encrypt_mpi(const unsigned char *encoded_m_buf,
186 		    const size_t sz_encoded_m_buf,
187 		    const __ops_pubkey_t * pubkey,
188 		    __ops_pk_sesskey_params_t * skp)
189 {
190 
191 	unsigned char   encmpibuf[NETPGP_BUFSIZ];
192 	int             n = 0;
193 
194 	if (sz_encoded_m_buf != (size_t) BN_num_bytes(pubkey->key.rsa.n)) {
195 		(void) fprintf(stderr, "sz_encoded_m_buf wrong\n");
196 		return 0;
197 	}
198 
199 	n = __ops_rsa_public_encrypt(encmpibuf, encoded_m_buf,
200 				sz_encoded_m_buf, &pubkey->key.rsa);
201 	if (n == -1) {
202 		(void) fprintf(stderr, "__ops_rsa_public_encrypt failure\n");
203 		return 0;
204 	}
205 
206 	if (n <= 0)
207 		return 0;
208 
209 	skp->rsa.encrypted_m = BN_bin2bn(encmpibuf, n, NULL);
210 
211 	if (__ops_get_debug_level(__FILE__)) {
212 		int             i;
213 		(void) fprintf(stderr, "encrypted mpi buf     : ");
214 		for (i = 0; i < 16; i++) {
215 			(void) fprintf(stderr, "%2x ", encmpibuf[i]);
216 		}
217 		(void) fprintf(stderr, "\n");
218 	}
219 	return 1;
220 }
221 
222 static          __ops_cb_ret_t
223 callback_write_parsed(const __ops_packet_t *, __ops_cbdata_t *);
224 
225 /**
226 \ingroup HighLevel_Crypto
227 Encrypt a file
228 \param infile Name of file to be encrypted
229 \param outfile Name of file to write to. If NULL, name is constructed from infile
230 \param pubkey Public Key to encrypt file for
231 \param use_armour Write armoured text, if set
232 \param allow_overwrite Allow output file to be overwrwritten if it exists
233 \return 1 if OK; else 0
234 */
235 unsigned
236 __ops_encrypt_file(__ops_io_t *io,
237 			const char *infile,
238 			const char *outfile,
239 			const __ops_key_t *pubkey,
240 			const unsigned use_armour,
241 			const unsigned allow_overwrite)
242 {
243 	__ops_output_t	*output;
244 	__ops_memory_t	*inmem;
245 	int		 fd_out = 0;
246 
247 	__OPS_USED(io);
248 	inmem = __ops_memory_new();
249 	if (!__ops_mem_readfile(inmem, infile)) {
250 		return 0;
251 	}
252 	fd_out = __ops_setup_file_write(&output, outfile, allow_overwrite);
253 	if (fd_out < 0) {
254 		__ops_memory_free(inmem);
255 		return 0;
256 	}
257 
258 	/* set armoured/not armoured here */
259 	if (use_armour) {
260 		__ops_writer_push_armor_msg(output);
261 	}
262 
263 	/* Push the encrypted writer */
264 	__ops_push_enc_se_ip(output, pubkey);
265 
266 	/* This does the writing */
267 	__ops_write(output, __ops_mem_data(inmem), __ops_mem_len(inmem));
268 
269 	/* tidy up */
270 	__ops_memory_free(inmem);
271 	__ops_teardown_file_write(output, fd_out);
272 
273 	return 1;
274 }
275 
276 /**
277    \ingroup HighLevel_Crypto
278    \brief Decrypt a file.
279    \param infile Name of file to be decrypted
280    \param outfile Name of file to write to. If NULL, the filename is constructed from the input filename, following GPG conventions.
281    \param keyring Keyring to use
282    \param use_armour Expect armoured text, if set
283    \param allow_overwrite Allow output file to overwritten, if set.
284    \param getpassfunc Callback to use to get passphrase
285 */
286 
287 unsigned
288 __ops_decrypt_file(__ops_io_t *io,
289 			const char *infile,
290 			const char *outfile,
291 			__ops_keyring_t *keyring,
292 			const unsigned use_armour,
293 			const unsigned allow_overwrite,
294 			void *passfp,
295 			__ops_cbfunc_t *getpassfunc)
296 {
297 	__ops_stream_t	*parse = NULL;
298 	const int		 printerrors = 1;
299 	char			*filename = NULL;
300 	int			 fd_in = 0;
301 	int			 fd_out = 0;
302 
303 	/* setup for reading from given input file */
304 	fd_in = __ops_setup_file_read(io, &parse, infile,
305 				    NULL,
306 				    callback_write_parsed,
307 				    0);
308 	if (fd_in < 0) {
309 		perror(infile);
310 		return 0;
311 	}
312 	/* setup output filename */
313 	if (outfile) {
314 		fd_out = __ops_setup_file_write(&parse->cbinfo.output, outfile,
315 				allow_overwrite);
316 		if (fd_out < 0) {
317 			perror(outfile);
318 			__ops_teardown_file_read(parse, fd_in);
319 			return 0;
320 		}
321 	} else {
322 		const int	suffixlen = 4;
323 		const char     *suffix = infile + strlen(infile) - suffixlen;
324 		unsigned	filenamelen;
325 
326 		if (strcmp(suffix, ".gpg") == 0 ||
327 		    strcmp(suffix, ".asc") == 0) {
328 			filenamelen = strlen(infile) - strlen(suffix);
329 			filename = calloc(1, filenamelen + 1);
330 			(void) strncpy(filename, infile, filenamelen);
331 			filename[filenamelen] = 0x0;
332 		}
333 
334 		fd_out = __ops_setup_file_write(&parse->cbinfo.output,
335 					filename, allow_overwrite);
336 		if (fd_out < 0) {
337 			perror(filename);
338 			(void) free(filename);
339 			__ops_teardown_file_read(parse, fd_in);
340 			return 0;
341 		}
342 		if (filename) {
343 			(void) free(filename);
344 		}
345 	}
346 
347 	/* \todo check for suffix matching armour param */
348 
349 	/* setup for writing decrypted contents to given output file */
350 
351 	/* setup keyring and passphrase callback */
352 	parse->cbinfo.cryptinfo.keyring = keyring;
353 	parse->cbinfo.passfp = passfp;
354 	parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
355 
356 	/* Set up armour/passphrase options */
357 	if (use_armour) {
358 		__ops_reader_push_dearmour(parse);
359 	}
360 
361 	/* Do it */
362 	__ops_parse(parse, printerrors);
363 
364 	/* Unsetup */
365 	if (use_armour) {
366 		__ops_reader_pop_dearmour(parse);
367 	}
368 
369 	if (filename) {
370 		__ops_teardown_file_write(parse->cbinfo.output, fd_out);
371 	}
372 	__ops_teardown_file_read(parse, fd_in);
373 	/* \todo cleardown crypt */
374 
375 	return 1;
376 }
377 
378 static __ops_cb_ret_t
379 callback_write_parsed(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
380 {
381 	const __ops_contents_t	*content = &pkt->u;
382 	static unsigned		 skipping;
383 
384 	if (__ops_get_debug_level(__FILE__)) {
385 		printf("callback_write_parsed: ");
386 		__ops_print_packet(pkt);
387 	}
388 	if (pkt->tag != OPS_PTAG_CT_UNARMOURED_TEXT && skipping) {
389 		puts("...end of skip");
390 		skipping = 0;
391 	}
392 	switch (pkt->tag) {
393 	case OPS_PTAG_CT_UNARMOURED_TEXT:
394 		printf("OPS_PTAG_CT_UNARMOURED_TEXT\n");
395 		if (!skipping) {
396 			puts("Skipping...");
397 			skipping = 1;
398 		}
399 		fwrite(content->unarmoured_text.data, 1,
400 		       content->unarmoured_text.length, stdout);
401 		break;
402 
403 	case OPS_PTAG_CT_PK_SESSION_KEY:
404 		return pk_sesskey_cb(pkt, cbinfo);
405 
406 	case OPS_GET_SECKEY:
407 		return get_seckey_cb(pkt, cbinfo);
408 
409 	case OPS_GET_PASSPHRASE:
410 		return cbinfo->cryptinfo.getpassphrase(pkt, cbinfo);
411 
412 	case OPS_PTAG_CT_LITDATA_BODY:
413 		return litdata_cb(pkt, cbinfo);
414 
415 	case OPS_PTAG_CT_ARMOUR_HEADER:
416 	case OPS_PTAG_CT_ARMOUR_TRAILER:
417 	case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
418 	case OPS_PTAG_CT_COMPRESSED:
419 	case OPS_PTAG_CT_LITDATA_HEADER:
420 	case OPS_PTAG_CT_SE_IP_DATA_BODY:
421 	case OPS_PTAG_CT_SE_IP_DATA_HEADER:
422 	case OPS_PTAG_CT_SE_DATA_BODY:
423 	case OPS_PTAG_CT_SE_DATA_HEADER:
424 		/* Ignore these packets  */
425 		/* They're handled in __ops_parse_packet() */
426 		/* and nothing else needs to be done */
427 		break;
428 
429 	default:
430 		if (__ops_get_debug_level(__FILE__)) {
431 			fprintf(stderr, "Unexpected packet tag=%d (0x%x)\n",
432 				pkt->tag,
433 				pkt->tag);
434 		}
435 		break;
436 	}
437 
438 	return OPS_RELEASE_MEMORY;
439 }
440