1987da915Sopenharmony_ci/**
2987da915Sopenharmony_ci * ntfsdecrypt - Decrypt ntfs encrypted files.  Part of the Linux-NTFS project.
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci * Copyright (c) 2005 Yuval Fledel
5987da915Sopenharmony_ci * Copyright (c) 2005-2007 Anton Altaparmakov
6987da915Sopenharmony_ci * Copyright (c) 2007 Yura Pakhuchiy
7987da915Sopenharmony_ci * Copyright (c) 2014-2015 Jean-Pierre Andre
8987da915Sopenharmony_ci *
9987da915Sopenharmony_ci * This utility will decrypt files and print the decrypted data on the standard
10987da915Sopenharmony_ci * output.
11987da915Sopenharmony_ci *
12987da915Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
13987da915Sopenharmony_ci * it under the terms of the GNU General Public License as published by
14987da915Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
15987da915Sopenharmony_ci * (at your option) any later version.
16987da915Sopenharmony_ci *
17987da915Sopenharmony_ci * This program is distributed in the hope that it will be useful,
18987da915Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
19987da915Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20987da915Sopenharmony_ci * GNU General Public License for more details.
21987da915Sopenharmony_ci *
22987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
23987da915Sopenharmony_ci * along with this program (in the main directory of the Linux-NTFS
24987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
25987da915Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26987da915Sopenharmony_ci */
27987da915Sopenharmony_ci
28987da915Sopenharmony_ci#include "config.h"
29987da915Sopenharmony_ci
30987da915Sopenharmony_ci#ifdef HAVE_SYS_TYPES_H
31987da915Sopenharmony_ci#include <sys/types.h>
32987da915Sopenharmony_ci#endif
33987da915Sopenharmony_ci#ifdef HAVE_SYS_STAT_H
34987da915Sopenharmony_ci#include <sys/stat.h>
35987da915Sopenharmony_ci#endif
36987da915Sopenharmony_ci#ifdef HAVE_FCNTL_H
37987da915Sopenharmony_ci#include <fcntl.h>
38987da915Sopenharmony_ci#endif
39987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
40987da915Sopenharmony_ci#include <stdio.h>
41987da915Sopenharmony_ci#endif
42987da915Sopenharmony_ci#ifdef HAVE_GETOPT_H
43987da915Sopenharmony_ci#include <getopt.h>
44987da915Sopenharmony_ci#endif
45987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
46987da915Sopenharmony_ci#include <stdlib.h>
47987da915Sopenharmony_ci#endif
48987da915Sopenharmony_ci#ifdef HAVE_STRING_H
49987da915Sopenharmony_ci#include <string.h>
50987da915Sopenharmony_ci#endif
51987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H
52987da915Sopenharmony_ci#include <unistd.h>
53987da915Sopenharmony_ci#endif
54987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H
55987da915Sopenharmony_ci#include <errno.h>
56987da915Sopenharmony_ci#endif
57987da915Sopenharmony_ci#include <gcrypt.h>
58987da915Sopenharmony_ci#include <gnutls/pkcs12.h>
59987da915Sopenharmony_ci
60987da915Sopenharmony_ci#include "types.h"
61987da915Sopenharmony_ci#include "attrib.h"
62987da915Sopenharmony_ci#include "utils.h"
63987da915Sopenharmony_ci#include "volume.h"
64987da915Sopenharmony_ci#include "debug.h"
65987da915Sopenharmony_ci#include "dir.h"
66987da915Sopenharmony_ci#include "layout.h"
67987da915Sopenharmony_ci/* #include "version.h" */
68987da915Sopenharmony_ci#include "misc.h"
69987da915Sopenharmony_ci
70987da915Sopenharmony_citypedef gcry_sexp_t ntfs_rsa_private_key;
71987da915Sopenharmony_ci
72987da915Sopenharmony_ci#define NTFS_SHA1_THUMBPRINT_SIZE 0x14
73987da915Sopenharmony_ci
74987da915Sopenharmony_ci#define NTFS_CRED_TYPE_CERT_THUMBPRINT const_cpu_to_le32(3)
75987da915Sopenharmony_ci
76987da915Sopenharmony_ci#define NTFS_EFS_CERT_PURPOSE_OID_DDF "1.3.6.1.4.1.311.10.3.4" /* decryption */
77987da915Sopenharmony_ci#define NTFS_EFS_CERT_PURPOSE_OID_DRF "1.3.6.1.4.1.311.10.3.4.1" /* recovery */
78987da915Sopenharmony_ci
79987da915Sopenharmony_citypedef enum {
80987da915Sopenharmony_ci	DF_TYPE_UNKNOWN,
81987da915Sopenharmony_ci	DF_TYPE_DDF, /* decryption */
82987da915Sopenharmony_ci	DF_TYPE_DRF, /* recovery */
83987da915Sopenharmony_ci} NTFS_DF_TYPES;
84987da915Sopenharmony_ci
85987da915Sopenharmony_ci/**
86987da915Sopenharmony_ci * enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit)
87987da915Sopenharmony_ci *
88987da915Sopenharmony_ci * To choose which one is used in Windows, create or set the REG_DWORD registry
89987da915Sopenharmony_ci * key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\
90987da915Sopenharmony_ci * AlgorithmID to the value of your chosen crypto algorithm, e.g. to use DesX,
91987da915Sopenharmony_ci * set AlgorithmID to 0x6604.
92987da915Sopenharmony_ci *
93987da915Sopenharmony_ci * Note that the Windows versions I have tried so far (all are high crypto
94987da915Sopenharmony_ci * enabled) ignore the AlgorithmID value if it is not one of CALG_3DES,
95987da915Sopenharmony_ci * CALG_DESX, or CALG_AES_256, i.e. you cannot select CALG_DES at all using
96987da915Sopenharmony_ci * this registry key.  It would be interesting to check out encryption on one
97987da915Sopenharmony_ci * of the "crippled" crypto Windows versions...
98987da915Sopenharmony_ci */
99987da915Sopenharmony_citypedef enum {
100987da915Sopenharmony_ci	CALG_DES	= const_cpu_to_le32(0x6601),
101987da915Sopenharmony_ci	/* If not one of the below three, fall back to standard Des. */
102987da915Sopenharmony_ci	CALG_3DES	= const_cpu_to_le32(0x6603),
103987da915Sopenharmony_ci	CALG_DESX	= const_cpu_to_le32(0x6604),
104987da915Sopenharmony_ci	CALG_AES_256	= const_cpu_to_le32(0x6610),
105987da915Sopenharmony_ci} NTFS_CRYPTO_ALGORITHMS;
106987da915Sopenharmony_ci
107987da915Sopenharmony_citypedef struct {
108987da915Sopenharmony_ci	u64 in_whitening, out_whitening;
109987da915Sopenharmony_ci	u8 des_key[8];
110987da915Sopenharmony_ci	u64 prev_blk;
111987da915Sopenharmony_ci} ntfs_desx_ctx;
112987da915Sopenharmony_ci
113987da915Sopenharmony_ci/**
114987da915Sopenharmony_ci * struct ntfs_fek - Decrypted, in-memory file encryption key.
115987da915Sopenharmony_ci */
116987da915Sopenharmony_ci
117987da915Sopenharmony_citypedef struct {
118987da915Sopenharmony_ci	gcry_cipher_hd_t gcry_cipher_hd;
119987da915Sopenharmony_ci	le32 alg_id;
120987da915Sopenharmony_ci	u8 *key_data;
121987da915Sopenharmony_ci	gcry_cipher_hd_t *des_gcry_cipher_hd_ptr;
122987da915Sopenharmony_ci	ntfs_desx_ctx desx_ctx;
123987da915Sopenharmony_ci} ntfs_fek;
124987da915Sopenharmony_ci
125987da915Sopenharmony_cistruct options {
126987da915Sopenharmony_ci	char *keyfile;	/* .pfx file containing the user's private key. */
127987da915Sopenharmony_ci	char *device;		/* Device/File to work with */
128987da915Sopenharmony_ci	char *file;		/* File to display */
129987da915Sopenharmony_ci	s64 inode;		/* Inode to work with */
130987da915Sopenharmony_ci	ATTR_TYPES attr;	/* Attribute type to display */
131987da915Sopenharmony_ci	int force;		/* Override common sense */
132987da915Sopenharmony_ci	int quiet;		/* Less output */
133987da915Sopenharmony_ci	int verbose;		/* Extra output */
134987da915Sopenharmony_ci	int encrypt;		/* Encrypt */
135987da915Sopenharmony_ci};
136987da915Sopenharmony_ci
137987da915Sopenharmony_cistatic const char *EXEC_NAME = "ntfsdecrypt";
138987da915Sopenharmony_cistatic struct options opts;
139987da915Sopenharmony_ci
140987da915Sopenharmony_cistatic ntfschar EFS[5] = {
141987da915Sopenharmony_ci	const_cpu_to_le16('$'), const_cpu_to_le16('E'), const_cpu_to_le16('F'),
142987da915Sopenharmony_ci	const_cpu_to_le16('S'), const_cpu_to_le16('\0')
143987da915Sopenharmony_ci};
144987da915Sopenharmony_ci
145987da915Sopenharmony_ci/**
146987da915Sopenharmony_ci * version - Print version information about the program
147987da915Sopenharmony_ci *
148987da915Sopenharmony_ci * Print a copyright statement and a brief description of the program.
149987da915Sopenharmony_ci *
150987da915Sopenharmony_ci * Return:  none
151987da915Sopenharmony_ci */
152987da915Sopenharmony_cistatic void version(void)
153987da915Sopenharmony_ci{
154987da915Sopenharmony_ci	ntfs_log_info("\n%s v%s (libntfs-3g) - Decrypt files and print on the "
155987da915Sopenharmony_ci			"standard output.\n\n", EXEC_NAME, VERSION);
156987da915Sopenharmony_ci	ntfs_log_info("Copyright (c) 2005 Yuval Fledel\n");
157987da915Sopenharmony_ci	ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n");
158987da915Sopenharmony_ci	ntfs_log_info("Copyright (c) 2014-2015 Jean-Pierre Andre\n");
159987da915Sopenharmony_ci	ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
160987da915Sopenharmony_ci}
161987da915Sopenharmony_ci
162987da915Sopenharmony_ci/**
163987da915Sopenharmony_ci * usage - Print a list of the parameters to the program
164987da915Sopenharmony_ci *
165987da915Sopenharmony_ci * Print a list of the parameters and options for the program.
166987da915Sopenharmony_ci *
167987da915Sopenharmony_ci * Return:  none
168987da915Sopenharmony_ci */
169987da915Sopenharmony_cistatic void usage(void)
170987da915Sopenharmony_ci{
171987da915Sopenharmony_ci	ntfs_log_info("\nUsage: %s [options] -k name.pfx device [file]\n\n"
172987da915Sopenharmony_ci	       "    -i, --inode num         Display this inode\n\n"
173987da915Sopenharmony_ci	       "    -k  --keyfile name.pfx  Use file name as the user's private key file.\n"
174987da915Sopenharmony_ci	       "    -e  --encrypt           Update an encrypted file\n"
175987da915Sopenharmony_ci	       "    -f  --force             Use less caution\n"
176987da915Sopenharmony_ci	       "    -h  --help              Print this help\n"
177987da915Sopenharmony_ci	       "    -q  --quiet             Less output\n"
178987da915Sopenharmony_ci	       "    -V  --version           Version information\n"
179987da915Sopenharmony_ci	       "    -v  --verbose           More output\n\n",
180987da915Sopenharmony_ci	       EXEC_NAME);
181987da915Sopenharmony_ci	ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
182987da915Sopenharmony_ci}
183987da915Sopenharmony_ci
184987da915Sopenharmony_ci/**
185987da915Sopenharmony_ci * parse_options - Read and validate the programs command line
186987da915Sopenharmony_ci *
187987da915Sopenharmony_ci * Read the command line, verify the syntax and parse the options.
188987da915Sopenharmony_ci * This function is very long, but quite simple.
189987da915Sopenharmony_ci *
190987da915Sopenharmony_ci * Return:  1 Success
191987da915Sopenharmony_ci *	    0 Error, one or more problems
192987da915Sopenharmony_ci */
193987da915Sopenharmony_cistatic int parse_options(int argc, char **argv)
194987da915Sopenharmony_ci{
195987da915Sopenharmony_ci	static const char *sopt = "-fh?ei:k:qVv";
196987da915Sopenharmony_ci	static const struct option lopt[] = {
197987da915Sopenharmony_ci		{"encrypt", no_argument, NULL, 'e'},
198987da915Sopenharmony_ci		{"force", no_argument, NULL, 'f'},
199987da915Sopenharmony_ci		{"help", no_argument, NULL, 'h'},
200987da915Sopenharmony_ci		{"inode", required_argument, NULL, 'i'},
201987da915Sopenharmony_ci		{"keyfile", required_argument, NULL, 'k'},
202987da915Sopenharmony_ci		{"quiet", no_argument, NULL, 'q'},
203987da915Sopenharmony_ci		{"version", no_argument, NULL, 'V'},
204987da915Sopenharmony_ci		{"verbose", no_argument, NULL, 'v'},
205987da915Sopenharmony_ci		{NULL, 0, NULL, 0}
206987da915Sopenharmony_ci	};
207987da915Sopenharmony_ci
208987da915Sopenharmony_ci	int c = -1;
209987da915Sopenharmony_ci	int err = 0;
210987da915Sopenharmony_ci	int ver = 0;
211987da915Sopenharmony_ci	int help = 0;
212987da915Sopenharmony_ci
213987da915Sopenharmony_ci	opterr = 0;		/* We'll handle the errors, thank you. */
214987da915Sopenharmony_ci
215987da915Sopenharmony_ci	opts.inode = -1;
216987da915Sopenharmony_ci
217987da915Sopenharmony_ci	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
218987da915Sopenharmony_ci		switch (c) {
219987da915Sopenharmony_ci		case 1:	/* A non-option argument */
220987da915Sopenharmony_ci			if (!opts.device)
221987da915Sopenharmony_ci				opts.device = argv[optind - 1];
222987da915Sopenharmony_ci			else if (!opts.file)
223987da915Sopenharmony_ci				opts.file = argv[optind - 1];
224987da915Sopenharmony_ci			else {
225987da915Sopenharmony_ci				ntfs_log_error("You must specify exactly one "
226987da915Sopenharmony_ci					"file.\n");
227987da915Sopenharmony_ci				err++;
228987da915Sopenharmony_ci			}
229987da915Sopenharmony_ci			break;
230987da915Sopenharmony_ci		case 'e':
231987da915Sopenharmony_ci			opts.encrypt++;
232987da915Sopenharmony_ci			break;
233987da915Sopenharmony_ci		case 'f':
234987da915Sopenharmony_ci			opts.force++;
235987da915Sopenharmony_ci			break;
236987da915Sopenharmony_ci		case 'h':
237987da915Sopenharmony_ci			help++;
238987da915Sopenharmony_ci			break;
239987da915Sopenharmony_ci		case 'k':
240987da915Sopenharmony_ci			if (!opts.keyfile)
241987da915Sopenharmony_ci				opts.keyfile = argv[optind - 1];
242987da915Sopenharmony_ci			else {
243987da915Sopenharmony_ci				ntfs_log_error("You must specify exactly one "
244987da915Sopenharmony_ci						"key file.\n");
245987da915Sopenharmony_ci				err++;
246987da915Sopenharmony_ci			}
247987da915Sopenharmony_ci			break;
248987da915Sopenharmony_ci		case 'i':
249987da915Sopenharmony_ci			if (opts.inode != -1)
250987da915Sopenharmony_ci				ntfs_log_error("You must specify exactly one "
251987da915Sopenharmony_ci						"inode.\n");
252987da915Sopenharmony_ci			else if (utils_parse_size(optarg, &opts.inode, FALSE))
253987da915Sopenharmony_ci				break;
254987da915Sopenharmony_ci			else
255987da915Sopenharmony_ci				ntfs_log_error("Couldn't parse inode number.\n");
256987da915Sopenharmony_ci			err++;
257987da915Sopenharmony_ci			break;
258987da915Sopenharmony_ci		case 'q':
259987da915Sopenharmony_ci			opts.quiet++;
260987da915Sopenharmony_ci			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
261987da915Sopenharmony_ci			break;
262987da915Sopenharmony_ci		case 'V':
263987da915Sopenharmony_ci			ver++;
264987da915Sopenharmony_ci			break;
265987da915Sopenharmony_ci		case 'v':
266987da915Sopenharmony_ci			opts.verbose++;
267987da915Sopenharmony_ci			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
268987da915Sopenharmony_ci			break;
269987da915Sopenharmony_ci		case '?':
270987da915Sopenharmony_ci		default:
271987da915Sopenharmony_ci			ntfs_log_error("Unknown option '%s'.\n",
272987da915Sopenharmony_ci				argv[optind - 1]);
273987da915Sopenharmony_ci			err++;
274987da915Sopenharmony_ci			break;
275987da915Sopenharmony_ci		}
276987da915Sopenharmony_ci	}
277987da915Sopenharmony_ci
278987da915Sopenharmony_ci	if (help || ver) {
279987da915Sopenharmony_ci		opts.quiet = 0;
280987da915Sopenharmony_ci		ntfs_log_set_levels(NTFS_LOG_LEVEL_QUIET);
281987da915Sopenharmony_ci	} else {
282987da915Sopenharmony_ci		if (!opts.keyfile) {
283987da915Sopenharmony_ci			ntfs_log_error("You must specify a key file.\n");
284987da915Sopenharmony_ci			err++;
285987da915Sopenharmony_ci		} else if (opts.device == NULL) {
286987da915Sopenharmony_ci			ntfs_log_error("You must specify a device.\n");
287987da915Sopenharmony_ci			err++;
288987da915Sopenharmony_ci		} else if (opts.file == NULL && opts.inode == -1) {
289987da915Sopenharmony_ci			ntfs_log_error("You must specify a file or inode with "
290987da915Sopenharmony_ci				"the -i option.\n");
291987da915Sopenharmony_ci			err++;
292987da915Sopenharmony_ci		} else if (opts.file != NULL && opts.inode != -1) {
293987da915Sopenharmony_ci			ntfs_log_error("You can't specify both a file and "
294987da915Sopenharmony_ci				"inode.\n");
295987da915Sopenharmony_ci			err++;
296987da915Sopenharmony_ci		}
297987da915Sopenharmony_ci		if (opts.quiet && opts.verbose) {
298987da915Sopenharmony_ci			ntfs_log_error("You may not use --quiet and --verbose "
299987da915Sopenharmony_ci				"at the same time.\n");
300987da915Sopenharmony_ci			err++;
301987da915Sopenharmony_ci		}
302987da915Sopenharmony_ci	}
303987da915Sopenharmony_ci
304987da915Sopenharmony_ci	if (ver)
305987da915Sopenharmony_ci		version();
306987da915Sopenharmony_ci	if (help || err)
307987da915Sopenharmony_ci		usage();
308987da915Sopenharmony_ci
309987da915Sopenharmony_ci		/* tri-state 0 : done, 1 : error, -1 : proceed */
310987da915Sopenharmony_ci	return (err ? 1 : (help || ver ? 0 : -1));
311987da915Sopenharmony_ci}
312987da915Sopenharmony_ci
313987da915Sopenharmony_ci/**
314987da915Sopenharmony_ci * ntfs_pkcs12_load_pfxfile
315987da915Sopenharmony_ci */
316987da915Sopenharmony_cistatic int ntfs_pkcs12_load_pfxfile(const char *keyfile, u8 **pfx,
317987da915Sopenharmony_ci		unsigned *pfx_size)
318987da915Sopenharmony_ci{
319987da915Sopenharmony_ci	int f, to_read, total, attempts, br;
320987da915Sopenharmony_ci	struct stat key_stat;
321987da915Sopenharmony_ci
322987da915Sopenharmony_ci	if (!keyfile || !pfx || !pfx_size) {
323987da915Sopenharmony_ci		ntfs_log_error("You have to specify the key file, a pointer "
324987da915Sopenharmony_ci				"to hold the key file contents, and a pointer "
325987da915Sopenharmony_ci				"to hold the size of the key file contents.\n");
326987da915Sopenharmony_ci		return -1;
327987da915Sopenharmony_ci	}
328987da915Sopenharmony_ci	f = open(keyfile, O_RDONLY);
329987da915Sopenharmony_ci	if (f == -1) {
330987da915Sopenharmony_ci		ntfs_log_perror("Failed to open key file");
331987da915Sopenharmony_ci		return -1;
332987da915Sopenharmony_ci	}
333987da915Sopenharmony_ci	if (fstat(f, &key_stat) == -1) {
334987da915Sopenharmony_ci		ntfs_log_perror("Failed to stat key file");
335987da915Sopenharmony_ci		goto file_out;
336987da915Sopenharmony_ci	}
337987da915Sopenharmony_ci	if (!S_ISREG(key_stat.st_mode)) {
338987da915Sopenharmony_ci		ntfs_log_error("Key file is not a regular file, cannot read "
339987da915Sopenharmony_ci				"it.\n");
340987da915Sopenharmony_ci		goto file_out;
341987da915Sopenharmony_ci	}
342987da915Sopenharmony_ci	if (!key_stat.st_size) {
343987da915Sopenharmony_ci		ntfs_log_error("Key file has zero size.\n");
344987da915Sopenharmony_ci		goto file_out;
345987da915Sopenharmony_ci	}
346987da915Sopenharmony_ci	*pfx = malloc(key_stat.st_size + 1);
347987da915Sopenharmony_ci	if (!*pfx) {
348987da915Sopenharmony_ci		ntfs_log_perror("Failed to allocate buffer for key file "
349987da915Sopenharmony_ci			"contents");
350987da915Sopenharmony_ci		goto file_out;
351987da915Sopenharmony_ci	}
352987da915Sopenharmony_ci	to_read = key_stat.st_size;
353987da915Sopenharmony_ci	total = attempts = 0;
354987da915Sopenharmony_ci	do {
355987da915Sopenharmony_ci		br = read(f, *pfx + total, to_read);
356987da915Sopenharmony_ci		if (br == -1) {
357987da915Sopenharmony_ci			ntfs_log_perror("Failed to read from key file");
358987da915Sopenharmony_ci			goto free_out;
359987da915Sopenharmony_ci		}
360987da915Sopenharmony_ci		if (!br)
361987da915Sopenharmony_ci			attempts++;
362987da915Sopenharmony_ci		to_read -= br;
363987da915Sopenharmony_ci		total += br;
364987da915Sopenharmony_ci	} while (to_read > 0 && attempts < 3);
365987da915Sopenharmony_ci	close(f);
366987da915Sopenharmony_ci	/* Make sure it is zero terminated. */
367987da915Sopenharmony_ci	(*pfx)[key_stat.st_size] = 0;
368987da915Sopenharmony_ci	*pfx_size = key_stat.st_size;
369987da915Sopenharmony_ci	return 0;
370987da915Sopenharmony_cifree_out:
371987da915Sopenharmony_ci	free(*pfx);
372987da915Sopenharmony_cifile_out:
373987da915Sopenharmony_ci	close(f);
374987da915Sopenharmony_ci	return -1;
375987da915Sopenharmony_ci}
376987da915Sopenharmony_ci
377987da915Sopenharmony_ci/**
378987da915Sopenharmony_ci * ntfs_crypto_init
379987da915Sopenharmony_ci */
380987da915Sopenharmony_cistatic int ntfs_crypto_init(void)
381987da915Sopenharmony_ci{
382987da915Sopenharmony_ci	int err;
383987da915Sopenharmony_ci
384987da915Sopenharmony_ci	/* Initialize gcrypt library.  Note: Must come before GNU TLS init. */
385987da915Sopenharmony_ci	if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != GPG_ERR_NO_ERROR) {
386987da915Sopenharmony_ci		ntfs_log_error("Failed to initialize the gcrypt library.\n");
387987da915Sopenharmony_ci		return -1;
388987da915Sopenharmony_ci	}
389987da915Sopenharmony_ci	/* Initialize GNU TLS library.  Note: Must come after libgcrypt init. */
390987da915Sopenharmony_ci	err = gnutls_global_init();
391987da915Sopenharmony_ci	if (err < 0) {
392987da915Sopenharmony_ci		ntfs_log_error("Failed to initialize GNU TLS library: %s\n",
393987da915Sopenharmony_ci				gnutls_strerror(err));
394987da915Sopenharmony_ci		return -1;
395987da915Sopenharmony_ci	}
396987da915Sopenharmony_ci	return 0;
397987da915Sopenharmony_ci}
398987da915Sopenharmony_ci
399987da915Sopenharmony_ci/**
400987da915Sopenharmony_ci * ntfs_crypto_deinit
401987da915Sopenharmony_ci */
402987da915Sopenharmony_cistatic void ntfs_crypto_deinit(void)
403987da915Sopenharmony_ci{
404987da915Sopenharmony_ci	gnutls_global_deinit();
405987da915Sopenharmony_ci}
406987da915Sopenharmony_ci
407987da915Sopenharmony_ci/**
408987da915Sopenharmony_ci * ntfs_rsa_private_key_import_from_gnutls
409987da915Sopenharmony_ci */
410987da915Sopenharmony_cistatic ntfs_rsa_private_key ntfs_rsa_private_key_import_from_gnutls(
411987da915Sopenharmony_ci		gnutls_x509_privkey_t priv_key)
412987da915Sopenharmony_ci{
413987da915Sopenharmony_ci	int i, j;
414987da915Sopenharmony_ci	size_t tmp_size;
415987da915Sopenharmony_ci	gnutls_datum_t rd[6];
416987da915Sopenharmony_ci	gcry_mpi_t rm[6];
417987da915Sopenharmony_ci	gcry_sexp_t rsa_key;
418987da915Sopenharmony_ci
419987da915Sopenharmony_ci	/* Extract the RSA parameters from the GNU TLS private key. */
420987da915Sopenharmony_ci	if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rd[0], &rd[1],
421987da915Sopenharmony_ci			&rd[2], &rd[3], &rd[4], &rd[5])) {
422987da915Sopenharmony_ci		ntfs_log_error("Failed to export rsa parameters.  (Is the "
423987da915Sopenharmony_ci				"key an RSA private key?)\n");
424987da915Sopenharmony_ci		return NULL;
425987da915Sopenharmony_ci	}
426987da915Sopenharmony_ci	/* Convert each RSA parameter to mpi format. */
427987da915Sopenharmony_ci	for (i = 0; i < 6; i++) {
428987da915Sopenharmony_ci		if (gcry_mpi_scan(&rm[i], GCRYMPI_FMT_USG, rd[i].data,
429987da915Sopenharmony_ci				rd[i].size, &tmp_size) != GPG_ERR_NO_ERROR) {
430987da915Sopenharmony_ci			ntfs_log_error("Failed to convert RSA parameter %i "
431987da915Sopenharmony_ci					"to mpi format (size %d)\n", i,
432987da915Sopenharmony_ci					rd[i].size);
433987da915Sopenharmony_ci			rsa_key = NULL;
434987da915Sopenharmony_ci			break;
435987da915Sopenharmony_ci		}
436987da915Sopenharmony_ci	}
437987da915Sopenharmony_ci	/* Release the no longer needed datum values. */
438987da915Sopenharmony_ci	for (j = 0; j < 6; j++) {
439987da915Sopenharmony_ci		if (rd[j].data && rd[j].size)
440987da915Sopenharmony_ci			gnutls_free(rd[j].data);
441987da915Sopenharmony_ci	}
442987da915Sopenharmony_ci	/*
443987da915Sopenharmony_ci	 * Build the gcrypt private key, note libgcrypt uses p and q inversed
444987da915Sopenharmony_ci	 * to what gnutls uses.
445987da915Sopenharmony_ci	 */
446987da915Sopenharmony_ci	if (i == 6 && gcry_sexp_build(&rsa_key, NULL,
447987da915Sopenharmony_ci			"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
448987da915Sopenharmony_ci			rm[0], rm[1], rm[2], rm[4], rm[3], rm[5]) !=
449987da915Sopenharmony_ci			GPG_ERR_NO_ERROR) {
450987da915Sopenharmony_ci		ntfs_log_error("Failed to build RSA private key s-exp.\n");
451987da915Sopenharmony_ci		rsa_key = NULL;
452987da915Sopenharmony_ci	}
453987da915Sopenharmony_ci	/* Release the no longer needed mpi values. */
454987da915Sopenharmony_ci	for (j = 0; j < i; j++)
455987da915Sopenharmony_ci		gcry_mpi_release(rm[j]);
456987da915Sopenharmony_ci	return (ntfs_rsa_private_key)rsa_key;
457987da915Sopenharmony_ci}
458987da915Sopenharmony_ci
459987da915Sopenharmony_ci/**
460987da915Sopenharmony_ci * ntfs_rsa_private_key_release
461987da915Sopenharmony_ci */
462987da915Sopenharmony_cistatic void ntfs_rsa_private_key_release(ntfs_rsa_private_key rsa_key)
463987da915Sopenharmony_ci{
464987da915Sopenharmony_ci	gcry_sexp_release((gcry_sexp_t)rsa_key);
465987da915Sopenharmony_ci}
466987da915Sopenharmony_ci
467987da915Sopenharmony_ci/**
468987da915Sopenharmony_ci * ntfs_pkcs12_extract_rsa_key
469987da915Sopenharmony_ci */
470987da915Sopenharmony_cistatic ntfs_rsa_private_key ntfs_pkcs12_extract_rsa_key(u8 *pfx, int pfx_size,
471987da915Sopenharmony_ci		char *password, char *thumbprint, int thumbprint_size,
472987da915Sopenharmony_ci		NTFS_DF_TYPES *df_type)
473987da915Sopenharmony_ci{
474987da915Sopenharmony_ci	int err, bag_index, flags;
475987da915Sopenharmony_ci	gnutls_datum_t dpfx, dkey;
476987da915Sopenharmony_ci	gnutls_pkcs12_t pkcs12 = NULL;
477987da915Sopenharmony_ci	gnutls_pkcs12_bag_t bag = NULL;
478987da915Sopenharmony_ci	gnutls_x509_privkey_t pkey = NULL;
479987da915Sopenharmony_ci	gnutls_x509_crt_t crt = NULL;
480987da915Sopenharmony_ci	ntfs_rsa_private_key rsa_key = NULL;
481987da915Sopenharmony_ci	char purpose_oid[100];
482987da915Sopenharmony_ci	size_t purpose_oid_size = sizeof(purpose_oid);
483987da915Sopenharmony_ci	int oid_index;
484987da915Sopenharmony_ci	size_t tp_size = thumbprint_size;
485987da915Sopenharmony_ci	BOOL have_thumbprint = FALSE;
486987da915Sopenharmony_ci
487987da915Sopenharmony_ci	*df_type = DF_TYPE_UNKNOWN;
488987da915Sopenharmony_ci	/* Create a pkcs12 structure. */
489987da915Sopenharmony_ci	err = gnutls_pkcs12_init(&pkcs12);
490987da915Sopenharmony_ci	if (err) {
491987da915Sopenharmony_ci		ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n",
492987da915Sopenharmony_ci				gnutls_strerror(err));
493987da915Sopenharmony_ci		return NULL;
494987da915Sopenharmony_ci	}
495987da915Sopenharmony_ci	/* Convert the PFX file (DER format) to native pkcs12 format. */
496987da915Sopenharmony_ci	dpfx.data = pfx;
497987da915Sopenharmony_ci	dpfx.size = pfx_size;
498987da915Sopenharmony_ci	err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0);
499987da915Sopenharmony_ci	if (err) {
500987da915Sopenharmony_ci		ntfs_log_error("Failed to convert the PFX file from DER to "
501987da915Sopenharmony_ci				"native PKCS#12 format: %s\n",
502987da915Sopenharmony_ci				gnutls_strerror(err));
503987da915Sopenharmony_ci		goto err;
504987da915Sopenharmony_ci	}
505987da915Sopenharmony_ci	/*
506987da915Sopenharmony_ci	 * Verify that the password is correct and that the key file has not
507987da915Sopenharmony_ci	 * been tampered with.  Note if the password has zero length and the
508987da915Sopenharmony_ci	 * verification fails, retry with password set to NULL.  This is needed
509987da915Sopenharmony_ci	 * to get passwordless .pfx files generated with Windows XP SP1 (and
510987da915Sopenharmony_ci	 * probably earlier versions of Windows) to work.
511987da915Sopenharmony_ci	 */
512987da915Sopenharmony_ciretry_verify:
513987da915Sopenharmony_ci	err = gnutls_pkcs12_verify_mac(pkcs12, password);
514987da915Sopenharmony_ci	if (err) {
515987da915Sopenharmony_ci		if (err == GNUTLS_E_MAC_VERIFY_FAILED &&
516987da915Sopenharmony_ci				password && !strlen(password)) {
517987da915Sopenharmony_ci			password = NULL;
518987da915Sopenharmony_ci			goto retry_verify;
519987da915Sopenharmony_ci		}
520987da915Sopenharmony_ci		ntfs_log_error("Failed to verify the MAC: %s  Is the "
521987da915Sopenharmony_ci				"password correct?\n", gnutls_strerror(err));
522987da915Sopenharmony_ci		goto err;
523987da915Sopenharmony_ci	}
524987da915Sopenharmony_ci	for (bag_index = 0; ; bag_index++) {
525987da915Sopenharmony_ci		err = gnutls_pkcs12_bag_init(&bag);
526987da915Sopenharmony_ci		if (err) {
527987da915Sopenharmony_ci			ntfs_log_error("Failed to initialize PKCS#12 Bag "
528987da915Sopenharmony_ci					"structure: %s\n",
529987da915Sopenharmony_ci					gnutls_strerror(err));
530987da915Sopenharmony_ci			goto err;
531987da915Sopenharmony_ci		}
532987da915Sopenharmony_ci		err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag);
533987da915Sopenharmony_ci		if (err) {
534987da915Sopenharmony_ci			if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
535987da915Sopenharmony_ci				err = 0;
536987da915Sopenharmony_ci				break;
537987da915Sopenharmony_ci			}
538987da915Sopenharmony_ci			ntfs_log_error("Failed to obtain Bag from PKCS#12 "
539987da915Sopenharmony_ci					"structure: %s\n",
540987da915Sopenharmony_ci					gnutls_strerror(err));
541987da915Sopenharmony_ci			goto err;
542987da915Sopenharmony_ci		}
543987da915Sopenharmony_cicheck_again:
544987da915Sopenharmony_ci		err = gnutls_pkcs12_bag_get_count(bag);
545987da915Sopenharmony_ci		if (err < 0) {
546987da915Sopenharmony_ci			ntfs_log_error("Failed to obtain Bag count: %s\n",
547987da915Sopenharmony_ci					gnutls_strerror(err));
548987da915Sopenharmony_ci			goto err;
549987da915Sopenharmony_ci		}
550987da915Sopenharmony_ci		err = gnutls_pkcs12_bag_get_type(bag, 0);
551987da915Sopenharmony_ci		if (err < 0) {
552987da915Sopenharmony_ci			ntfs_log_error("Failed to determine Bag type: %s\n",
553987da915Sopenharmony_ci					gnutls_strerror(err));
554987da915Sopenharmony_ci			goto err;
555987da915Sopenharmony_ci		}
556987da915Sopenharmony_ci		flags = 0;
557987da915Sopenharmony_ci		switch (err) {
558987da915Sopenharmony_ci		case GNUTLS_BAG_PKCS8_KEY:
559987da915Sopenharmony_ci			flags = GNUTLS_PKCS_PLAIN;
560987da915Sopenharmony_ci			/* FALLTHRU */
561987da915Sopenharmony_ci		case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
562987da915Sopenharmony_ci			err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey);
563987da915Sopenharmony_ci			if (err < 0) {
564987da915Sopenharmony_ci				ntfs_log_error("Failed to obtain Bag data: "
565987da915Sopenharmony_ci						"%s\n", gnutls_strerror(err));
566987da915Sopenharmony_ci				goto err;
567987da915Sopenharmony_ci			}
568987da915Sopenharmony_ci			err = gnutls_x509_privkey_init(&pkey);
569987da915Sopenharmony_ci			if (err) {
570987da915Sopenharmony_ci				ntfs_log_error("Failed to initialized "
571987da915Sopenharmony_ci						"private key structure: %s\n",
572987da915Sopenharmony_ci						gnutls_strerror(err));
573987da915Sopenharmony_ci				goto err;
574987da915Sopenharmony_ci			}
575987da915Sopenharmony_ci			/* Decrypt the private key into GNU TLS format. */
576987da915Sopenharmony_ci			err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey,
577987da915Sopenharmony_ci					GNUTLS_X509_FMT_DER, password, flags);
578987da915Sopenharmony_ci			if (err) {
579987da915Sopenharmony_ci				ntfs_log_error("Failed to convert private "
580987da915Sopenharmony_ci						"key from DER to GNU TLS "
581987da915Sopenharmony_ci						"format: %s\n",
582987da915Sopenharmony_ci						gnutls_strerror(err));
583987da915Sopenharmony_ci				goto err;
584987da915Sopenharmony_ci			}
585987da915Sopenharmony_ci#if 0
586987da915Sopenharmony_ci			/*
587987da915Sopenharmony_ci			 * Export the key again, but unencrypted, and output it
588987da915Sopenharmony_ci			 * to stderr.  Note the output has an RSA header so to
589987da915Sopenharmony_ci			 * compare to openssl pkcs12 -nodes -in myfile.pfx
590987da915Sopenharmony_ci			 * output need to ignore the part of the key between
591987da915Sopenharmony_ci			 * the first "MII..." up to the second "MII...".  The
592987da915Sopenharmony_ci			 * actual RSA private key begins at the second "MII..."
593987da915Sopenharmony_ci			 * and in my testing at least was identical to openssl
594987da915Sopenharmony_ci			 * output and was also identical both on big and little
595987da915Sopenharmony_ci			 * endian so gnutls should be endianness safe.
596987da915Sopenharmony_ci			 */
597987da915Sopenharmony_ci			char *buf = malloc(8192);
598987da915Sopenharmony_ci			size_t bufsize = 8192;
599987da915Sopenharmony_ci			err = gnutls_x509_privkey_export_pkcs8(pkey,
600987da915Sopenharmony_ci				GNUTLS_X509_FMT_PEM, "", GNUTLS_PKCS_PLAIN, buf,
601987da915Sopenharmony_ci				&bufsize);
602987da915Sopenharmony_ci			if (err) {
603987da915Sopenharmony_ci				ntfs_log_error("eek1\n");
604987da915Sopenharmony_ci				exit(1);
605987da915Sopenharmony_ci			}
606987da915Sopenharmony_ci			ntfs_log_error("%s\n", buf);
607987da915Sopenharmony_ci			free(buf);
608987da915Sopenharmony_ci#endif
609987da915Sopenharmony_ci			/* Convert the private key to our internal format. */
610987da915Sopenharmony_ci			rsa_key = ntfs_rsa_private_key_import_from_gnutls(pkey);
611987da915Sopenharmony_ci			if (!rsa_key)
612987da915Sopenharmony_ci				goto err;
613987da915Sopenharmony_ci			break;
614987da915Sopenharmony_ci		case GNUTLS_BAG_ENCRYPTED:
615987da915Sopenharmony_ci			err = gnutls_pkcs12_bag_decrypt(bag, password);
616987da915Sopenharmony_ci			if (err) {
617987da915Sopenharmony_ci				ntfs_log_error("Failed to decrypt Bag: %s\n",
618987da915Sopenharmony_ci						gnutls_strerror(err));
619987da915Sopenharmony_ci				goto err;
620987da915Sopenharmony_ci			}
621987da915Sopenharmony_ci			goto check_again;
622987da915Sopenharmony_ci		case GNUTLS_BAG_CERTIFICATE:
623987da915Sopenharmony_ci			err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey);
624987da915Sopenharmony_ci			if (err < 0) {
625987da915Sopenharmony_ci				ntfs_log_error("Failed to obtain Bag data: "
626987da915Sopenharmony_ci						"%s\n", gnutls_strerror(err));
627987da915Sopenharmony_ci				goto err;
628987da915Sopenharmony_ci			}
629987da915Sopenharmony_ci			err = gnutls_x509_crt_init(&crt);
630987da915Sopenharmony_ci			if (err) {
631987da915Sopenharmony_ci				ntfs_log_error("Failed to initialize "
632987da915Sopenharmony_ci						"certificate structure: %s\n",
633987da915Sopenharmony_ci						gnutls_strerror(err));
634987da915Sopenharmony_ci				goto err;
635987da915Sopenharmony_ci			}
636987da915Sopenharmony_ci			err = gnutls_x509_crt_import(crt, &dkey,
637987da915Sopenharmony_ci					GNUTLS_X509_FMT_DER);
638987da915Sopenharmony_ci			if (err) {
639987da915Sopenharmony_ci				ntfs_log_error("Failed to convert certificate "
640987da915Sopenharmony_ci						"from DER to GNU TLS format: "
641987da915Sopenharmony_ci						"%s\n", gnutls_strerror(err));
642987da915Sopenharmony_ci				goto err;
643987da915Sopenharmony_ci			}
644987da915Sopenharmony_ci			oid_index = 0;
645987da915Sopenharmony_ci				/*
646987da915Sopenharmony_ci				 * Search in the key purposes for an EFS
647987da915Sopenharmony_ci				 * encryption purpose or an EFS recovery
648987da915Sopenharmony_ci				 * purpose, and use the first one found.
649987da915Sopenharmony_ci				 */
650987da915Sopenharmony_ci			do {
651987da915Sopenharmony_ci				purpose_oid_size = sizeof(purpose_oid);
652987da915Sopenharmony_ci				err = gnutls_x509_crt_get_key_purpose_oid(crt,
653987da915Sopenharmony_ci					oid_index,
654987da915Sopenharmony_ci					purpose_oid, &purpose_oid_size, NULL);
655987da915Sopenharmony_ci				if (!err) {
656987da915Sopenharmony_ci					purpose_oid[purpose_oid_size - 1]
657987da915Sopenharmony_ci							= '\0';
658987da915Sopenharmony_ci					if (!strcmp(purpose_oid,
659987da915Sopenharmony_ci						NTFS_EFS_CERT_PURPOSE_OID_DRF))
660987da915Sopenharmony_ci					*df_type = DF_TYPE_DRF;
661987da915Sopenharmony_ci					else if (!strcmp(purpose_oid,
662987da915Sopenharmony_ci						NTFS_EFS_CERT_PURPOSE_OID_DDF))
663987da915Sopenharmony_ci						*df_type = DF_TYPE_DDF;
664987da915Sopenharmony_ci					else
665987da915Sopenharmony_ci						oid_index++;
666987da915Sopenharmony_ci				}
667987da915Sopenharmony_ci			} while (!err && (*df_type == DF_TYPE_UNKNOWN));
668987da915Sopenharmony_ci			if (*df_type == DF_TYPE_UNKNOWN) {
669987da915Sopenharmony_ci				/* End of list reached ? */
670987da915Sopenharmony_ci				if (err
671987da915Sopenharmony_ci				    == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
672987da915Sopenharmony_ci					ntfs_log_error("Key does not have an "
673987da915Sopenharmony_ci						"EFS purpose OID\n");
674987da915Sopenharmony_ci				else
675987da915Sopenharmony_ci					ntfs_log_error("Failed to get a key "
676987da915Sopenharmony_ci							"purpose OID : %s ",
677987da915Sopenharmony_ci							gnutls_strerror(err));
678987da915Sopenharmony_ci				goto err;
679987da915Sopenharmony_ci			}
680987da915Sopenharmony_ci			/* Return the thumbprint to the caller. */
681987da915Sopenharmony_ci			err = gnutls_x509_crt_get_fingerprint(crt,
682987da915Sopenharmony_ci					GNUTLS_DIG_SHA1, thumbprint, &tp_size);
683987da915Sopenharmony_ci			if (err) {
684987da915Sopenharmony_ci				ntfs_log_error("Failed to get thumbprint: "
685987da915Sopenharmony_ci						"%s\n", gnutls_strerror(err));
686987da915Sopenharmony_ci				goto err;
687987da915Sopenharmony_ci			}
688987da915Sopenharmony_ci			if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) {
689987da915Sopenharmony_ci				ntfs_log_error("Invalid thumbprint size %zd.  "
690987da915Sopenharmony_ci						"Should be %d.\n", tp_size,
691987da915Sopenharmony_ci						thumbprint_size);
692987da915Sopenharmony_ci				err = EINVAL;
693987da915Sopenharmony_ci				goto err;
694987da915Sopenharmony_ci			}
695987da915Sopenharmony_ci			have_thumbprint = TRUE;
696987da915Sopenharmony_ci			gnutls_x509_crt_deinit(crt);
697987da915Sopenharmony_ci			crt = NULL;
698987da915Sopenharmony_ci			break;
699987da915Sopenharmony_ci		default:
700987da915Sopenharmony_ci			/* We do not care about other types. */
701987da915Sopenharmony_ci			break;
702987da915Sopenharmony_ci		}
703987da915Sopenharmony_ci		gnutls_pkcs12_bag_deinit(bag);
704987da915Sopenharmony_ci	}
705987da915Sopenharmony_cierr:
706987da915Sopenharmony_ci	if (rsa_key && (err || *df_type == DF_TYPE_UNKNOWN ||
707987da915Sopenharmony_ci			!have_thumbprint)) {
708987da915Sopenharmony_ci		if (!err)
709987da915Sopenharmony_ci			ntfs_log_error("Key type or thumbprint not found, "
710987da915Sopenharmony_ci					"aborting.\n");
711987da915Sopenharmony_ci		ntfs_rsa_private_key_release(rsa_key);
712987da915Sopenharmony_ci		rsa_key = NULL;
713987da915Sopenharmony_ci	}
714987da915Sopenharmony_ci	if (crt)
715987da915Sopenharmony_ci		gnutls_x509_crt_deinit(crt);
716987da915Sopenharmony_ci	if (pkey)
717987da915Sopenharmony_ci		gnutls_x509_privkey_deinit(pkey);
718987da915Sopenharmony_ci	if (bag)
719987da915Sopenharmony_ci		gnutls_pkcs12_bag_deinit(bag);
720987da915Sopenharmony_ci	if (pkcs12)
721987da915Sopenharmony_ci		gnutls_pkcs12_deinit(pkcs12);
722987da915Sopenharmony_ci	return rsa_key;
723987da915Sopenharmony_ci}
724987da915Sopenharmony_ci
725987da915Sopenharmony_ci/**
726987da915Sopenharmony_ci * ntfs_buffer_reverse -
727987da915Sopenharmony_ci *
728987da915Sopenharmony_ci * This is a utility function for reversing the order of a buffer in place.
729987da915Sopenharmony_ci * Users of this function should be very careful not to sweep byte order
730987da915Sopenharmony_ci * problems under the rug.
731987da915Sopenharmony_ci */
732987da915Sopenharmony_cistatic inline void ntfs_buffer_reverse(u8 *buf, unsigned buf_size)
733987da915Sopenharmony_ci{
734987da915Sopenharmony_ci	unsigned i;
735987da915Sopenharmony_ci	u8 t;
736987da915Sopenharmony_ci
737987da915Sopenharmony_ci	for (i = 0; i < buf_size / 2; i++) {
738987da915Sopenharmony_ci		t = buf[i];
739987da915Sopenharmony_ci		buf[i] = buf[buf_size - i - 1];
740987da915Sopenharmony_ci		buf[buf_size - i - 1] = t;
741987da915Sopenharmony_ci	}
742987da915Sopenharmony_ci}
743987da915Sopenharmony_ci
744987da915Sopenharmony_ci#ifndef HAVE_STRNLEN
745987da915Sopenharmony_ci/**
746987da915Sopenharmony_ci * strnlen - strnlen is a gnu extension so emulate it if not present
747987da915Sopenharmony_ci */
748987da915Sopenharmony_cistatic size_t strnlen(const char *s, size_t maxlen)
749987da915Sopenharmony_ci{
750987da915Sopenharmony_ci	const char *p, *end;
751987da915Sopenharmony_ci
752987da915Sopenharmony_ci	/* Look for a '\0' character. */
753987da915Sopenharmony_ci	for (p = s, end = s + maxlen; p < end && *p; p++)
754987da915Sopenharmony_ci		;
755987da915Sopenharmony_ci	return p - s;
756987da915Sopenharmony_ci}
757987da915Sopenharmony_ci#endif /* ! HAVE_STRNLEN */
758987da915Sopenharmony_ci
759987da915Sopenharmony_ci/**
760987da915Sopenharmony_ci * ntfs_raw_fek_decrypt -
761987da915Sopenharmony_ci *
762987da915Sopenharmony_ci * Note: decrypting into the input buffer.
763987da915Sopenharmony_ci */
764987da915Sopenharmony_cistatic unsigned ntfs_raw_fek_decrypt(u8 *fek, u32 fek_size,
765987da915Sopenharmony_ci		ntfs_rsa_private_key rsa_key)
766987da915Sopenharmony_ci{
767987da915Sopenharmony_ci	gcry_mpi_t fek_mpi;
768987da915Sopenharmony_ci	gcry_sexp_t fek_sexp, fek_sexp2;
769987da915Sopenharmony_ci	gcry_error_t err;
770987da915Sopenharmony_ci	size_t size, padding;
771987da915Sopenharmony_ci
772987da915Sopenharmony_ci	/* Reverse the raw FEK. */
773987da915Sopenharmony_ci	ntfs_buffer_reverse(fek, fek_size);
774987da915Sopenharmony_ci	/* Convert the FEK to internal MPI format. */
775987da915Sopenharmony_ci	err = gcry_mpi_scan(&fek_mpi, GCRYMPI_FMT_USG, fek, fek_size, NULL);
776987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
777987da915Sopenharmony_ci		ntfs_log_error("Failed to convert file encryption key to "
778987da915Sopenharmony_ci				"internal MPI format: %s\n",
779987da915Sopenharmony_ci				gcry_strerror(err));
780987da915Sopenharmony_ci		return 0;
781987da915Sopenharmony_ci	}
782987da915Sopenharmony_ci	/* Create an internal S-expression from the FEK. */
783987da915Sopenharmony_ci	err = gcry_sexp_build(&fek_sexp, NULL,
784987da915Sopenharmony_ci			"(enc-val (flags) (rsa (a %m)))", fek_mpi);
785987da915Sopenharmony_ci	gcry_mpi_release(fek_mpi);
786987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
787987da915Sopenharmony_ci		ntfs_log_error("Failed to create internal S-expression of "
788987da915Sopenharmony_ci				"the file encryption key: %s\n",
789987da915Sopenharmony_ci				gcry_strerror(err));
790987da915Sopenharmony_ci		return 0;
791987da915Sopenharmony_ci	}
792987da915Sopenharmony_ci	/* Decrypt the FEK. */
793987da915Sopenharmony_ci	err = gcry_pk_decrypt(&fek_sexp2, fek_sexp, (gcry_sexp_t)rsa_key);
794987da915Sopenharmony_ci	gcry_sexp_release(fek_sexp);
795987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
796987da915Sopenharmony_ci		ntfs_log_error("Failed to decrypt the file encryption key: "
797987da915Sopenharmony_ci				"%s\n", gcry_strerror(err));
798987da915Sopenharmony_ci		return 0;
799987da915Sopenharmony_ci	}
800987da915Sopenharmony_ci	/* Extract the actual FEK from the decrypted raw S-expression. */
801987da915Sopenharmony_ci	fek_sexp = gcry_sexp_find_token(fek_sexp2, "value", 0);
802987da915Sopenharmony_ci	gcry_sexp_release(fek_sexp2);
803987da915Sopenharmony_ci	if (!fek_sexp) {
804987da915Sopenharmony_ci		ntfs_log_error("Failed to find the decrypted file encryption "
805987da915Sopenharmony_ci				"key in the internal S-expression.\n");
806987da915Sopenharmony_ci		return 0;
807987da915Sopenharmony_ci	}
808987da915Sopenharmony_ci	/* Convert the decrypted FEK S-expression into MPI format. */
809987da915Sopenharmony_ci	fek_mpi = gcry_sexp_nth_mpi(fek_sexp, 1, GCRYMPI_FMT_USG);
810987da915Sopenharmony_ci	gcry_sexp_release(fek_sexp);
811987da915Sopenharmony_ci	if (!fek_mpi) {
812987da915Sopenharmony_ci		ntfs_log_error("Failed to convert the decrypted file "
813987da915Sopenharmony_ci				"encryption key S-expression to internal MPI "
814987da915Sopenharmony_ci				"format.\n");
815987da915Sopenharmony_ci		return 0;
816987da915Sopenharmony_ci	}
817987da915Sopenharmony_ci	/* Convert the decrypted FEK from MPI format to binary data. */
818987da915Sopenharmony_ci	err = gcry_mpi_print(GCRYMPI_FMT_USG, fek, fek_size, &size, fek_mpi);
819987da915Sopenharmony_ci	gcry_mpi_release(fek_mpi);
820987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR || !size) {
821987da915Sopenharmony_ci		ntfs_log_error("Failed to convert decrypted file encryption "
822987da915Sopenharmony_ci				"key from internal MPI format to binary data: "
823987da915Sopenharmony_ci				"%s\n", gcry_strerror(err));
824987da915Sopenharmony_ci		return 0;
825987da915Sopenharmony_ci	}
826987da915Sopenharmony_ci	/*
827987da915Sopenharmony_ci	 * Finally, remove the PKCS#1 padding and return the size of the
828987da915Sopenharmony_ci	 * decrypted FEK.
829987da915Sopenharmony_ci	 */
830987da915Sopenharmony_ci	padding = strnlen((char *)fek, size) + 1;
831987da915Sopenharmony_ci	if (padding > size) {
832987da915Sopenharmony_ci		ntfs_log_error("Failed to remove PKCS#1 padding from "
833987da915Sopenharmony_ci				"decrypted file encryption key.\n");
834987da915Sopenharmony_ci		return 0;
835987da915Sopenharmony_ci	}
836987da915Sopenharmony_ci	size -= padding;
837987da915Sopenharmony_ci	memmove(fek, fek + padding, size);
838987da915Sopenharmony_ci	return size;
839987da915Sopenharmony_ci}
840987da915Sopenharmony_ci
841987da915Sopenharmony_ci/**
842987da915Sopenharmony_ci * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key
843987da915Sopenharmony_ci * @src:	source buffer containing 128-bit key
844987da915Sopenharmony_ci *
845987da915Sopenharmony_ci * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the
846987da915Sopenharmony_ci * out-whitening keys required to perform desx {de,en}cryption.
847987da915Sopenharmony_ci */
848987da915Sopenharmony_cistatic gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key,
849987da915Sopenharmony_ci		u64 *out_whitening, u64 *in_whitening)
850987da915Sopenharmony_ci{
851987da915Sopenharmony_ci	static const u8 *salt1 = (const u8*)"Dan Simon  ";
852987da915Sopenharmony_ci	static const u8 *salt2 = (const u8*)"Scott Field";
853987da915Sopenharmony_ci	static const int salt_len = 12;
854987da915Sopenharmony_ci	gcry_md_hd_t hd1, hd2;
855987da915Sopenharmony_ci	u32 *md;
856987da915Sopenharmony_ci	gcry_error_t err;
857987da915Sopenharmony_ci
858987da915Sopenharmony_ci	err = gcry_md_open(&hd1, GCRY_MD_MD5, 0);
859987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
860987da915Sopenharmony_ci		ntfs_log_error("Failed to open MD5 digest.\n");
861987da915Sopenharmony_ci		return err;
862987da915Sopenharmony_ci	}
863987da915Sopenharmony_ci	/* Hash the on-disk key. */
864987da915Sopenharmony_ci	gcry_md_write(hd1, src, 128 / 8);
865987da915Sopenharmony_ci	/* Copy the current hash for efficiency. */
866987da915Sopenharmony_ci	err = gcry_md_copy(&hd2, hd1);
867987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
868987da915Sopenharmony_ci		ntfs_log_error("Failed to copy MD5 digest object.\n");
869987da915Sopenharmony_ci		goto out;
870987da915Sopenharmony_ci	}
871987da915Sopenharmony_ci	/* Hash with the first salt and store the result. */
872987da915Sopenharmony_ci	gcry_md_write(hd1, salt1, salt_len);
873987da915Sopenharmony_ci	md = (u32*)gcry_md_read(hd1, 0);
874987da915Sopenharmony_ci	des_key[0] = md[0] ^ md[1];
875987da915Sopenharmony_ci	des_key[1] = md[2] ^ md[3];
876987da915Sopenharmony_ci	/* Hash with the second salt and store the result. */
877987da915Sopenharmony_ci	gcry_md_write(hd2, salt2, salt_len);
878987da915Sopenharmony_ci	md = (u32*)gcry_md_read(hd2, 0);
879987da915Sopenharmony_ci	*out_whitening = *(u64*)md;
880987da915Sopenharmony_ci	*in_whitening = *(u64*)(md + 2);
881987da915Sopenharmony_ci	gcry_md_close(hd2);
882987da915Sopenharmony_ciout:
883987da915Sopenharmony_ci	gcry_md_close(hd1);
884987da915Sopenharmony_ci	return err;
885987da915Sopenharmony_ci}
886987da915Sopenharmony_ci
887987da915Sopenharmony_ci/**
888987da915Sopenharmony_ci * ntfs_desx_decrypt
889987da915Sopenharmony_ci */
890987da915Sopenharmony_cistatic gcry_error_t ntfs_desx_decrypt(ntfs_fek *fek, u8 *outbuf,
891987da915Sopenharmony_ci				const u8 *inbuf)
892987da915Sopenharmony_ci{
893987da915Sopenharmony_ci	gcry_error_t err;
894987da915Sopenharmony_ci	u64 curr_blk;
895987da915Sopenharmony_ci	ntfs_desx_ctx *ctx = &fek->desx_ctx;
896987da915Sopenharmony_ci
897987da915Sopenharmony_ci	curr_blk = *(const u64*)inbuf;
898987da915Sopenharmony_ci	*(u64*)outbuf = curr_blk ^ ctx->out_whitening;
899987da915Sopenharmony_ci	err = gcry_cipher_encrypt(fek->gcry_cipher_hd, outbuf, 8, NULL, 0);
900987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR)
901987da915Sopenharmony_ci		ntfs_log_error("Des decryption failed (error 0x%x).\n", err);
902987da915Sopenharmony_ci	*(u64*)outbuf ^= ctx->in_whitening ^ ctx->prev_blk;
903987da915Sopenharmony_ci	ctx->prev_blk = curr_blk;
904987da915Sopenharmony_ci	return (err);
905987da915Sopenharmony_ci}
906987da915Sopenharmony_ci
907987da915Sopenharmony_ci/**
908987da915Sopenharmony_ci * ntfs_desx_encrypt
909987da915Sopenharmony_ci */
910987da915Sopenharmony_cistatic gcry_error_t ntfs_desx_encrypt(ntfs_fek *fek, u8 *outbuf,
911987da915Sopenharmony_ci				const u8 *inbuf)
912987da915Sopenharmony_ci{
913987da915Sopenharmony_ci	gcry_error_t err;
914987da915Sopenharmony_ci	ntfs_desx_ctx *ctx = &fek->desx_ctx;
915987da915Sopenharmony_ci
916987da915Sopenharmony_ci	*(u64*)outbuf = *(const u64*)inbuf ^ ctx->in_whitening ^ ctx->prev_blk;
917987da915Sopenharmony_ci	err = gcry_cipher_decrypt(fek->gcry_cipher_hd, outbuf, 8, NULL, 0);
918987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR)
919987da915Sopenharmony_ci		ntfs_log_error("Des decryption failed (error 0x%x).\n", err);
920987da915Sopenharmony_ci	*(u64*)outbuf ^= ctx->out_whitening;
921987da915Sopenharmony_ci	ctx->prev_blk = *(u64*)outbuf;
922987da915Sopenharmony_ci	return (err);
923987da915Sopenharmony_ci}
924987da915Sopenharmony_ci
925987da915Sopenharmony_ci//#define DO_CRYPTO_TESTS 1
926987da915Sopenharmony_ci
927987da915Sopenharmony_ci#ifdef DO_CRYPTO_TESTS
928987da915Sopenharmony_ci
929987da915Sopenharmony_ci/* Do not remove this test code from this file! AIA */
930987da915Sopenharmony_ci/**
931987da915Sopenharmony_ci * ntfs_desx_key_expand_test
932987da915Sopenharmony_ci */
933987da915Sopenharmony_cistatic BOOL ntfs_desx_key_expand_test(void)
934987da915Sopenharmony_ci{
935987da915Sopenharmony_ci	const u8 known_desx_on_disk_key[16] = {
936987da915Sopenharmony_ci		0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f,
937987da915Sopenharmony_ci		0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30
938987da915Sopenharmony_ci	};
939987da915Sopenharmony_ci	const u8 known_des_key[8] = {
940987da915Sopenharmony_ci		0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f,
941987da915Sopenharmony_ci	};
942987da915Sopenharmony_ci	const u8 known_out_whitening[8] = {
943987da915Sopenharmony_ci		0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d,
944987da915Sopenharmony_ci	};
945987da915Sopenharmony_ci	const u8 known_in_whitening[8] = {
946987da915Sopenharmony_ci		0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e
947987da915Sopenharmony_ci	};
948987da915Sopenharmony_ci	u64 test_out_whitening, test_in_whitening;
949987da915Sopenharmony_ci	union {
950987da915Sopenharmony_ci		u64 u64;
951987da915Sopenharmony_ci		u32 u32[2];
952987da915Sopenharmony_ci	} test_des_key;
953987da915Sopenharmony_ci	gcry_error_t err;
954987da915Sopenharmony_ci	BOOL res;
955987da915Sopenharmony_ci
956987da915Sopenharmony_ci	err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32,
957987da915Sopenharmony_ci			&test_out_whitening, &test_in_whitening);
958987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR)
959987da915Sopenharmony_ci		res = FALSE;
960987da915Sopenharmony_ci	else
961987da915Sopenharmony_ci		res = test_des_key.u64 == *(u64*)known_des_key &&
962987da915Sopenharmony_ci				test_out_whitening ==
963987da915Sopenharmony_ci				*(u64*)known_out_whitening &&
964987da915Sopenharmony_ci				test_in_whitening ==
965987da915Sopenharmony_ci				*(u64*)known_in_whitening;
966987da915Sopenharmony_ci	ntfs_log_error("Testing whether ntfs_desx_key_expand() works: %s\n",
967987da915Sopenharmony_ci			res ? "SUCCESS" : "FAILED");
968987da915Sopenharmony_ci	return res;
969987da915Sopenharmony_ci}
970987da915Sopenharmony_ci
971987da915Sopenharmony_ci/**
972987da915Sopenharmony_ci * ntfs_des_test
973987da915Sopenharmony_ci */
974987da915Sopenharmony_cistatic BOOL ntfs_des_test(void)
975987da915Sopenharmony_ci{
976987da915Sopenharmony_ci	const u8 known_des_key[8] = {
977987da915Sopenharmony_ci		0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f
978987da915Sopenharmony_ci	};
979987da915Sopenharmony_ci	const u8 known_des_encrypted_data[8] = {
980987da915Sopenharmony_ci		0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f
981987da915Sopenharmony_ci	};
982987da915Sopenharmony_ci	const u8 known_decrypted_data[8] = {
983987da915Sopenharmony_ci		0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09
984987da915Sopenharmony_ci	};
985987da915Sopenharmony_ci	u8 test_decrypted_data[8];
986987da915Sopenharmony_ci	int res;
987987da915Sopenharmony_ci	gcry_error_t err;
988987da915Sopenharmony_ci	gcry_cipher_hd_t gcry_cipher_hd;
989987da915Sopenharmony_ci
990987da915Sopenharmony_ci	err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES,
991987da915Sopenharmony_ci			GCRY_CIPHER_MODE_ECB, 0);
992987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
993987da915Sopenharmony_ci		ntfs_log_error("Failed to open des cipher (error 0x%x).\n",
994987da915Sopenharmony_ci				err);
995987da915Sopenharmony_ci		return FALSE;
996987da915Sopenharmony_ci	}
997987da915Sopenharmony_ci	err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key,
998987da915Sopenharmony_ci			sizeof(known_des_key));
999987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
1000987da915Sopenharmony_ci		ntfs_log_error("Failed to set des key (error 0x%x.\n", err);
1001987da915Sopenharmony_ci		gcry_cipher_close(gcry_cipher_hd);
1002987da915Sopenharmony_ci		return FALSE;
1003987da915Sopenharmony_ci	}
1004987da915Sopenharmony_ci	/*
1005987da915Sopenharmony_ci	 * Apply DES decryption (ntfs actually uses encryption when decrypting).
1006987da915Sopenharmony_ci	 */
1007987da915Sopenharmony_ci	err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data,
1008987da915Sopenharmony_ci			sizeof(test_decrypted_data), known_des_encrypted_data,
1009987da915Sopenharmony_ci			sizeof(known_des_encrypted_data));
1010987da915Sopenharmony_ci	gcry_cipher_close(gcry_cipher_hd);
1011987da915Sopenharmony_ci	if (err) {
1012987da915Sopenharmony_ci		ntfs_log_error("Failed to des decrypt test data (error "
1013987da915Sopenharmony_ci				"0x%x).\n", err);
1014987da915Sopenharmony_ci		return FALSE;
1015987da915Sopenharmony_ci	}
1016987da915Sopenharmony_ci	res = !memcmp(test_decrypted_data, known_decrypted_data,
1017987da915Sopenharmony_ci			sizeof(known_decrypted_data));
1018987da915Sopenharmony_ci	ntfs_log_error("Testing whether des decryption works: %s\n",
1019987da915Sopenharmony_ci			res ? "SUCCESS" : "FAILED");
1020987da915Sopenharmony_ci	return res;
1021987da915Sopenharmony_ci}
1022987da915Sopenharmony_ci
1023987da915Sopenharmony_ci#else /* !defined(DO_CRYPTO_TESTS) */
1024987da915Sopenharmony_ci
1025987da915Sopenharmony_ci/**
1026987da915Sopenharmony_ci * ntfs_desx_key_expand_test
1027987da915Sopenharmony_ci */
1028987da915Sopenharmony_cistatic inline BOOL ntfs_desx_key_expand_test(void)
1029987da915Sopenharmony_ci{
1030987da915Sopenharmony_ci	return TRUE;
1031987da915Sopenharmony_ci}
1032987da915Sopenharmony_ci
1033987da915Sopenharmony_ci/**
1034987da915Sopenharmony_ci * ntfs_des_test
1035987da915Sopenharmony_ci */
1036987da915Sopenharmony_cistatic inline BOOL ntfs_des_test(void)
1037987da915Sopenharmony_ci{
1038987da915Sopenharmony_ci	return TRUE;
1039987da915Sopenharmony_ci}
1040987da915Sopenharmony_ci
1041987da915Sopenharmony_ci#endif /* !defined(DO_CRYPTO_TESTS) */
1042987da915Sopenharmony_ci
1043987da915Sopenharmony_ci/**
1044987da915Sopenharmony_ci * ntfs_fek_import_from_raw
1045987da915Sopenharmony_ci */
1046987da915Sopenharmony_cistatic ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, unsigned fek_size)
1047987da915Sopenharmony_ci{
1048987da915Sopenharmony_ci	ntfs_fek *fek;
1049987da915Sopenharmony_ci	u32 key_size, wanted_key_size, gcry_algo;
1050987da915Sopenharmony_ci	int gcry_mode;
1051987da915Sopenharmony_ci	gcry_error_t err;
1052987da915Sopenharmony_ci	ntfs_desx_ctx *ctx;
1053987da915Sopenharmony_ci
1054987da915Sopenharmony_ci	key_size = le32_to_cpup((le32*) fek_buf);
1055987da915Sopenharmony_ci	ntfs_log_debug("key_size 0x%x\n", key_size);
1056987da915Sopenharmony_ci	if (key_size + 16 > fek_size) {
1057987da915Sopenharmony_ci		ntfs_log_debug("Invalid FEK.  It was probably decrypted with "
1058987da915Sopenharmony_ci				"the incorrect RSA key.");
1059987da915Sopenharmony_ci		errno = EINVAL;
1060987da915Sopenharmony_ci		return NULL;
1061987da915Sopenharmony_ci	}
1062987da915Sopenharmony_ci	fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) +
1063987da915Sopenharmony_ci			sizeof(gcry_cipher_hd_t));
1064987da915Sopenharmony_ci	if (!fek) {
1065987da915Sopenharmony_ci		errno = ENOMEM;
1066987da915Sopenharmony_ci		return NULL;
1067987da915Sopenharmony_ci	}
1068987da915Sopenharmony_ci	ctx = &fek->desx_ctx;
1069987da915Sopenharmony_ci	fek->alg_id = *(le32*)(fek_buf + 8);
1070987da915Sopenharmony_ci	//ntfs_log_debug("alg_id 0x%x\n", le32_to_cpu(fek->alg_id));
1071987da915Sopenharmony_ci	fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7);
1072987da915Sopenharmony_ci	memcpy(fek->key_data, fek_buf + 16, key_size);
1073987da915Sopenharmony_ci	fek->des_gcry_cipher_hd_ptr = NULL;
1074987da915Sopenharmony_ci	*(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) =
1075987da915Sopenharmony_ci			&fek->des_gcry_cipher_hd_ptr;
1076987da915Sopenharmony_ci	switch (fek->alg_id) {
1077987da915Sopenharmony_ci	case CALG_DESX:
1078987da915Sopenharmony_ci		wanted_key_size = 16;
1079987da915Sopenharmony_ci		gcry_algo = GCRY_CIPHER_DES;
1080987da915Sopenharmony_ci		gcry_mode = GCRY_CIPHER_MODE_ECB;
1081987da915Sopenharmony_ci		break;
1082987da915Sopenharmony_ci	case CALG_3DES:
1083987da915Sopenharmony_ci		wanted_key_size = 24;
1084987da915Sopenharmony_ci		gcry_algo = GCRY_CIPHER_3DES;
1085987da915Sopenharmony_ci		gcry_mode = GCRY_CIPHER_MODE_CBC;
1086987da915Sopenharmony_ci		break;
1087987da915Sopenharmony_ci	case CALG_AES_256:
1088987da915Sopenharmony_ci		wanted_key_size = 32;
1089987da915Sopenharmony_ci		gcry_algo = GCRY_CIPHER_AES256;
1090987da915Sopenharmony_ci		gcry_mode = GCRY_CIPHER_MODE_CBC;
1091987da915Sopenharmony_ci		break;
1092987da915Sopenharmony_ci	default:
1093987da915Sopenharmony_ci		wanted_key_size = 8;
1094987da915Sopenharmony_ci		gcry_algo = GCRY_CIPHER_DES;
1095987da915Sopenharmony_ci		gcry_mode = GCRY_CIPHER_MODE_CBC;
1096987da915Sopenharmony_ci		if (fek->alg_id == CALG_DES)
1097987da915Sopenharmony_ci			ntfs_log_error("DES is not supported at present\n");
1098987da915Sopenharmony_ci		else
1099987da915Sopenharmony_ci			ntfs_log_error("Unknown crypto algorithm 0x%x\n",
1100987da915Sopenharmony_ci					le32_to_cpu(fek->alg_id));
1101987da915Sopenharmony_ci		ntfs_log_error(".  Please email %s and say that you saw this "
1102987da915Sopenharmony_ci				"message.  We will then try to implement "
1103987da915Sopenharmony_ci				"support for this algorithm.\n", NTFS_DEV_LIST);
1104987da915Sopenharmony_ci		err = EOPNOTSUPP;
1105987da915Sopenharmony_ci		goto out;
1106987da915Sopenharmony_ci	}
1107987da915Sopenharmony_ci	if (key_size != wanted_key_size) {
1108987da915Sopenharmony_ci		ntfs_log_error("%s key of %u bytes but needed size is %u "
1109987da915Sopenharmony_ci				"bytes, assuming corrupt or incorrect key.  "
1110987da915Sopenharmony_ci				"Aborting.\n",
1111987da915Sopenharmony_ci				gcry_cipher_algo_name(gcry_algo),
1112987da915Sopenharmony_ci				(unsigned)key_size, (unsigned)wanted_key_size);
1113987da915Sopenharmony_ci		err = EIO;
1114987da915Sopenharmony_ci		goto out;
1115987da915Sopenharmony_ci	}
1116987da915Sopenharmony_ci	err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo,
1117987da915Sopenharmony_ci				gcry_mode, 0);
1118987da915Sopenharmony_ci
1119987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
1120987da915Sopenharmony_ci		ntfs_log_error("gcry_cipher_open() failed: %s\n",
1121987da915Sopenharmony_ci				gcry_strerror(err));
1122987da915Sopenharmony_ci		err = EINVAL;
1123987da915Sopenharmony_ci		goto out;
1124987da915Sopenharmony_ci	}
1125987da915Sopenharmony_ci	if (fek->alg_id == CALG_DESX) {
1126987da915Sopenharmony_ci		err = ntfs_desx_key_expand(fek->key_data, (u32*)ctx->des_key,
1127987da915Sopenharmony_ci				&ctx->out_whitening, &ctx->in_whitening);
1128987da915Sopenharmony_ci		if (err == GPG_ERR_NO_ERROR)
1129987da915Sopenharmony_ci			err = gcry_cipher_setkey(fek->gcry_cipher_hd,
1130987da915Sopenharmony_ci							ctx->des_key, 8);
1131987da915Sopenharmony_ci	} else {
1132987da915Sopenharmony_ci		err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data,
1133987da915Sopenharmony_ci							key_size);
1134987da915Sopenharmony_ci	}
1135987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
1136987da915Sopenharmony_ci		ntfs_log_error("gcry_cipher_setkey() failed: %s\n",
1137987da915Sopenharmony_ci				gcry_strerror(err));
1138987da915Sopenharmony_ci		gcry_cipher_close(fek->gcry_cipher_hd);
1139987da915Sopenharmony_ci		err = EINVAL;
1140987da915Sopenharmony_ci		goto out;
1141987da915Sopenharmony_ci	}
1142987da915Sopenharmony_ci	return fek;
1143987da915Sopenharmony_ciout:
1144987da915Sopenharmony_ci	free(fek);
1145987da915Sopenharmony_ci	errno = err;
1146987da915Sopenharmony_ci	return NULL;
1147987da915Sopenharmony_ci}
1148987da915Sopenharmony_ci
1149987da915Sopenharmony_ci/**
1150987da915Sopenharmony_ci * ntfs_fek_release
1151987da915Sopenharmony_ci */
1152987da915Sopenharmony_cistatic void ntfs_fek_release(ntfs_fek *fek)
1153987da915Sopenharmony_ci{
1154987da915Sopenharmony_ci	if (fek->des_gcry_cipher_hd_ptr)
1155987da915Sopenharmony_ci		gcry_cipher_close(*fek->des_gcry_cipher_hd_ptr);
1156987da915Sopenharmony_ci	gcry_cipher_close(fek->gcry_cipher_hd);
1157987da915Sopenharmony_ci	free(fek);
1158987da915Sopenharmony_ci}
1159987da915Sopenharmony_ci
1160987da915Sopenharmony_ci/**
1161987da915Sopenharmony_ci * ntfs_df_array_fek_get
1162987da915Sopenharmony_ci */
1163987da915Sopenharmony_cistatic ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array,
1164987da915Sopenharmony_ci		ntfs_rsa_private_key rsa_key, char *thumbprint,
1165987da915Sopenharmony_ci		int thumbprint_size)
1166987da915Sopenharmony_ci{
1167987da915Sopenharmony_ci	EFS_DF_HEADER *df_header;
1168987da915Sopenharmony_ci	EFS_DF_CREDENTIAL_HEADER *df_cred;
1169987da915Sopenharmony_ci	EFS_DF_CERT_THUMBPRINT_HEADER *df_cert;
1170987da915Sopenharmony_ci	u8 *fek_buf;
1171987da915Sopenharmony_ci	ntfs_fek *fek;
1172987da915Sopenharmony_ci	u32 df_count, fek_size;
1173987da915Sopenharmony_ci	unsigned i;
1174987da915Sopenharmony_ci
1175987da915Sopenharmony_ci	df_count = le32_to_cpu(df_array->df_count);
1176987da915Sopenharmony_ci	if (!df_count)
1177987da915Sopenharmony_ci		ntfs_log_error("There are no elements in the DF array.\n");
1178987da915Sopenharmony_ci	df_header = (EFS_DF_HEADER*)(df_array + 1);
1179987da915Sopenharmony_ci	for (i = 0; i < df_count; i++, df_header = (EFS_DF_HEADER*)(
1180987da915Sopenharmony_ci			(u8*)df_header + le32_to_cpu(df_header->df_length))) {
1181987da915Sopenharmony_ci		df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header +
1182987da915Sopenharmony_ci				le32_to_cpu(df_header->cred_header_offset));
1183987da915Sopenharmony_ci		if (df_cred->type != NTFS_CRED_TYPE_CERT_THUMBPRINT) {
1184987da915Sopenharmony_ci			ntfs_log_debug("Credential type is not certificate "
1185987da915Sopenharmony_ci					"thumbprint, skipping DF entry.\n");
1186987da915Sopenharmony_ci			continue;
1187987da915Sopenharmony_ci		}
1188987da915Sopenharmony_ci		df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred +
1189987da915Sopenharmony_ci				le32_to_cpu(
1190987da915Sopenharmony_ci				df_cred->cert_thumbprint_header_offset));
1191987da915Sopenharmony_ci		if ((int)le32_to_cpu(df_cert->thumbprint_size)
1192987da915Sopenharmony_ci						!= thumbprint_size) {
1193987da915Sopenharmony_ci			ntfs_log_error("Thumbprint size %d is not valid "
1194987da915Sopenharmony_ci					"(should be %d), skipping this DF "
1195987da915Sopenharmony_ci					"entry.\n",
1196987da915Sopenharmony_ci					le32_to_cpu(df_cert->thumbprint_size),
1197987da915Sopenharmony_ci					thumbprint_size);
1198987da915Sopenharmony_ci			continue;
1199987da915Sopenharmony_ci		}
1200987da915Sopenharmony_ci		if (memcmp((u8*)df_cert +
1201987da915Sopenharmony_ci				le32_to_cpu(df_cert->thumbprint_offset),
1202987da915Sopenharmony_ci				thumbprint, thumbprint_size)) {
1203987da915Sopenharmony_ci			ntfs_log_debug("Thumbprints do not match, skipping "
1204987da915Sopenharmony_ci					"this DF entry.\n");
1205987da915Sopenharmony_ci			continue;
1206987da915Sopenharmony_ci		}
1207987da915Sopenharmony_ci		/*
1208987da915Sopenharmony_ci		 * The thumbprints match so this is probably the DF entry
1209987da915Sopenharmony_ci		 * matching the RSA key.  Try to decrypt the FEK with it.
1210987da915Sopenharmony_ci		 */
1211987da915Sopenharmony_ci		fek_size = le32_to_cpu(df_header->fek_size);
1212987da915Sopenharmony_ci		fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset);
1213987da915Sopenharmony_ci		/* Decrypt the FEK.  Note: This is done in place. */
1214987da915Sopenharmony_ci		fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key);
1215987da915Sopenharmony_ci		if (fek_size) {
1216987da915Sopenharmony_ci			/* Convert the FEK to our internal format. */
1217987da915Sopenharmony_ci			fek = ntfs_fek_import_from_raw(fek_buf, fek_size);
1218987da915Sopenharmony_ci			if (fek)
1219987da915Sopenharmony_ci				return fek;
1220987da915Sopenharmony_ci			ntfs_log_error("Failed to convert the decrypted file "
1221987da915Sopenharmony_ci					"encryption key to internal format.\n");
1222987da915Sopenharmony_ci		} else
1223987da915Sopenharmony_ci			ntfs_log_error("Failed to decrypt the file "
1224987da915Sopenharmony_ci					"encryption key.\n");
1225987da915Sopenharmony_ci	}
1226987da915Sopenharmony_ci	return NULL;
1227987da915Sopenharmony_ci}
1228987da915Sopenharmony_ci
1229987da915Sopenharmony_ci/**
1230987da915Sopenharmony_ci * ntfs_inode_fek_get -
1231987da915Sopenharmony_ci */
1232987da915Sopenharmony_cistatic ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode,
1233987da915Sopenharmony_ci		ntfs_rsa_private_key rsa_key, char *thumbprint,
1234987da915Sopenharmony_ci		int thumbprint_size, NTFS_DF_TYPES df_type)
1235987da915Sopenharmony_ci{
1236987da915Sopenharmony_ci	EFS_ATTR_HEADER *efs;
1237987da915Sopenharmony_ci	EFS_DF_ARRAY_HEADER *df_array = NULL;
1238987da915Sopenharmony_ci	ntfs_fek *fek = NULL;
1239987da915Sopenharmony_ci
1240987da915Sopenharmony_ci	/* Obtain the $EFS contents. */
1241987da915Sopenharmony_ci	efs = ntfs_attr_readall(inode, AT_LOGGED_UTILITY_STREAM, EFS, 4, NULL);
1242987da915Sopenharmony_ci	if (!efs) {
1243987da915Sopenharmony_ci		ntfs_log_perror("Failed to read $EFS attribute");
1244987da915Sopenharmony_ci		return NULL;
1245987da915Sopenharmony_ci	}
1246987da915Sopenharmony_ci	/*
1247987da915Sopenharmony_ci	 * Depending on whether the key is a normal key or a data recovery key,
1248987da915Sopenharmony_ci	 * iterate through the DDF or DRF array, respectively.
1249987da915Sopenharmony_ci	 */
1250987da915Sopenharmony_ci	if (df_type == DF_TYPE_DDF) {
1251987da915Sopenharmony_ci		if (efs->offset_to_ddf_array)
1252987da915Sopenharmony_ci			df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs +
1253987da915Sopenharmony_ci					le32_to_cpu(efs->offset_to_ddf_array));
1254987da915Sopenharmony_ci		else
1255987da915Sopenharmony_ci			ntfs_log_error("There are no entries in the DDF "
1256987da915Sopenharmony_ci					"array.\n");
1257987da915Sopenharmony_ci	} else if (df_type == DF_TYPE_DRF) {
1258987da915Sopenharmony_ci		if (efs->offset_to_drf_array)
1259987da915Sopenharmony_ci			df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs +
1260987da915Sopenharmony_ci					le32_to_cpu(efs->offset_to_drf_array));
1261987da915Sopenharmony_ci		else
1262987da915Sopenharmony_ci			ntfs_log_error("There are no entries in the DRF "
1263987da915Sopenharmony_ci					"array.\n");
1264987da915Sopenharmony_ci	} else
1265987da915Sopenharmony_ci		ntfs_log_error("Invalid DF type.\n");
1266987da915Sopenharmony_ci	if (df_array)
1267987da915Sopenharmony_ci		fek = ntfs_df_array_fek_get(df_array, rsa_key, thumbprint,
1268987da915Sopenharmony_ci				thumbprint_size);
1269987da915Sopenharmony_ci	free(efs);
1270987da915Sopenharmony_ci	return fek;
1271987da915Sopenharmony_ci}
1272987da915Sopenharmony_ci
1273987da915Sopenharmony_ci/**
1274987da915Sopenharmony_ci * ntfs_fek_decrypt_sector
1275987da915Sopenharmony_ci */
1276987da915Sopenharmony_cistatic int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset)
1277987da915Sopenharmony_ci{
1278987da915Sopenharmony_ci	gcry_error_t err;
1279987da915Sopenharmony_ci
1280987da915Sopenharmony_ci	err = gcry_cipher_reset(fek->gcry_cipher_hd);
1281987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
1282987da915Sopenharmony_ci		ntfs_log_error("Failed to reset cipher: %s\n",
1283987da915Sopenharmony_ci				gcry_strerror(err));
1284987da915Sopenharmony_ci		return -1;
1285987da915Sopenharmony_ci	}
1286987da915Sopenharmony_ci	/*
1287987da915Sopenharmony_ci	 * Note: You may wonder why we are not calling gcry_cipher_setiv() here
1288987da915Sopenharmony_ci	 * instead of doing it by hand after the decryption.  The answer is
1289987da915Sopenharmony_ci	 * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give
1290987da915Sopenharmony_ci	 * it a length of 16 for AES256 so it does not like it.
1291987da915Sopenharmony_ci	 */
1292987da915Sopenharmony_ci	if (fek->alg_id == CALG_DESX) {
1293987da915Sopenharmony_ci		int k;
1294987da915Sopenharmony_ci
1295987da915Sopenharmony_ci		fek->desx_ctx.prev_blk = 0;
1296987da915Sopenharmony_ci		for (k=0; (k < 512) && (err == GPG_ERR_NO_ERROR); k+=8) {
1297987da915Sopenharmony_ci			err = ntfs_desx_decrypt(fek, &data[k], &data[k]);
1298987da915Sopenharmony_ci		}
1299987da915Sopenharmony_ci	} else
1300987da915Sopenharmony_ci		err = gcry_cipher_decrypt(fek->gcry_cipher_hd, data, 512, NULL, 0);
1301987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
1302987da915Sopenharmony_ci		ntfs_log_error("Decryption failed: %s\n", gcry_strerror(err));
1303987da915Sopenharmony_ci		return -1;
1304987da915Sopenharmony_ci	}
1305987da915Sopenharmony_ci	/* Apply the IV. */
1306987da915Sopenharmony_ci	if (fek->alg_id == CALG_AES_256) {
1307987da915Sopenharmony_ci		((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset);
1308987da915Sopenharmony_ci		((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset);
1309987da915Sopenharmony_ci	} else {
1310987da915Sopenharmony_ci		/* All other algos (Des, 3Des, DesX) use the same IV. */
1311987da915Sopenharmony_ci		((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset);
1312987da915Sopenharmony_ci	}
1313987da915Sopenharmony_ci	return 512;
1314987da915Sopenharmony_ci}
1315987da915Sopenharmony_ci
1316987da915Sopenharmony_ci/**
1317987da915Sopenharmony_ci * ntfs_fek_encrypt_sector
1318987da915Sopenharmony_ci */
1319987da915Sopenharmony_cistatic int ntfs_fek_encrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset)
1320987da915Sopenharmony_ci{
1321987da915Sopenharmony_ci	gcry_error_t err;
1322987da915Sopenharmony_ci
1323987da915Sopenharmony_ci	err = gcry_cipher_reset(fek->gcry_cipher_hd);
1324987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
1325987da915Sopenharmony_ci		ntfs_log_error("Failed to reset cipher: %s\n",
1326987da915Sopenharmony_ci				gcry_strerror(err));
1327987da915Sopenharmony_ci		return -1;
1328987da915Sopenharmony_ci	}
1329987da915Sopenharmony_ci	/*
1330987da915Sopenharmony_ci	 * Note: You may wonder why we are not calling gcry_cipher_setiv() here
1331987da915Sopenharmony_ci	 * instead of doing it by hand after the decryption.  The answer is
1332987da915Sopenharmony_ci	 * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give
1333987da915Sopenharmony_ci	 * it a length of 16 for AES256 so it does not like it.
1334987da915Sopenharmony_ci	 */
1335987da915Sopenharmony_ci	/* Apply the IV. */
1336987da915Sopenharmony_ci	if (fek->alg_id == CALG_AES_256) {
1337987da915Sopenharmony_ci		((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset);
1338987da915Sopenharmony_ci		((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset);
1339987da915Sopenharmony_ci	} else {
1340987da915Sopenharmony_ci		/* All other algos (Des, 3Des, DesX) use the same IV. */
1341987da915Sopenharmony_ci		((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset);
1342987da915Sopenharmony_ci	}
1343987da915Sopenharmony_ci	if (fek->alg_id == CALG_DESX) {
1344987da915Sopenharmony_ci		int k;
1345987da915Sopenharmony_ci
1346987da915Sopenharmony_ci		fek->desx_ctx.prev_blk = 0;
1347987da915Sopenharmony_ci		for (k=0; (k < 512) && (err == GPG_ERR_NO_ERROR); k+=8) {
1348987da915Sopenharmony_ci			err = ntfs_desx_encrypt(fek, &data[k], &data[k]);
1349987da915Sopenharmony_ci		}
1350987da915Sopenharmony_ci	} else
1351987da915Sopenharmony_ci		err = gcry_cipher_encrypt(fek->gcry_cipher_hd, data, 512, NULL, 0);
1352987da915Sopenharmony_ci	if (err != GPG_ERR_NO_ERROR) {
1353987da915Sopenharmony_ci		ntfs_log_error("Encryption failed: %s\n", gcry_strerror(err));
1354987da915Sopenharmony_ci		return -1;
1355987da915Sopenharmony_ci	}
1356987da915Sopenharmony_ci	return 512;
1357987da915Sopenharmony_ci}
1358987da915Sopenharmony_ci
1359987da915Sopenharmony_ci/**
1360987da915Sopenharmony_ci * ntfs_cat_decrypt - Decrypt the contents of an encrypted file to stdout.
1361987da915Sopenharmony_ci * @inode:	An encrypted file's inode structure, as obtained by
1362987da915Sopenharmony_ci * 		ntfs_inode_open().
1363987da915Sopenharmony_ci * @fek:	A file encryption key. As obtained by ntfs_inode_fek_get().
1364987da915Sopenharmony_ci */
1365987da915Sopenharmony_cistatic int ntfs_cat_decrypt(ntfs_inode *inode, ntfs_fek *fek)
1366987da915Sopenharmony_ci{
1367987da915Sopenharmony_ci	int bufsize = 512;
1368987da915Sopenharmony_ci	unsigned char *buffer;
1369987da915Sopenharmony_ci	ntfs_attr *attr;
1370987da915Sopenharmony_ci	s64 bytes_read, written, offset, total;
1371987da915Sopenharmony_ci	s64 old_data_size, old_initialized_size;
1372987da915Sopenharmony_ci	int i;
1373987da915Sopenharmony_ci
1374987da915Sopenharmony_ci	buffer = malloc(bufsize);
1375987da915Sopenharmony_ci	if (!buffer)
1376987da915Sopenharmony_ci		return 1;
1377987da915Sopenharmony_ci	attr = ntfs_attr_open(inode, AT_DATA, NULL, 0);
1378987da915Sopenharmony_ci	if (!attr) {
1379987da915Sopenharmony_ci		ntfs_log_error("Cannot cat a directory.\n");
1380987da915Sopenharmony_ci		free(buffer);
1381987da915Sopenharmony_ci		return 1;
1382987da915Sopenharmony_ci	}
1383987da915Sopenharmony_ci	total = attr->data_size;
1384987da915Sopenharmony_ci
1385987da915Sopenharmony_ci	// hack: make sure attr will not be commited to disk if you use this.
1386987da915Sopenharmony_ci	// clear the encrypted bit, otherwise the library won't allow reading.
1387987da915Sopenharmony_ci	NAttrClearEncrypted(attr);
1388987da915Sopenharmony_ci	// extend the size, we may need to read past the end of the stream.
1389987da915Sopenharmony_ci	old_data_size = attr->data_size;
1390987da915Sopenharmony_ci	old_initialized_size = attr->initialized_size;
1391987da915Sopenharmony_ci	attr->data_size = attr->initialized_size = attr->allocated_size;
1392987da915Sopenharmony_ci
1393987da915Sopenharmony_ci	offset = 0;
1394987da915Sopenharmony_ci	while (total > 0) {
1395987da915Sopenharmony_ci		bytes_read = ntfs_attr_pread(attr, offset, 512, buffer);
1396987da915Sopenharmony_ci		if (bytes_read == -1) {
1397987da915Sopenharmony_ci			ntfs_log_perror("ERROR: Couldn't read file");
1398987da915Sopenharmony_ci			break;
1399987da915Sopenharmony_ci		}
1400987da915Sopenharmony_ci		if (!bytes_read)
1401987da915Sopenharmony_ci			break;
1402987da915Sopenharmony_ci		if ((i = ntfs_fek_decrypt_sector(fek, buffer, offset)) <
1403987da915Sopenharmony_ci				bytes_read) {
1404987da915Sopenharmony_ci			ntfs_log_perror("ERROR: Couldn't decrypt all data!");
1405987da915Sopenharmony_ci			ntfs_log_error("%u/%lld/%lld/%lld\n", i,
1406987da915Sopenharmony_ci				(long long)bytes_read, (long long)offset,
1407987da915Sopenharmony_ci				(long long)total);
1408987da915Sopenharmony_ci			break;
1409987da915Sopenharmony_ci		}
1410987da915Sopenharmony_ci		if (bytes_read > total)
1411987da915Sopenharmony_ci			bytes_read = total;
1412987da915Sopenharmony_ci		written = fwrite(buffer, 1, bytes_read, stdout);
1413987da915Sopenharmony_ci		if (written != bytes_read) {
1414987da915Sopenharmony_ci			ntfs_log_perror("ERROR: Couldn't output all data!");
1415987da915Sopenharmony_ci			break;
1416987da915Sopenharmony_ci		}
1417987da915Sopenharmony_ci		offset += bytes_read;
1418987da915Sopenharmony_ci		total -= bytes_read;
1419987da915Sopenharmony_ci	}
1420987da915Sopenharmony_ci	attr->data_size = old_data_size;
1421987da915Sopenharmony_ci	attr->initialized_size = old_initialized_size;
1422987da915Sopenharmony_ci	NAttrSetEncrypted(attr);
1423987da915Sopenharmony_ci	ntfs_attr_close(attr);
1424987da915Sopenharmony_ci	free(buffer);
1425987da915Sopenharmony_ci	return 0;
1426987da915Sopenharmony_ci}
1427987da915Sopenharmony_ci
1428987da915Sopenharmony_ci/**
1429987da915Sopenharmony_ci * ntfs_feed_encrypt - Encrypt the contents of stdin to an encrypted file
1430987da915Sopenharmony_ci * @inode:	An encrypted file's inode structure, as obtained by
1431987da915Sopenharmony_ci * 		ntfs_inode_open().
1432987da915Sopenharmony_ci * @fek:	A file encryption key. As obtained by ntfs_inode_fek_get().
1433987da915Sopenharmony_ci */
1434987da915Sopenharmony_cistatic int ntfs_feed_encrypt(ntfs_inode *inode, ntfs_fek *fek)
1435987da915Sopenharmony_ci{
1436987da915Sopenharmony_ci	const int bufsize = 512;
1437987da915Sopenharmony_ci	unsigned char *buffer;
1438987da915Sopenharmony_ci	ntfs_attr *attr;
1439987da915Sopenharmony_ci	s64 bytes_read, written, offset, total;
1440987da915Sopenharmony_ci	unsigned char *b;
1441987da915Sopenharmony_ci	long val;
1442987da915Sopenharmony_ci	int count;
1443987da915Sopenharmony_ci	int i;
1444987da915Sopenharmony_ci
1445987da915Sopenharmony_ci	buffer = (unsigned char*)malloc(bufsize);
1446987da915Sopenharmony_ci	if (!buffer)
1447987da915Sopenharmony_ci		return 1;
1448987da915Sopenharmony_ci	attr = ntfs_attr_open(inode, AT_DATA, NULL, 0);
1449987da915Sopenharmony_ci	if (!attr) {
1450987da915Sopenharmony_ci		ntfs_log_error("Cannot feed into a directory.\n");
1451987da915Sopenharmony_ci		goto rejected;
1452987da915Sopenharmony_ci	}
1453987da915Sopenharmony_ci	total = 0;
1454987da915Sopenharmony_ci
1455987da915Sopenharmony_ci	if (!(attr->data_flags & ATTR_IS_ENCRYPTED)) {
1456987da915Sopenharmony_ci		ntfs_log_error("The data stream was not encrypted\n");
1457987da915Sopenharmony_ci		goto rejected;
1458987da915Sopenharmony_ci	}
1459987da915Sopenharmony_ci	inode->vol->efs_raw = TRUE;
1460987da915Sopenharmony_ci
1461987da915Sopenharmony_ci	if (ntfs_attr_truncate(attr, 0)) {
1462987da915Sopenharmony_ci		ntfs_log_error("Failed to truncate the data stream\n");
1463987da915Sopenharmony_ci		goto rejected;
1464987da915Sopenharmony_ci	}
1465987da915Sopenharmony_ci	offset = 0;
1466987da915Sopenharmony_ci	do {
1467987da915Sopenharmony_ci		bytes_read = fread(buffer, 1, bufsize, stdin);
1468987da915Sopenharmony_ci		if (bytes_read <= 0) {
1469987da915Sopenharmony_ci			if (bytes_read < 0)
1470987da915Sopenharmony_ci				ntfs_log_perror("ERROR: Couldn't read data");
1471987da915Sopenharmony_ci		} else {
1472987da915Sopenharmony_ci			if (bytes_read < bufsize) {
1473987da915Sopenharmony_ci				/* Fill with random data */
1474987da915Sopenharmony_ci				srandom((unsigned int)(sle64_to_cpu(
1475987da915Sopenharmony_ci					inode->last_data_change_time)
1476987da915Sopenharmony_ci					/100000000));
1477987da915Sopenharmony_ci				count = bufsize - bytes_read;
1478987da915Sopenharmony_ci				b = &buffer[bytes_read];
1479987da915Sopenharmony_ci				do {
1480987da915Sopenharmony_ci					val = random();
1481987da915Sopenharmony_ci					switch (count) {
1482987da915Sopenharmony_ci						default :
1483987da915Sopenharmony_ci							*b++ = val;
1484987da915Sopenharmony_ci							val >>= 8;
1485987da915Sopenharmony_ci							/* FALLTHRU */
1486987da915Sopenharmony_ci						case 3 :
1487987da915Sopenharmony_ci							*b++ = val;
1488987da915Sopenharmony_ci							val >>= 8;
1489987da915Sopenharmony_ci							/* FALLTHRU */
1490987da915Sopenharmony_ci						case 2 :
1491987da915Sopenharmony_ci							*b++ = val;
1492987da915Sopenharmony_ci							val >>= 8;
1493987da915Sopenharmony_ci							/* FALLTHRU */
1494987da915Sopenharmony_ci						case 1 :
1495987da915Sopenharmony_ci							*b++ = val;
1496987da915Sopenharmony_ci							val >>= 8;
1497987da915Sopenharmony_ci					}
1498987da915Sopenharmony_ci					count -= 4;
1499987da915Sopenharmony_ci				} while (count > 0);
1500987da915Sopenharmony_ci			}
1501987da915Sopenharmony_ci			if ((i = ntfs_fek_encrypt_sector(fek, buffer, offset))
1502987da915Sopenharmony_ci					< bufsize) {
1503987da915Sopenharmony_ci				ntfs_log_perror("ERROR: Couldn't encrypt all data!");
1504987da915Sopenharmony_ci				ntfs_log_error("%u/%lld/%lld/%lld\n", i,
1505987da915Sopenharmony_ci					(long long)bytes_read, (long long)offset,
1506987da915Sopenharmony_ci					(long long)total);
1507987da915Sopenharmony_ci				break;
1508987da915Sopenharmony_ci			}
1509987da915Sopenharmony_ci		written = ntfs_attr_pwrite(attr, offset, bufsize, buffer);
1510987da915Sopenharmony_ci		if (written != bufsize) {
1511987da915Sopenharmony_ci			ntfs_log_perror("ERROR: Couldn't output all data!");
1512987da915Sopenharmony_ci			break;
1513987da915Sopenharmony_ci		}
1514987da915Sopenharmony_ci		offset += bufsize;
1515987da915Sopenharmony_ci		total += bytes_read;
1516987da915Sopenharmony_ci		}
1517987da915Sopenharmony_ci	} while (bytes_read == bufsize);
1518987da915Sopenharmony_ci	ntfs_attr_truncate(attr, total);
1519987da915Sopenharmony_ci	inode->last_data_change_time = ntfs_current_time();
1520987da915Sopenharmony_ci	NAttrSetEncrypted(attr);
1521987da915Sopenharmony_ci	ntfs_attr_close(attr);
1522987da915Sopenharmony_ci	free(buffer);
1523987da915Sopenharmony_ci	return 0;
1524987da915Sopenharmony_cirejected :
1525987da915Sopenharmony_ci	free(buffer);
1526987da915Sopenharmony_ci	return (-1);
1527987da915Sopenharmony_ci}
1528987da915Sopenharmony_ci
1529987da915Sopenharmony_ci/**
1530987da915Sopenharmony_ci * main - Begin here
1531987da915Sopenharmony_ci *
1532987da915Sopenharmony_ci * Start from here.
1533987da915Sopenharmony_ci *
1534987da915Sopenharmony_ci * Return:  0  Success, the program worked
1535987da915Sopenharmony_ci *	    1  Error, something went wrong
1536987da915Sopenharmony_ci */
1537987da915Sopenharmony_ciint main(int argc, char *argv[])
1538987da915Sopenharmony_ci{
1539987da915Sopenharmony_ci	u8 *pfx_buf;
1540987da915Sopenharmony_ci	char *password;
1541987da915Sopenharmony_ci	ntfs_rsa_private_key rsa_key;
1542987da915Sopenharmony_ci	ntfs_volume *vol;
1543987da915Sopenharmony_ci	ntfs_inode *inode;
1544987da915Sopenharmony_ci	ntfs_fek *fek;
1545987da915Sopenharmony_ci	unsigned pfx_size;
1546987da915Sopenharmony_ci	int res;
1547987da915Sopenharmony_ci	NTFS_DF_TYPES df_type;
1548987da915Sopenharmony_ci	char thumbprint[NTFS_SHA1_THUMBPRINT_SIZE];
1549987da915Sopenharmony_ci
1550987da915Sopenharmony_ci	ntfs_log_set_handler(ntfs_log_handler_stderr);
1551987da915Sopenharmony_ci
1552987da915Sopenharmony_ci	res = parse_options(argc, argv);
1553987da915Sopenharmony_ci	if (res >= 0)
1554987da915Sopenharmony_ci		return (res);
1555987da915Sopenharmony_ci	utils_set_locale();
1556987da915Sopenharmony_ci
1557987da915Sopenharmony_ci	/* Initialize crypto in ntfs. */
1558987da915Sopenharmony_ci	if (ntfs_crypto_init()) {
1559987da915Sopenharmony_ci		ntfs_log_error("Failed to initialize crypto.  Aborting.\n");
1560987da915Sopenharmony_ci		return 1;
1561987da915Sopenharmony_ci	}
1562987da915Sopenharmony_ci	/* Load the PKCS#12 (.pfx) file containing the user's private key. */
1563987da915Sopenharmony_ci	if (ntfs_pkcs12_load_pfxfile(opts.keyfile, &pfx_buf, &pfx_size)) {
1564987da915Sopenharmony_ci		ntfs_log_error("Failed to load key file.  Aborting.\n");
1565987da915Sopenharmony_ci		ntfs_crypto_deinit();
1566987da915Sopenharmony_ci		return 1;
1567987da915Sopenharmony_ci	}
1568987da915Sopenharmony_ci	/* Ask the user for their password. */
1569987da915Sopenharmony_ci	password = getpass("Enter the password with which the private key was "
1570987da915Sopenharmony_ci			"encrypted: ");
1571987da915Sopenharmony_ci	if (!password) {
1572987da915Sopenharmony_ci		ntfs_log_perror("Failed to obtain user password");
1573987da915Sopenharmony_ci		free(pfx_buf);
1574987da915Sopenharmony_ci		ntfs_crypto_deinit();
1575987da915Sopenharmony_ci		return 1;
1576987da915Sopenharmony_ci	}
1577987da915Sopenharmony_ci	/* Obtain the user's private RSA key from the key file. */
1578987da915Sopenharmony_ci	rsa_key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, password,
1579987da915Sopenharmony_ci			thumbprint, sizeof(thumbprint), &df_type);
1580987da915Sopenharmony_ci	/* Destroy the password. */
1581987da915Sopenharmony_ci	memset(password, 0, strlen(password));
1582987da915Sopenharmony_ci	/* No longer need the pfx file contents. */
1583987da915Sopenharmony_ci	free(pfx_buf);
1584987da915Sopenharmony_ci	if (!rsa_key) {
1585987da915Sopenharmony_ci		ntfs_log_error("Failed to extract the private RSA key.\n");
1586987da915Sopenharmony_ci		ntfs_crypto_deinit();
1587987da915Sopenharmony_ci		return 1;
1588987da915Sopenharmony_ci	}
1589987da915Sopenharmony_ci	/* Mount the ntfs volume. */
1590987da915Sopenharmony_ci	vol = utils_mount_volume(opts.device,
1591987da915Sopenharmony_ci			(opts.encrypt ? 0 :  NTFS_MNT_RDONLY) |
1592987da915Sopenharmony_ci			(opts.force ? NTFS_MNT_RECOVER : 0));
1593987da915Sopenharmony_ci	if (!vol) {
1594987da915Sopenharmony_ci		ntfs_log_error("Failed to mount ntfs volume.  Aborting.\n");
1595987da915Sopenharmony_ci		ntfs_rsa_private_key_release(rsa_key);
1596987da915Sopenharmony_ci		ntfs_crypto_deinit();
1597987da915Sopenharmony_ci		return 1;
1598987da915Sopenharmony_ci	}
1599987da915Sopenharmony_ci	/* Open the encrypted ntfs file. */
1600987da915Sopenharmony_ci	if (opts.inode != -1)
1601987da915Sopenharmony_ci		inode = ntfs_inode_open(vol, opts.inode);
1602987da915Sopenharmony_ci	else
1603987da915Sopenharmony_ci		inode = ntfs_pathname_to_inode(vol, NULL, opts.file);
1604987da915Sopenharmony_ci	if (!inode) {
1605987da915Sopenharmony_ci		ntfs_log_error("Failed to open encrypted file.  Aborting.\n");
1606987da915Sopenharmony_ci		ntfs_umount(vol, FALSE);
1607987da915Sopenharmony_ci		ntfs_rsa_private_key_release(rsa_key);
1608987da915Sopenharmony_ci		ntfs_crypto_deinit();
1609987da915Sopenharmony_ci		return 1;
1610987da915Sopenharmony_ci	}
1611987da915Sopenharmony_ci	/* Obtain the file encryption key of the encrypted file. */
1612987da915Sopenharmony_ci	fek = ntfs_inode_fek_get(inode, rsa_key, thumbprint,
1613987da915Sopenharmony_ci			sizeof(thumbprint), df_type);
1614987da915Sopenharmony_ci	ntfs_rsa_private_key_release(rsa_key);
1615987da915Sopenharmony_ci	if (fek) {
1616987da915Sopenharmony_ci		if (opts.encrypt)
1617987da915Sopenharmony_ci			res = ntfs_feed_encrypt(inode, fek);
1618987da915Sopenharmony_ci		else
1619987da915Sopenharmony_ci			res = ntfs_cat_decrypt(inode, fek);
1620987da915Sopenharmony_ci		ntfs_fek_release(fek);
1621987da915Sopenharmony_ci	} else {
1622987da915Sopenharmony_ci		ntfs_log_error("Failed to obtain file encryption key.  "
1623987da915Sopenharmony_ci				"Aborting.\n");
1624987da915Sopenharmony_ci		res = 1;
1625987da915Sopenharmony_ci	}
1626987da915Sopenharmony_ci	ntfs_inode_close(inode);
1627987da915Sopenharmony_ci	ntfs_umount(vol, FALSE);
1628987da915Sopenharmony_ci	ntfs_crypto_deinit();
1629987da915Sopenharmony_ci	return res;
1630987da915Sopenharmony_ci}
1631