1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <fcntl.h>
32#include <libintl.h>
33#include <locale.h>
34#include <sys/des.h>
35#include <strings.h>
36#include <errno.h>
37#include <wanbootutil.h>
38#include <sys/sysmacros.h>
39#include <sys/wanboot_impl.h>
40
41/* Return codes */
42#define	ENCR_SUCCESS	0
43#define	ENCR_NOKEY	1
44#define	ENCR_ERROR	2
45
46/* Private buffer length */
47#define	ENCR_BUF_LEN		1024
48
49/* Encryption algorithm suboption. */
50#define	TYPE	0
51
52static char *opts[] = { "type", NULL };
53
54/*
55 * This routine is used to parse the suboptions of '-o' option.
56 *
57 * The option should be of the form: type=<3des|aes>
58 *
59 * This routine will pass the value of the suboption back in the
60 * supplied arguments, 'ka'.
61 *
62 * Returns:
63 *	ENCR_SUCCESS or ENCR_ERROR.
64 */
65static int
66process_option(char *arg, wbku_key_attr_t *ka)
67{
68	char *value;
69	wbku_retcode_t ret;
70
71	while (*arg != '\0') {
72		switch (getsubopt(&arg, opts, &value)) {
73		case TYPE:
74			/*
75			 * Key type.
76			 */
77			ret = wbku_str_to_keyattr(value, ka, WBKU_ENCR_KEY);
78			if (ret != WBKU_SUCCESS) {
79				wbku_printerr("%s\n", wbku_retmsg(ret));
80				return (ENCR_ERROR);
81			}
82			break;
83		default:
84			wbku_printerr("Invalid option %s\n", value);
85			return (ENCR_ERROR);
86		}
87	}
88
89	return (ENCR_SUCCESS);
90}
91
92/*
93 * This routine is used to find the key of type defined by 'ka' and
94 * return it in 'key'. The key file should have been opened by the
95 * caller and the handle passed in 'key_fp'.
96 *
97 * Returns:
98 *	ENCR_SUCCESS, ENCR_ERROR or ENCR_NOKEY.
99 */
100static int
101get_key(FILE *key_fp, wbku_key_attr_t *ka, uint8_t *key)
102{
103	wbku_retcode_t ret;
104
105	/*
106	 * Find the client key, if it exists.
107	 */
108	ret = wbku_find_key(key_fp, NULL, ka, key, B_FALSE);
109	if (ret != WBKU_SUCCESS) {
110		wbku_printerr("%s\n", wbku_retmsg(ret));
111		if (ret == WBKU_NOKEY)
112			return (ENCR_NOKEY);
113		else
114			return (ENCR_ERROR);
115	}
116	return (ENCR_SUCCESS);
117}
118
119/*
120 * This routine is the common encryption routine used to encrypt data
121 * using the CBC handle initialized by the calling routine.  The data
122 * to be encrypted is read from stdin and the encrypted data is written to
123 * stdout.
124 *
125 * Returns:
126 *	ENCR_SUCCESS or ENCR_ERROR.
127 */
128static int
129encr_gen(cbc_handle_t *ch)
130{
131	uint8_t iv[WANBOOT_MAXBLOCKLEN];
132	uint8_t buf[ENCR_BUF_LEN];
133	uint8_t *bufp;
134	int read_size;
135	ssize_t i, j, k;
136
137	/*
138	 * Use a random number as the IV
139	 */
140	if (wbio_nread_rand(iv, ch->blocklen) != 0) {
141		wbku_printerr("Cannot generate initialization vector");
142		return (ENCR_ERROR);
143	}
144
145	/*
146	 * Output the IV to stdout.
147	 */
148	if (wbio_nwrite(STDOUT_FILENO, iv, ch->blocklen) != 0) {
149		wbku_printerr("Write error encountered\n");
150		return (ENCR_ERROR);
151	}
152
153	/*
154	 * Try to read in multiple of block_size as CBC requires
155	 * that data be encrypted in block_size chunks.
156	 */
157	read_size = ENCR_BUF_LEN / ch->blocklen * ch->blocklen;
158	while ((i = read(STDIN_FILENO, buf, read_size)) > 0) {
159		/*
160		 * If data received is not a multiple of the block size,
161		 * try to receive more.  If reach EOF, pad the rest with
162		 * 0.
163		 */
164		if ((j = i % ch->blocklen) != 0) {
165			/*
166			 * Determine how more data need to be received to
167			 * fill out the buffer so that it contains a
168			 * multiple of block_size chunks.
169			 */
170			j = ch->blocklen - j;
171			bufp = buf + i;
172			k = j;
173
174			/*
175			 * Try to fill the gap.
176			 *
177			 */
178			while ((j = read(STDIN_FILENO, bufp, j)) != k &&
179			    j != 0) {
180				bufp += j;
181				k -= j;
182				j = k;
183			}
184
185			/*
186			 * This is the total length of the buffer.
187			 */
188			i = (i + ch->blocklen) - (i % ch->blocklen);
189
190			if (j == 0) {
191				/* EOF, do padding. */
192				(void) memset(bufp, 0, k);
193				(void) cbc_encrypt(ch, buf, i, iv);
194			} else if (j > 0) {
195				/* The gap has been filled in */
196				(void) cbc_encrypt(ch, buf, i, iv);
197			} else {
198				/* Oops. */
199				wbku_printerr("Input error");
200				return (ENCR_ERROR);
201			}
202		} else {
203			/* A multiple of the block size was received */
204			(void) cbc_encrypt(ch, buf, i, iv);
205		}
206		if (wbio_nwrite(STDOUT_FILENO, buf, i) != 0) {
207			wbku_printerr("Write error encountered\n");
208			return (ENCR_ERROR);
209		}
210	}
211
212	return (ENCR_SUCCESS);
213}
214
215/*
216 * This routine initializes a CBC handle for 3DES and calls the
217 * common encryption routine to encrypt data.
218 *
219 * Returns:
220 *	ENCR_SUCCESS or ENCR_ERROR.
221 */
222static int
223encr_gen_3des(const wbku_key_attr_t *ka, const uint8_t *key)
224{
225	cbc_handle_t ch;
226	void *eh;
227	int ret;
228
229	/*
230	 * Initialize a 3DES handle.
231	 */
232	if (des3_init(&eh) != 0) {
233		return (ENCR_ERROR);
234	}
235	des3_key(eh, key);
236
237	/*
238	 * Initialize the CBC handle.
239	 */
240	cbc_makehandle(&ch, eh, ka->ka_len, DES3_BLOCK_SIZE,
241	    DES3_IV_SIZE, des3_encrypt, des3_decrypt);
242
243	/*
244	 * Encrypt the data.
245	 */
246	ret = encr_gen(&ch);
247
248	/*
249	 *  Free the 3DES resources.
250	 */
251	des3_fini(eh);
252
253	return (ret);
254}
255
256/*
257 * This routine initializes a CBC handle for AES and calls the
258 * common encryption routine to encrypt data.
259 *
260 * Returns:
261 *	ENCR_SUCCESS or ENCR_ERROR.
262 */
263static int
264encr_gen_aes(const wbku_key_attr_t *ka, const uint8_t *key)
265{
266	cbc_handle_t ch;
267	void *eh;
268	int ret;
269
270	/*
271	 * Initialize an AES handle.
272	 */
273	if (aes_init(&eh) != 0) {
274		return (ENCR_ERROR);
275	}
276	aes_key(eh, key, ka->ka_len);
277
278	/*
279	 * Initialize the CBC handle.
280	 */
281	cbc_makehandle(&ch, eh, ka->ka_len, AES_BLOCK_SIZE,
282	    AES_IV_SIZE, aes_encrypt, aes_decrypt);
283
284	/*
285	 * Encrypt the data.
286	 */
287	ret = encr_gen(&ch);
288
289	/*
290	 *  Free the AES resources.
291	 */
292	aes_fini(eh);
293
294	return (ret);
295}
296
297/*
298 * Prints usage().
299 */
300static void
301usage(const char *cmd)
302{
303	(void) fprintf(stderr,
304	    gettext("Usage: %s -o type=<%s|%s> -k key_file\n"),
305	    cmd, WBKU_KW_3DES, WBKU_KW_AES_128);
306}
307
308/*
309 * This program is used to encrypt data read from stdin and print it to
310 * stdout. The path to the key file and the algorithm to use are
311 * provided by the user.
312 *
313 * Returns:
314 *	ENCR_SUCCESS, ENCR_ERROR or ENCR_NOKEY.
315 */
316int
317main(int argc, char **argv)
318{
319	uint8_t key[WANBOOT_MAXKEYLEN];
320	int c;
321	char *keyfile_name = NULL;
322	wbku_key_attr_t ka;
323	FILE *key_fp;
324	int ret;
325
326	/*
327	 * Do the necessary magic for localization support.
328	 */
329	(void) setlocale(LC_ALL, "");
330#if !defined(TEXT_DOMAIN)
331#define	TEXT_DOMAIN "SYS_TEST"
332#endif
333	(void) textdomain(TEXT_DOMAIN);
334
335	/*
336	 * Initialize program name for use by wbku_printerr().
337	 */
338	wbku_errinit(argv[0]);
339
340	/*
341	 * Should be five arguments.
342	 */
343	if (argc < 5) {
344		usage(argv[0]);
345		return (ENCR_ERROR);
346	}
347
348	/*
349	 * Parse the options.
350	 */
351	ka.ka_type = WBKU_KEY_UNKNOWN;
352	while ((c = getopt(argc, argv, "o:k:")) != EOF) {
353		switch (c) {
354		case 'o':
355			/*
356			 * Suboptions.
357			 */
358			ret = process_option(optarg, &ka);
359			if (ret != ENCR_SUCCESS) {
360				usage(argv[0]);
361				return (ret);
362			}
363			break;
364		case 'k':
365			/*
366			 * Path to key file.
367			 */
368			keyfile_name = optarg;
369			break;
370		default:
371			usage(argv[0]);
372			return (ENCR_ERROR);
373		}
374	}
375
376	/*
377	 * Gotta have a key file.
378	 */
379	if (keyfile_name == NULL) {
380		wbku_printerr("Must specify the key_file\n");
381		return (ENCR_ERROR);
382	}
383
384	/*
385	 * Gotta have a key type.
386	 */
387	if (ka.ka_type == WBKU_KEY_UNKNOWN) {
388		wbku_printerr("Unsupported encryption algorithm\n");
389		return (ENCR_ERROR);
390	}
391
392	/*
393	 * Open the key file for reading.
394	 */
395	if ((key_fp = fopen(keyfile_name, "r")) == NULL) {
396		wbku_printerr("Cannot open %s", keyfile_name);
397		return (ENCR_ERROR);
398	}
399
400	/*
401	 * Get the key from the key file and call the right
402	 * encryption routine.
403	 */
404	ret = get_key(key_fp, &ka, key);
405	if (ret == ENCR_SUCCESS) {
406		switch (ka.ka_type) {
407		case WBKU_KEY_3DES:
408			ret = encr_gen_3des(&ka, key);
409			break;
410		case WBKU_KEY_AES_128:
411			ret = encr_gen_aes(&ka, key);
412			break;
413		default:
414			ret = ENCR_ERROR;	/* Internal error only */
415		}
416	}
417
418	(void) fclose(key_fp);
419	return (ret);
420}
421