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