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