From 640573bf12e26e1c1d553b60e19c4073db620c18 Mon Sep 17 00:00:00 2001 From: antona Date: Fri, 5 Aug 2005 23:41:52 +0000 Subject: [PATCH] Finish next stage in ntfsdecrypt evolution. It is now almost finished in that it fully works to decrypt any ntfs enecrypted file both from Linux and Windows. All you need to supply is your private key in form of a .prx file as exported for example by the cypher command in XP SP2 or otherwise as exported by the management console's certificate manager plugin. --- ntfsprogs/Makefile.am | 4 +- ntfsprogs/decrypt.c | 803 ------------------------------ ntfsprogs/decrypt.h | 50 -- ntfsprogs/ntfsdecrypt.c | 1041 +++++++++++++++++++++++++++++++++------ 4 files changed, 906 insertions(+), 992 deletions(-) delete mode 100644 ntfsprogs/decrypt.c delete mode 100644 ntfsprogs/decrypt.h diff --git a/ntfsprogs/Makefile.am b/ntfsprogs/Makefile.am index d2abe6b2..f73d15a0 100644 --- a/ntfsprogs/Makefile.am +++ b/ntfsprogs/Makefile.am @@ -118,9 +118,9 @@ ntfsdump_logfile_LDADD = $(AM_LIBS) ntfsdump_logfile_LDFLAGS= $(AM_LFLAGS) if ENABLE_CRYPTO -ntfsdecrypt_SOURCES = ntfsdecrypt.c decrypt.c decrypt.h utils.c utils.h +ntfsdecrypt_SOURCES = ntfsdecrypt.c utils.c utils.h ntfsdecrypt_LDADD = $(AM_LIBS) -ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) -lgcrypt +ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) -lgcrypt -lgnutls endif # Extra targets diff --git a/ntfsprogs/decrypt.c b/ntfsprogs/decrypt.c deleted file mode 100644 index 0e8a8f4c..00000000 --- a/ntfsprogs/decrypt.c +++ /dev/null @@ -1,803 +0,0 @@ -/* - * decrypt.c - $EFS decryption routines. Part of the Linux-NTFS project. - * - * Copyright (c) 2005 Yuval Fledel - * Copyright (c) 2005 Anton Altaparmakov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program (in the main directory of the Linux-NTFS distribution - * in the file COPYING); if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include -#include -#include - -#include "decrypt.h" - -#ifdef __CYGWIN__ -#define _WIN32_WINNT 0x501 -#define WINVER 0x501 - -#include -#include - -/* Missing cygwin macros */ -#ifndef CERT_SYSTEM_STORE_CURRENT_USER -#define CERT_SYSTEM_STORE_CURRENT_USER 0x00010000 -#endif - -#ifndef CERT_CLOSE_STORE_CHECK_FLAG -#define CERT_CLOSE_STORE_CHECK_FLAG 2 -#endif - -#ifndef CRYPT_ACQUIRE_CACHE_FLAG -#define CRYPT_ACQUIRE_CACHE_FLAG 1 -#endif - -/* Windows 2k+ imports. */ -typedef BOOL(WINAPI *LPFN_CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT, - DWORD, void *, HCRYPTPROV *, DWORD *, BOOL *); -typedef BOOL(WINAPI *LPFN_CertCloseStore)(HCERTSTORE, DWORD); -typedef PCCERT_CONTEXT(WINAPI *LPFN_CertFindCertificateInStore)(HCERTSTORE, - DWORD, DWORD, DWORD, const void *, PCCERT_CONTEXT); -typedef BOOL(WINAPI *LPFN_CertFreeCertificateContext)(PCCERT_CONTEXT); -typedef HCERTSTORE(WINAPI *LPFN_CertOpenStore)(LPCSTR, DWORD, HCRYPTPROV, - DWORD, const void *); - -// NT4SP3+ WINME or 95+ w/ IE5+ -static LPFN_CryptAcquireCertificatePrivateKey - fnCryptAcquireCertificatePrivateKey; -// osr2+ NT4SP3+ or NT4 w/ IE3.02: -static LPFN_CertCloseStore fnCertCloseStore; -static LPFN_CertFindCertificateInStore fnCertFindCertificateInStore; -static LPFN_CertFreeCertificateContext fnCertFreeCertificateContext; -static LPFN_CertOpenStore fnCertOpenStore; - -/* Global variable: Handle to crypt32.dll */ -static HMODULE hCrypt32 = INVALID_HANDLE_VALUE; - -#else /* !defined(__CYGWIN__) */ - -#include -#include -#define CALG_DES (0x6601) -/* If not one of the below three, fall back to standard Des. */ -#define CALG_3DES (0x6603) -#define CALG_DESX (0x6604) -#define CALG_AES_256 (0x6610) - -#endif /* !defined(__CYGWIN__) */ - -/* This must be after windows.h include. */ -#include "types.h" - -typedef struct { -#ifdef __CYGWIN__ - HCERTSTORE hSystemStore; -#else /* !defined(__CYGWIN__) */ - int nothing; /* unused */ -#endif /* !defined(__CYGWIN__) */ -} NTFS_DECRYPT_USER_KEY_SESSION; - -typedef struct { - gcry_sexp_t sexp_key; // the user's RSA key. -} NTFS_DECRYPT_USER_KEY; - -typedef struct { - u8 *key_data; - u32 alg_id; - gcry_cipher_hd_t gcry_cipher_hd; - gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; -} NTFS_DECRYPT_DATA_KEY; - -/* DESX-MS128 implementation for libgcrypt. */ -static gcry_module_t ntfs_desx_module; -static int ntfs_desx_algorithm_id = -1; -static int ntfs_desx_module_count; - -typedef struct { - u64 in_whitening, out_whitening; - gcry_cipher_hd_t gcry_cipher_hd; -} ntfs_desx_ctx; - -#ifdef __CYGWIN__ -static int cryptoAPI_init_imports(void) -{ - if (hCrypt32 == INVALID_HANDLE_VALUE) - hCrypt32 = LoadLibrary("crypt32.dll"); - if (!fnCryptAcquireCertificatePrivateKey) - fnCryptAcquireCertificatePrivateKey = - (LPFN_CryptAcquireCertificatePrivateKey) - GetProcAddress(hCrypt32, - "CryptAcquireCertificatePrivateKey"); - if (!fnCertCloseStore) - fnCertCloseStore = (LPFN_CertCloseStore) - GetProcAddress(hCrypt32, "CertCloseStore"); - if (!fnCertFindCertificateInStore) - fnCertFindCertificateInStore = (LPFN_CertFindCertificateInStore) - GetProcAddress(hCrypt32, - "CertFindCertificateInStore"); - if (!fnCertFreeCertificateContext) - fnCertFreeCertificateContext = (LPFN_CertFreeCertificateContext) - GetProcAddress(hCrypt32, - "CertFreeCertificateContext"); - if (!fnCertOpenStore) - fnCertOpenStore = (LPFN_CertOpenStore)GetProcAddress(hCrypt32, - "CertOpenStore"); - return fnCryptAcquireCertificatePrivateKey && fnCertCloseStore && - fnCertFindCertificateInStore && - fnCertFreeCertificateContext && fnCertOpenStore; -} -#endif /* defined(__CYGWIN__) */ - -ntfs_decrypt_user_key_session *ntfs_decrypt_user_key_session_open(void) -{ - ntfs_decrypt_user_key_session *session; -#ifdef __CYGWIN__ - HCERTSTORE hSystemStore; - - /* - * FIXME: This really needs locking and reference counting so it is - * safe from races. - */ - if (!cryptoAPI_init_imports()) { - fprintf(stderr, "Some imports do not exist.\n"); - errno = EINVAL; - return NULL; - } - if (!(hSystemStore = fnCertOpenStore(((LPCSTR)CERT_STORE_PROV_SYSTEM), - 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"MY"))) { - fprintf(stderr, "Could not open system store.\n"); - errno = EINVAL; - return NULL; - } -#endif /* defined(__CYGWIN__) */ - session = malloc(sizeof(NTFS_DECRYPT_USER_KEY_SESSION)); -#ifdef __CYGWIN__ - ((NTFS_DECRYPT_USER_KEY_SESSION*)session)->hSystemStore = hSystemStore; -#endif /* defined(__CYGWIN__) */ - gcry_control(GCRYCTL_DISABLE_SECMEM, 0); - return session; -} - -void ntfs_decrypt_user_key_session_close(ntfs_decrypt_user_key_session *session) -{ -#ifdef __CYGWIN__ - HMODULE tmp; - - if (((NTFS_DECRYPT_USER_KEY_SESSION*)session)->hSystemStore) - fnCertCloseStore(((NTFS_DECRYPT_USER_KEY_SESSION*)session)-> - hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG); - /* - * FIXME: This really needs locking and reference counting so it is - * safe from races. - */ - tmp = hCrypt32; - hCrypt32 = INVALID_HANDLE_VALUE; - FreeLibrary(tmp); -#endif /* defined(__CYGWIN__) */ - free(session); -} - -/** - * reverse_buffer - - * - * This is a utility function for reversing the order of a buffer in place. - * Users of this function should be very careful not to sweep byte order - * problems under the rug. - */ -static inline void reverse_buffer(unsigned char *buf, unsigned buf_size) -{ - unsigned char t; - unsigned i; - - for (i = 0; i < buf_size / 2; i++) { - t = buf[i]; - buf[i] = buf[buf_size - i - 1]; - buf[buf_size - i - 1] = t; - } -} - -ntfs_decrypt_user_key *ntfs_decrypt_user_key_open( - ntfs_decrypt_user_key_session *session __attribute__((unused)), - unsigned char *thumb_print __attribute__((unused)), - unsigned thumb_size __attribute__((unused))) -{ -#ifdef __CYGWIN__ - CRYPT_HASH_BLOB hash_blob; - HCRYPTPROV hCryptProv; - PCCERT_CONTEXT pCert; - BOOL fCallerFreeProv; - HCRYPTKEY hCryptKey; - ntfs_decrypt_user_key *key; - DWORD dwKeySpec; - DWORD key_size; - BYTE key_blob[1000]; - RSAPUBKEY *rsa_pub_key; - gcry_ac_handle_t gcry_handle; - unsigned char *mpi_data; - gcry_mpi_t n, e, d, p, q, u; - gcry_sexp_t sexp_key; - gcry_error_t err; - size_t size; - int rc; - - hash_blob.cbData = thumb_size; - hash_blob.pbData = thumb_print; - - if (!(pCert = fnCertFindCertificateInStore( - ((NTFS_DECRYPT_USER_KEY_SESSION*)session)->hSystemStore, - (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING), 0, - CERT_FIND_HASH, &hash_blob, NULL))) { - fprintf(stderr, "Could not find cert in store.\n"); - goto decrypt_key_open_err; - } - dwKeySpec = AT_KEYEXCHANGE; - if (!fnCryptAcquireCertificatePrivateKey(pCert, - CRYPT_ACQUIRE_CACHE_FLAG, NULL, &hCryptProv, - &dwKeySpec, &fCallerFreeProv)) { - fprintf(stderr, "Could not aquire private key from cert.\n"); - goto decrypt_key_open_err; - } - if (!CryptGetUserKey(hCryptProv, AT_KEYEXCHANGE, &hCryptKey)) { - fprintf(stderr, "Could not aquire user key.\n"); - goto decrypt_key_open_err; - } - key_size = sizeof(key_blob); - if (!CryptExportKey(hCryptKey, 0, PRIVATEKEYBLOB, 0, key_blob, - &key_size)) { - fprintf(stderr, "Could not export key: Error 0x%x\n", - (unsigned)GetLastError()); - errno = EINVAL; - return NULL; - } - CryptDestroyKey(hCryptKey); - rsa_pub_key = (RSAPUBKEY*)(key_blob + sizeof(PUBLICKEYSTRUC)); - if ((err = gcry_ac_open(&gcry_handle, GCRY_AC_RSA, 0))) { - fprintf(stderr, "Could not init gcrypt handle\n"); - errno = EINVAL; - return NULL; - } - gcry_control(GCRYCTL_DISABLE_SECMEM, 0); - e = gcry_mpi_set_ui(NULL, rsa_pub_key->pubexp); - mpi_data = (key_blob + 0x14); - size = rsa_pub_key->bitlen / 8; - reverse_buffer(mpi_data, size); - if ((rc = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, mpi_data, size, &size))) - fprintf(stderr, "error scanning n.\n"); - mpi_data += (rsa_pub_key->bitlen / 8); - size = rsa_pub_key->bitlen / 16; - reverse_buffer(mpi_data, size); - if ((rc = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, mpi_data, size, &size))) - fprintf(stderr, "error scanning p.\n"); - mpi_data += (rsa_pub_key->bitlen / 16); - size = rsa_pub_key->bitlen / 16; - reverse_buffer(mpi_data, size); - if ((rc = gcry_mpi_scan(&p, GCRYMPI_FMT_USG, mpi_data, size, &size))) - fprintf(stderr, "error scanning q.\n"); - mpi_data += (rsa_pub_key->bitlen / 16) * 3; - size = rsa_pub_key->bitlen / 16; - reverse_buffer(mpi_data, size); - if ((rc = gcry_mpi_scan(&u, GCRYMPI_FMT_USG, mpi_data, size, &size))) - fprintf(stderr, "error scanning u.\n"); - mpi_data += (rsa_pub_key->bitlen / 16); - size = rsa_pub_key->bitlen / 8; - reverse_buffer(mpi_data, size); - if ((rc = gcry_mpi_scan(&d, GCRYMPI_FMT_USG, mpi_data, size, &size))) - fprintf(stderr, "error scanning d.\n"); - sexp_key = NULL; - if ((rc = gcry_sexp_build(&sexp_key, NULL, "(private-key (rsa (n %m) " - "(e %m) (d %m) (p %m) (q %m) (u %m)))", n, e, d, p, q, - u))) { - fprintf(stderr, "Could build sexp from data, (error = 0x%x)\n", - rc); - errno = EINVAL; - return NULL; - } - if ((key = (ntfs_decrypt_user_key*) - malloc(sizeof(NTFS_DECRYPT_USER_KEY)))) - ((NTFS_DECRYPT_USER_KEY*)key)->sexp_key = sexp_key; - // todo: release all - return key; -decrypt_key_open_err: - if (hCryptKey) - CryptDestroyKey(hCryptKey); - if (pCert) - fnCertFreeCertificateContext(pCert); - errno = EINVAL; -#else /* !defined(__CYGWIN__) */ - errno = EOPNOTSUPP; -#endif /* !defined(__CYGWIN__) */ - return NULL; -} - -void ntfs_decrypt_user_key_close(ntfs_decrypt_user_key *key) -{ - gcry_sexp_release(((NTFS_DECRYPT_USER_KEY*)key)->sexp_key); - free(key); -} - -/** - * warning: decrypting into the input buffer! - */ -unsigned ntfs_decrypt_user_key_decrypt(ntfs_decrypt_user_key *key, - unsigned char *data, unsigned data_size) -{ - gcry_sexp_t sexp_plain_data, sexp_enc_data; - gcry_ac_handle_t gcry_handle; - gcry_mpi_t mpi_buf; - gcry_ac_data_t in; - gcry_error_t err; - unsigned size, padding_length, i; - int rc; - - if ((err = gcry_ac_open(&gcry_handle, GCRY_AC_RSA, 0))) { - fprintf(stderr, "Could not init gcrypt handle\n"); - errno = EINVAL; - return 0; - } - if ((rc = gcry_ac_data_new(&in))) - fprintf(stderr, "error allocating 'in'.\n"); - reverse_buffer(data, data_size); - size = data_size; - if ((rc = gcry_mpi_scan(&mpi_buf, GCRYMPI_FMT_USG, data, - (size_t)data_size, &size))) - fprintf(stderr, "error scanning 'in'.\n"); - if ((rc = gcry_sexp_build(&sexp_enc_data, &size, "(enc-val (flags) " - "(rsa (a %m)))", mpi_buf))) { - fprintf(stderr, "Could build sexp from data, (error = 0x%x)\n", - rc); - errno = EINVAL; - return 0; - } - if ((rc = gcry_pk_decrypt(&sexp_plain_data, sexp_enc_data, - ((NTFS_DECRYPT_USER_KEY*)key)->sexp_key))) { - fprintf(stderr, "Could not decrypt fek via s-exp, (error = " - "0x%x)\n", rc); - errno = EINVAL; - return 0; - } - sexp_plain_data = gcry_sexp_find_token(sexp_plain_data, "value", 0); - if (!mpi_buf) { - fprintf(stderr, "Could find value in s-exp, (error = 0x%x)\n", - rc); - errno = EINVAL; - return 0; - } - mpi_buf = gcry_sexp_nth_mpi(sexp_plain_data, 1, GCRYMPI_FMT_USG); - if ((rc = gcry_mpi_print(GCRYMPI_FMT_USG, data, data_size, &size, - mpi_buf))) { - fprintf(stderr, "Could copy decrypted data back, (error = " - "0x%x)\n", rc); - errno = EINVAL; - return 0; - } - // remove the pkcs1 padding - for (padding_length = 1; (padding_length < size) && - data[padding_length]; padding_length++) - ; - padding_length++; - // todo: should memcpy fit? (overlapping) - for (i = 0; i + padding_length < size; i++) - data[i] = data[padding_length + i]; - // todo: mpi_buf->data - // todo: release all - gcry_ac_data_destroy(in); - return size - padding_length; -} - -#if 0 -// This is the old code based on OpenSSL. Please do not remove it. AIA - -#include - -/** - * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key - * @src: source buffer containing 128-bit key - * - * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the - * out-whitening keys required to perform desx {de,en}cryption. - */ -static void ntfs_desx_key_expand(const u8 *src, u32 *des_key, - u64 *out_whitening, u64 *in_whitening) -{ - static const int salt_len = 12; - static const u8 *salt1 = "Dan Simon "; - static const u8 *salt2 = "Scott Field"; - u32 md[4]; - MD5_CTX ctx1, ctx2; - - MD5_Init(&ctx1); - - /* Hash the on-disk key. */ - MD5_Update(&ctx1, src, 128 / 8); - memcpy(&ctx2, &ctx1, sizeof(ctx1)); - - /* Hash with the first salt and store the result. */ - MD5_Update(&ctx1, salt1, salt_len); - MD5_Final((u8*)md, &ctx1); - des_key[0] = md[0] ^ md[1]; - des_key[1] = md[2] ^ md[3]; - - /* Hash with the second salt and store the result. */ - MD5_Update(&ctx2, salt2, salt_len); - MD5_Final((u8*)md, &ctx2); - *out_whitening = *(u64*)md; - *in_whitening = *(u64*)(md + 2); -} -#endif - -/** - * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key - * @src: source buffer containing 128-bit key - * - * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the - * out-whitening keys required to perform desx {de,en}cryption. - */ -static gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key, - u64 *out_whitening, u64 *in_whitening) -{ - static const u8 *salt1 = "Dan Simon "; - static const u8 *salt2 = "Scott Field"; - static const int salt_len = 12; - gcry_md_hd_t hd1, hd2; - u32 *md; - gcry_error_t err; - - err = gcry_md_open(&hd1, GCRY_MD_MD5, 0); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to open MD5 digest.\n"); - return err; - } - /* Hash the on-disk key. */ - gcry_md_write(hd1, src, 128 / 8); - /* Copy the current hash for efficiency. */ - err = gcry_md_copy(&hd2, hd1); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to copy MD5 digest object.\n"); - goto out; - } - /* Hash with the first salt and store the result. */ - gcry_md_write(hd1, salt1, salt_len); - md = (u32*)gcry_md_read(hd1, 0); - des_key[0] = md[0] ^ md[1]; - des_key[1] = md[2] ^ md[3]; - /* Hash with the second salt and store the result. */ - gcry_md_write(hd2, salt2, salt_len); - md = (u32*)gcry_md_read(hd2, 0); - *out_whitening = *(u64*)md; - *in_whitening = *(u64*)(md + 2); - gcry_md_close(hd2); -out: - gcry_md_close(hd1); - return err; -} - -/** - * ntfs_desx_setkey - libgcrypt set_key implementation for DES-X-MS128 - * @context: pointer to a variable of type ntfs_desx_ctx - * @key: the 128 bit DES-X-MS128 key, concated with the DES handle - * @keylen: must always be 16 - * - * This is the libgcrypt set_key implementation for DES-X-MS128. - */ -static gcry_err_code_t ntfs_desx_setkey(void *context, const u8 *key, - unsigned keylen) -{ - ntfs_desx_ctx *ctx = context; - gcry_error_t err; - u8 des_key[8]; - - if (keylen != 16) { - fprintf(stderr, "Key length for desx must be 16.\n"); - return GPG_ERR_INV_KEYLEN; - } - err = gcry_cipher_open(&ctx->gcry_cipher_hd, GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_ECB, 0); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to open des cipher (error 0x%x).\n", - err); - return err; - } - err = ntfs_desx_key_expand(key, (u32*)des_key, &ctx->out_whitening, - &ctx->in_whitening); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to expand desx key (error 0x%x).\n", - err); - gcry_cipher_close(ctx->gcry_cipher_hd); - return err; - } - err = gcry_cipher_setkey(ctx->gcry_cipher_hd, des_key, sizeof(des_key)); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to set des key (error 0x%x).\n", err); - gcry_cipher_close(ctx->gcry_cipher_hd); - return err; - } - /* - * Take a note of the ctx->gcry_cipher_hd since we need to close it at - * ntfs_decrypt_data_key_close() time. - */ - **(gcry_cipher_hd_t***)(key + ((keylen + 7) & ~7)) = - &ctx->gcry_cipher_hd; - return GPG_ERR_NO_ERROR; -} - -static void ntfs_desx_decrypt(void *context, u8 *outbuf, const u8 *inbuf) -{ - ntfs_desx_ctx *ctx = context; - gcry_error_t err; - - err = gcry_cipher_reset(ctx->gcry_cipher_hd); - if (err != GPG_ERR_NO_ERROR) - fprintf(stderr, "Failed to reset des cipher (error 0x%x).\n", - err); - *(u64*)outbuf = *(const u64*)inbuf ^ ctx->out_whitening; - err = gcry_cipher_encrypt(ctx->gcry_cipher_hd, outbuf, 8, NULL, 0); - if (err != GPG_ERR_NO_ERROR) - fprintf(stderr, "Des decryption failed (error 0x%x).\n", err); - *(u64*)outbuf ^= ctx->in_whitening; -} - -static gcry_cipher_spec_t ntfs_desx_cipher = { - .name = "DES-X-MS128", - .blocksize = 8, - .keylen = 128, - .contextsize = sizeof(ntfs_desx_ctx), - .setkey = ntfs_desx_setkey, - .decrypt = ntfs_desx_decrypt, -}; - -//#define DO_CRYPTO_TESTS 1 - -#ifdef DO_CRYPTO_TESTS - -/* Do not remove this test code from this file! AIA */ -static BOOL ntfs_desx_key_expand_test(void) -{ - const u8 known_desx_on_disk_key[16] = { - 0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f, - 0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30 - }; - const u8 known_des_key[8] = { - 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f, - }; - const u8 known_out_whitening[8] = { - 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d, - }; - const u8 known_in_whitening[8] = { - 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e - }; - u64 test_out_whitening, test_in_whitening; - union { - u64 u64; - u32 u32[2]; - } test_des_key; - gcry_error_t err; - BOOL res; - - err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32, - &test_out_whitening, &test_in_whitening); - if (err != GPG_ERR_NO_ERROR) - res = FALSE; - else - res = test_des_key.u64 == *(u64*)known_des_key && - test_out_whitening == - *(u64*)known_out_whitening && - test_in_whitening == - *(u64*)known_in_whitening; - fprintf(stderr, "Testing whether ntfs_desx_key_expand() works: %s\n", - res ? "SUCCESS" : "FAILED"); - return res; -} - -static BOOL ntfs_des_test(void) -{ - const u8 known_des_key[8] = { - 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f - }; - const u8 known_des_encrypted_data[8] = { - 0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f - }; - const u8 known_decrypted_data[8] = { - 0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09 - }; - u8 test_decrypted_data[8]; - int res; - gcry_error_t err; - gcry_cipher_hd_t gcry_cipher_hd; - - err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_ECB, 0); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to open des cipher (error 0x%x).\n", - err); - return FALSE; - } - err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key, - sizeof(known_des_key)); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to set des key (error 0x%x.\n", err); - gcry_cipher_close(gcry_cipher_hd); - return FALSE; - } - /* - * Apply DES decryption (ntfs actually uses encryption when decrypting). - */ - err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data, - sizeof(test_decrypted_data), known_des_encrypted_data, - sizeof(known_des_encrypted_data)); - gcry_cipher_close(gcry_cipher_hd); - if (err) { - fprintf(stderr, "Failed to des decrypt test data (error " - "0x%x).\n", err); - return FALSE; - } - res = !memcmp(test_decrypted_data, known_decrypted_data, - sizeof(known_decrypted_data)); - fprintf(stderr, "Testing whether des decryption works: %s\n", - res ? "SUCCESS" : "FAILED"); - return res; -} - -#else /* !defined(DO_CRYPTO_TESTS) */ - -static inline BOOL ntfs_desx_key_expand_test(void) -{ - return TRUE; -} - -static inline BOOL ntfs_des_test(void) -{ - return TRUE; -} - -#endif /* !defined(DO_CRYPTO_TESTS) */ - -ntfs_decrypt_data_key *ntfs_decrypt_data_key_open(unsigned char *data, - unsigned data_size __attribute__((unused))) -{ - NTFS_DECRYPT_DATA_KEY *key; - unsigned key_size, wanted_key_size, gcry_algo; - gcry_error_t err; - - key_size = *(u32*)data; - key = (NTFS_DECRYPT_DATA_KEY*)malloc(((((sizeof(*key) + 7) & ~7) + - key_size + 7) & ~7) + sizeof(gcry_cipher_hd_t)); - if (!key) { - errno = ENOMEM; - return NULL; - } - key->alg_id = *(u32*)(data + 8); - key->key_data = (u8*)key + ((sizeof(*key) + 7) & ~7); - memcpy(key->key_data, data + 16, key_size); - key->des_gcry_cipher_hd_ptr = NULL; - *(gcry_cipher_hd_t***)(key->key_data + ((key_size + 7) & ~7)) = - &key->des_gcry_cipher_hd_ptr; - gcry_control(GCRYCTL_DISABLE_SECMEM, 0); - switch (key->alg_id) { - case CALG_DESX: - /* FIXME: This really needs locking so it is safe from races. */ - if (!ntfs_desx_module_count++) { - if (!ntfs_desx_key_expand_test() || !ntfs_des_test()) { - errno = EINVAL; - return NULL; - } - err = gcry_cipher_register(&ntfs_desx_cipher, - &ntfs_desx_algorithm_id, - &ntfs_desx_module); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to register desx " - "cipher (error 0x%x).\n", err); - errno = EINVAL; - return NULL; - } - } - wanted_key_size = 16; - gcry_algo = ntfs_desx_algorithm_id; - break; - case CALG_3DES: - wanted_key_size = 24; - gcry_algo = GCRY_CIPHER_3DES; - break; - case CALG_AES_256: - wanted_key_size = 32; - gcry_algo = GCRY_CIPHER_AES256; - break; - default: - wanted_key_size = 8; - gcry_algo = GCRY_CIPHER_DES; - fprintf(stderr, "DES is not supported at present. Please " - "email linux-ntfs-dev@lists.sourceforge.net " - "and say that you saw this message. We will " - "then implement support for DES.\n"); - free(key); - errno = EOPNOTSUPP; - return NULL; - } - if (key_size != wanted_key_size) { - fprintf(stderr, "%s key of %u bytes but needed size is %u " - "bytes, assuming corrupt key. Aborting.\n", - gcry_cipher_algo_name(gcry_algo), key_size, - wanted_key_size); - free(key); - errno = EIO; - return NULL; - } - err = gcry_cipher_open(&key->gcry_cipher_hd, gcry_algo, - GCRY_CIPHER_MODE_CBC, 0); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "gcry_cipher_open() failed with error 0x%x.\n", - err); - free(key); - errno = EINVAL; - return 0; - } - err = gcry_cipher_setkey(key->gcry_cipher_hd, key->key_data, key_size); - if (err != GPG_ERR_NO_ERROR) { - fprintf(stderr, "gcry_cipher_setkey() failed with error " - "0x%x.\n", err); - gcry_cipher_close(key->gcry_cipher_hd); - free(key); - errno = EINVAL; - return NULL; - } - return (ntfs_decrypt_data_key*)key; -} - -void ntfs_decrypt_data_key_close(ntfs_decrypt_data_key *key) -{ - NTFS_DECRYPT_DATA_KEY *dkey = (NTFS_DECRYPT_DATA_KEY*)key; - if (dkey->des_gcry_cipher_hd_ptr) - gcry_cipher_close(*dkey->des_gcry_cipher_hd_ptr); - gcry_cipher_close(dkey->gcry_cipher_hd); - free(key); - /* FIXME: This really needs locking so it is safe from races. */ - if (!--ntfs_desx_module_count) { - gcry_cipher_unregister(ntfs_desx_module); - ntfs_desx_module = NULL; - ntfs_desx_algorithm_id = -1; - } -} - -unsigned ntfs_decrypt_data_key_decrypt_sector(ntfs_decrypt_data_key *key, - unsigned char *data, unsigned long long offset) -{ - NTFS_DECRYPT_DATA_KEY *dkey = (NTFS_DECRYPT_DATA_KEY*)key; - gcry_error_t err; - - err = gcry_cipher_reset(dkey->gcry_cipher_hd); - if (err != GPG_ERR_NO_ERROR) - fprintf(stderr, "Failed to reset cipher (error 0x%x).\n", err); - /* - * Note: You may wonder why are we not calling gcry_cipher_setiv() here - * instead of doing it by hand after the decryption. The answer is - * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give - * it a length of 16 for AES256 so it does not like it. - */ - if ((err = gcry_cipher_decrypt(dkey->gcry_cipher_hd, data, 512, NULL, - 0))) - fprintf(stderr, "Decryption failed (error 0x%x).\n", err); - /* Apply the IV. */ - if (dkey->alg_id == CALG_AES_256) { - ((u64*)data)[0] ^= 0x5816657be9161312LL + offset; - ((u64*)data)[1] ^= 0x1989adbe44918961LL + offset; - } else { - /* All other algos (Des, 3Des, DesX) use the same IV. */ - ((u64*)data)[0] ^= 0x169119629891ad13LL + offset; - } - return 512; -} diff --git a/ntfsprogs/decrypt.h b/ntfsprogs/decrypt.h deleted file mode 100644 index 56fe168d..00000000 --- a/ntfsprogs/decrypt.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * decrypt.h - Interface for decryption rutines. Part of the Linux-NTFS - * project. - * - * Copyright (c) 2005 Yuval Fledel - * Copyright (c) 2005 Anton Altaparmakov - * - * This program/include file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program/include file is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _NTFS_DECRYPT_H -#define _NTFS_DECRYPT_H - -typedef void *ntfs_decrypt_user_key_session; -typedef void *ntfs_decrypt_user_key; -typedef void *ntfs_decrypt_data_key; - -extern ntfs_decrypt_user_key_session *ntfs_decrypt_user_key_session_open(void); -extern void ntfs_decrypt_user_key_session_close( - ntfs_decrypt_user_key_session *session); - -extern ntfs_decrypt_user_key *ntfs_decrypt_user_key_open( - ntfs_decrypt_user_key_session *session, - unsigned char *thumb_print, unsigned thumb_size); -extern void ntfs_decrypt_user_key_close(ntfs_decrypt_user_key *key); - -extern unsigned ntfs_decrypt_user_key_decrypt(ntfs_decrypt_user_key *key, - unsigned char *data, unsigned data_size); - -extern ntfs_decrypt_data_key *ntfs_decrypt_data_key_open(unsigned char *data, - unsigned data_size); -extern void ntfs_decrypt_data_key_close(ntfs_decrypt_data_key *key); - -extern unsigned ntfs_decrypt_data_key_decrypt_sector(ntfs_decrypt_data_key *key, - unsigned char *data, unsigned long long offset); - -#endif /* defined _NTFS_DECRYPT_H */ diff --git a/ntfsprogs/ntfsdecrypt.c b/ntfsprogs/ntfsdecrypt.c index 63580c67..b08e6ff3 100644 --- a/ntfsprogs/ntfsdecrypt.c +++ b/ntfsprogs/ntfsdecrypt.c @@ -1,10 +1,11 @@ /** - * ntfsdecrypt - Part of the Linux-NTFS project. + * ntfsdecrypt - Decrypt ntfs encrypted files. Part of the Linux-NTFS project. * * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2005 Anton Altaparmakov * - * This utility will decrypt files and print on the standard output. + * This utility will decrypt files and print the decrypted data on the standard + * output. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,10 +25,17 @@ #include "config.h" +#include +#include +#include #include #include #include #include +#include +#include +#include +#include #include "types.h" #include "attrib.h" @@ -36,9 +44,33 @@ #include "debug.h" #include "dir.h" #include "layout.h" -#include "decrypt.h" + +typedef gcry_sexp_t ntfs_rsa_private_key; + +typedef struct { + u8 *key_data; + u32 alg_id; + gcry_cipher_hd_t gcry_cipher_hd; + gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; +} ntfs_fek; + +#define CALG_DES (0x6601) +/* If not one of the below three, fall back to standard Des. */ +#define CALG_3DES (0x6603) +#define CALG_DESX (0x6604) +#define CALG_AES_256 (0x6610) + +/* DESX-MS128 implementation for libgcrypt. */ +static gcry_module_t ntfs_desx_module; +static int ntfs_desx_algorithm_id = -1; + +typedef struct { + u64 in_whitening, out_whitening; + gcry_cipher_hd_t gcry_cipher_hd; +} ntfs_desx_ctx; struct options { + char *keyfile; /* .pfx file containing the user's private key. */ char *device; /* Device/File to work with */ char *file; /* File to display */ s64 inode; /* Inode to work with */ @@ -60,7 +92,6 @@ static ntfschar EFS[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('E'), const_cpu_to_le16('F'), const_cpu_to_le16('S'), const_cpu_to_le16('\0') }; -static const int EFS_name_length = 4; /** * version - Print version information about the program @@ -87,14 +118,14 @@ static void version(void) */ static void usage(void) { - Printf("\nUsage: %s [options] device [file]\n\n" - " -i, --inode num Display this inode\n\n" - " -f --force Use less caution\n" - " -h --help Print this help\n" - " -q --quiet Less output\n" - " -V --version Version information\n" - " -v --verbose More output\n\n", - //" -r --raw Display the compressed or encrypted file", + Printf("\nUsage: %s [options] -k name.pfx device [file]\n\n" + " -i, --inode num Display this inode\n\n" + " -k --keyfile name.pfx Use file name as the user's private key file.\n" + " -f --force Use less caution\n" + " -h --help Print this help\n" + " -q --quiet Less output\n" + " -V --version Version information\n" + " -v --verbose More output\n\n", EXEC_NAME); Printf("%s%s\n", ntfs_bugs, ntfs_home); } @@ -110,11 +141,12 @@ static void usage(void) */ static int parse_options(int argc, char **argv) { - static const char *sopt = "-fh?i:qVv"; // F:N: + static const char *sopt = "-fh?i:k:qVv"; static const struct option lopt[] = { {"force", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"inode", required_argument, NULL, 'i'}, + {"keyfile", required_argument, NULL, 'k'}, {"quiet", no_argument, NULL, 'q'}, {"version", no_argument, NULL, 'V'}, {"verbose", no_argument, NULL, 'v'}, @@ -149,6 +181,15 @@ static int parse_options(int argc, char **argv) case '?': help++; break; + case 'k': + if (!opts.keyfile) + opts.keyfile = argv[optind - 1]; + else { + Eprintf("You must specify exactly one " + "key file.\n"); + err++; + } + break; case 'i': if (opts.inode != -1) Eprintf("You must specify exactly one " @@ -178,20 +219,20 @@ static int parse_options(int argc, char **argv) if (help || ver) { opts.quiet = 0; } else { - if (opts.device == NULL) { + if (!opts.keyfile) { + Eprintf("You must specify a key file.\n"); + err++; + } else if (opts.device == NULL) { Eprintf("You must specify a device.\n"); err++; - } else if (opts.file == NULL && opts.inode == -1) { Eprintf("You must specify a file or inode with the -i " "option.\n"); err++; - } else if (opts.file != NULL && opts.inode != -1) { Eprintf("You can't specify both a file and inode.\n"); err++; } - if (opts.quiet && opts.verbose) { Eprintf("You may not use --quiet and --verbose at the " "same time.\n"); @@ -207,17 +248,802 @@ static int parse_options(int argc, char **argv) return (!err && !help && !ver); } +static int ntfs_pkcs12_load_pfxfile(const char *keyfile, u8 **pfx, + unsigned *pfx_size) +{ + int f, to_read, total, attempts, br; + struct stat stat; + + if (!keyfile || !pfx || !pfx_size) { + fprintf(stderr, "You have to specify the key file, a pointer " + "to hold the key file contents, and a pointer " + "to hold the size of the key file contents."); + return -1; + } + f = open(keyfile, O_RDONLY); + if (f == -1) { + perror("Failed to open key file"); + return -1; + } + if (fstat(f, &stat) == -1) { + perror("Failed to stat key file"); + goto file_out; + } + if (!S_ISREG(stat.st_mode)) { + fprintf(stderr, "Key file is not a regular file, cannot read " + "it."); + goto file_out; + } + if (!stat.st_size) { + fprintf(stderr, "Key file has zero size."); + goto file_out; + } + *pfx = malloc(stat.st_size + 1); + if (!*pfx) { + perror("Failed to allocate buffer for key file contents"); + goto file_out; + } + to_read = stat.st_size; + total = attempts = 0; + do { + br = read(f, *pfx + total, to_read); + if (br == -1) { + perror("Failed to read from key file"); + goto free_out; + } + if (!br) + attempts++; + to_read -= br; + total += br; + } while (to_read > 0 && attempts < 3); + close(f); + /* Make sure it is zero terminated. */ + (*pfx)[stat.st_size] = 0; + *pfx_size = stat.st_size; + return 0; +free_out: + free(*pfx); +file_out: + close(f); + return -1; +} + +static int ntfs_crypto_init(void) +{ + int err; + + /* Initialize gcrypt library. Note: Must come before GNU TLS init. */ + if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to initialize the gcrypt library.\n"); + return -1; + } + /* Initialize GNU TLS library. Note: Must come after libgcrypt init. */ + err = gnutls_global_init(); + if (err < 0) { + fprintf(stderr, "Failed to initialize GNU TLS library: %s\n", + gnutls_strerror(err)); + return -1; + } + return 0; +} + +static void ntfs_crypto_deinit(void) +{ + gnutls_global_deinit(); + if (ntfs_desx_module) { + gcry_cipher_unregister(ntfs_desx_module); + ntfs_desx_module = NULL; + ntfs_desx_algorithm_id = -1; + } +} + +static ntfs_rsa_private_key ntfs_rsa_private_key_import_from_gnutls( + gnutls_x509_privkey_t priv_key) +{ + int i, tmp_size; + gnutls_datum_t rd[6]; + gcry_mpi_t rm[6]; + gcry_sexp_t rsa_key; + + /* Extract the RSA parameters from the GNU TLS private key. */ + if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rd[0], &rd[1], + &rd[2], &rd[3], &rd[4], &rd[5]) != 0) { + fprintf(stderr, "Failed to export rsa parameters. (Is the " + "key an RSA private key?)\n"); + return NULL; + } + /* Convert each RSA parameter to mpi format. */ + for (i = 0; i < 6; i++) { + if (gcry_mpi_scan(&rm[i], GCRYMPI_FMT_USG, rd[i].data, + rd[i].size, &tmp_size) !=0) { + fprintf(stderr, "Failed to convert RSA parameter %i " + "to mpi format (size %d)\n", i, + rd[i].size); + return NULL; + } + } + /* Build the gcrypt private key. */ + if (gcry_sexp_build(&rsa_key, NULL, + "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", + rm[0], rm[1], rm[2], rm[3], rm[4], rm[5]) != 0) { + fprintf(stderr, "Failed to build RSA private key s-exp.\n"); + return NULL; + } + return (ntfs_rsa_private_key)rsa_key; +} + +static ntfs_rsa_private_key ntfs_pkcs12_extract_rsa_key(u8 *pfx, int pfx_size, + char *password) +{ + int err, bag_index, flags; + gnutls_datum_t dpfx, dkey; + gnutls_pkcs12_t pkcs12; + gnutls_pkcs12_bag_t bag; + gnutls_x509_privkey_t pkey; + ntfs_rsa_private_key rsa_key = NULL; + + /* Create a pkcs12 structure. */ + err = gnutls_pkcs12_init(&pkcs12); + if (err < 0) { + fprintf(stderr, "Failed to initialize PKCS#12 structure: %s\n", + gnutls_strerror(err)); + return NULL; + } + /* Convert the PFX file (DER format) to native pkcs12 format. */ + dpfx.data = pfx; + dpfx.size = pfx_size; + err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0); + if (err < 0) { + fprintf(stderr, "Failed to convert the PFX file from DER to " + "native PKCS#12 format: %s\n", + gnutls_strerror(err)); + goto out; + } + /* + * Verify that the password is correct and that the key file has not + * been tampered with. + */ + err = gnutls_pkcs12_verify_mac(pkcs12, password); + if (err < 0) { + fprintf(stderr, "Failed to verify the MAC (%s). Is the " + "password correct?\n", gnutls_strerror(err)); + goto out; + } + for (bag_index = 0; ; bag_index++) { + err = gnutls_pkcs12_bag_init(&bag); + if (err < 0) { + fprintf(stderr, "Failed to initialize PKCS#12 Bag " + "structure: %s\n", + gnutls_strerror(err)); + goto out; + } + err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag); + if (err < 0) { + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + fprintf(stderr, "Failed to obtain Bag from PKCS#12 " + "structure: %s\n", + gnutls_strerror(err)); + goto bag_out; + } +check_again: + err = gnutls_pkcs12_bag_get_count(bag); + if (err < 0) { + fprintf(stderr, "Failed to obtain Bag count: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + err = gnutls_pkcs12_bag_get_type(bag, 0); + if (err < 0) { + fprintf(stderr, "Failed to determine Bag type: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + flags = 0; + switch (err) { + case GNUTLS_BAG_PKCS8_KEY: + flags = GNUTLS_PKCS_PLAIN; + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + gnutls_x509_privkey_init(&pkey); + + err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); + if (err < 0) { + fprintf(stderr, "Failed to obtain Bag data: " + "%s\n", gnutls_strerror(err)); + goto bag_out; + } + /* Decrypt the private key into GNU TLS format. */ + err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey, + GNUTLS_X509_FMT_DER, password, flags); + if (err < 0) { + fprintf(stderr, "Failed to convert private " + "key from DER to GNU TLS " + "format: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + /* Convert the private key to our internal format. */ + rsa_key = ntfs_rsa_private_key_import_from_gnutls(pkey); + goto bag_out; + case GNUTLS_BAG_ENCRYPTED: + err = gnutls_pkcs12_bag_decrypt(bag, password); + if (err < 0) { + fprintf(stderr, "Failed to decrypt Bag: %s\n", + gnutls_strerror(err)); + goto bag_out; + } + goto check_again; + default: + /* We do not care about other types. */ + break; + } + gnutls_pkcs12_bag_deinit(bag); + } +bag_out: + gnutls_pkcs12_bag_deinit(bag); +out: + gnutls_pkcs12_deinit(pkcs12); + return rsa_key; +} + +static void ntfs_rsa_private_key_release(ntfs_rsa_private_key rsa_key) +{ + gcry_sexp_release((gcry_sexp_t)rsa_key); +} + +/** + * ntfs_buffer_reverse - + * + * This is a utility function for reversing the order of a buffer in place. + * Users of this function should be very careful not to sweep byte order + * problems under the rug. + */ +static inline void ntfs_buffer_reverse(u8 *buf, unsigned buf_size) +{ + unsigned i; + u8 t; + + for (i = 0; i < buf_size / 2; i++) { + t = buf[i]; + buf[i] = buf[buf_size - i - 1]; + buf[buf_size - i - 1] = t; + } +} + +/** + * ntfs_raw_fek_decrypt - + * + * Note: decrypting into the input buffer. + */ +static unsigned ntfs_raw_fek_decrypt(u8 *fek, u32 fek_size, + ntfs_rsa_private_key rsa_key) +{ + gcry_mpi_t fek_mpi; + gcry_sexp_t fek_sexp, fek_sexp2; + gcry_error_t err; + size_t size, padding; + + /* Reverse the raw FEK. */ + ntfs_buffer_reverse(fek, fek_size); + /* Convert the FEK to internal MPI format. */ + err = gcry_mpi_scan(&fek_mpi, GCRYMPI_FMT_USG, fek, fek_size, NULL); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to convert file encryption key to " + "internal MPI format: %s\n", + gcry_strerror(err)); + return 0; + } + /* Create an internal S-expression from the FEK. */ + err = gcry_sexp_build(&fek_sexp, NULL, + "(enc-val (flags) (rsa (a %m)))", fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to create internal S-expression of " + "the file encryption key: %s\n", + gcry_strerror(err)); + return 0; + } + /* Decrypt the FEK. */ + err = gcry_pk_decrypt(&fek_sexp2, fek_sexp, (gcry_sexp_t)rsa_key); + gcry_sexp_release(fek_sexp); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to decrypt the file encryption key: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* Extract the actual FEK from the decrypted raw S-expression. */ + fek_sexp = gcry_sexp_find_token(fek_sexp2, "value", 0); + gcry_sexp_release(fek_sexp2); + if (!fek_sexp) { + fprintf(stderr, "Failed to find the decrypted file encryption " + "key in the internal S-expression.\n"); + return 0; + } + /* Convert the decrypted FEK S-expression into MPI format. */ + fek_mpi = gcry_sexp_nth_mpi(fek_sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(fek_sexp); + if (!fek_mpi) { + fprintf(stderr, "Failed to convert the decrypted file " + "encryption key S-expression to internal MPI " + "format.\n"); + return 0; + } + /* Convert the decrypted FEK from MPI format to binary data. */ + err = gcry_mpi_print(GCRYMPI_FMT_USG, fek, fek_size, &size, fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR || !size) { + fprintf(stderr, "Failed to convert decrypted file encryption " + "key from internal MPI format to binary data: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* + * Finally, remove the PKCS#1 padding and return the size of the + * decrypted FEK. + */ + padding = strnlen(fek, size) + 1; + if (padding > size) { + fprintf(stderr, "Failed to remove PKCS#1 padding from " + "decrypted file encryption key.\n"); + return 0; + } + size -= padding; + memmove(fek, fek + padding, size); + return size; +} + +/** + * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key + * @src: source buffer containing 128-bit key + * + * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the + * out-whitening keys required to perform desx {de,en}cryption. + */ +static gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key, + u64 *out_whitening, u64 *in_whitening) +{ + static const u8 *salt1 = "Dan Simon "; + static const u8 *salt2 = "Scott Field"; + static const int salt_len = 12; + gcry_md_hd_t hd1, hd2; + u32 *md; + gcry_error_t err; + + err = gcry_md_open(&hd1, GCRY_MD_MD5, 0); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to open MD5 digest.\n"); + return err; + } + /* Hash the on-disk key. */ + gcry_md_write(hd1, src, 128 / 8); + /* Copy the current hash for efficiency. */ + err = gcry_md_copy(&hd2, hd1); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to copy MD5 digest object.\n"); + goto out; + } + /* Hash with the first salt and store the result. */ + gcry_md_write(hd1, salt1, salt_len); + md = (u32*)gcry_md_read(hd1, 0); + des_key[0] = md[0] ^ md[1]; + des_key[1] = md[2] ^ md[3]; + /* Hash with the second salt and store the result. */ + gcry_md_write(hd2, salt2, salt_len); + md = (u32*)gcry_md_read(hd2, 0); + *out_whitening = *(u64*)md; + *in_whitening = *(u64*)(md + 2); + gcry_md_close(hd2); +out: + gcry_md_close(hd1); + return err; +} + +/** + * ntfs_desx_setkey - libgcrypt set_key implementation for DES-X-MS128 + * @context: pointer to a variable of type ntfs_desx_ctx + * @key: the 128 bit DES-X-MS128 key, concated with the DES handle + * @keylen: must always be 16 + * + * This is the libgcrypt set_key implementation for DES-X-MS128. + */ +static gcry_err_code_t ntfs_desx_setkey(void *context, const u8 *key, + unsigned keylen) +{ + ntfs_desx_ctx *ctx = context; + gcry_error_t err; + u8 des_key[8]; + + if (keylen != 16) { + fprintf(stderr, "Key length for desx must be 16.\n"); + return GPG_ERR_INV_KEYLEN; + } + err = gcry_cipher_open(&ctx->gcry_cipher_hd, GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_ECB, 0); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to open des cipher (error 0x%x).\n", + err); + return err; + } + err = ntfs_desx_key_expand(key, (u32*)des_key, &ctx->out_whitening, + &ctx->in_whitening); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to expand desx key (error 0x%x).\n", + err); + gcry_cipher_close(ctx->gcry_cipher_hd); + return err; + } + err = gcry_cipher_setkey(ctx->gcry_cipher_hd, des_key, sizeof(des_key)); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to set des key (error 0x%x).\n", err); + gcry_cipher_close(ctx->gcry_cipher_hd); + return err; + } + /* + * Take a note of the ctx->gcry_cipher_hd since we need to close it at + * ntfs_decrypt_data_key_close() time. + */ + **(gcry_cipher_hd_t***)(key + ((keylen + 7) & ~7)) = + &ctx->gcry_cipher_hd; + return GPG_ERR_NO_ERROR; +} + +static void ntfs_desx_decrypt(void *context, u8 *outbuf, const u8 *inbuf) +{ + ntfs_desx_ctx *ctx = context; + gcry_error_t err; + + err = gcry_cipher_reset(ctx->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) + fprintf(stderr, "Failed to reset des cipher (error 0x%x).\n", + err); + *(u64*)outbuf = *(const u64*)inbuf ^ ctx->out_whitening; + err = gcry_cipher_encrypt(ctx->gcry_cipher_hd, outbuf, 8, NULL, 0); + if (err != GPG_ERR_NO_ERROR) + fprintf(stderr, "Des decryption failed (error 0x%x).\n", err); + *(u64*)outbuf ^= ctx->in_whitening; +} + +static gcry_cipher_spec_t ntfs_desx_cipher = { + .name = "DES-X-MS128", + .blocksize = 8, + .keylen = 128, + .contextsize = sizeof(ntfs_desx_ctx), + .setkey = ntfs_desx_setkey, + .decrypt = ntfs_desx_decrypt, +}; + +//#define DO_CRYPTO_TESTS 1 + +#ifdef DO_CRYPTO_TESTS + +/* Do not remove this test code from this file! AIA */ +static BOOL ntfs_desx_key_expand_test(void) +{ + const u8 known_desx_on_disk_key[16] = { + 0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f, + 0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30 + }; + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f, + }; + const u8 known_out_whitening[8] = { + 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d, + }; + const u8 known_in_whitening[8] = { + 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e + }; + u64 test_out_whitening, test_in_whitening; + union { + u64 u64; + u32 u32[2]; + } test_des_key; + gcry_error_t err; + BOOL res; + + err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32, + &test_out_whitening, &test_in_whitening); + if (err != GPG_ERR_NO_ERROR) + res = FALSE; + else + res = test_des_key.u64 == *(u64*)known_des_key && + test_out_whitening == + *(u64*)known_out_whitening && + test_in_whitening == + *(u64*)known_in_whitening; + fprintf(stderr, "Testing whether ntfs_desx_key_expand() works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +static BOOL ntfs_des_test(void) +{ + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f + }; + const u8 known_des_encrypted_data[8] = { + 0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f + }; + const u8 known_decrypted_data[8] = { + 0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09 + }; + u8 test_decrypted_data[8]; + int res; + gcry_error_t err; + gcry_cipher_hd_t gcry_cipher_hd; + + err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_ECB, 0); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to open des cipher (error 0x%x).\n", + err); + return FALSE; + } + err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key, + sizeof(known_des_key)); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to set des key (error 0x%x.\n", err); + gcry_cipher_close(gcry_cipher_hd); + return FALSE; + } + /* + * Apply DES decryption (ntfs actually uses encryption when decrypting). + */ + err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data, + sizeof(test_decrypted_data), known_des_encrypted_data, + sizeof(known_des_encrypted_data)); + gcry_cipher_close(gcry_cipher_hd); + if (err) { + fprintf(stderr, "Failed to des decrypt test data (error " + "0x%x).\n", err); + return FALSE; + } + res = !memcmp(test_decrypted_data, known_decrypted_data, + sizeof(known_decrypted_data)); + fprintf(stderr, "Testing whether des decryption works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +#else /* !defined(DO_CRYPTO_TESTS) */ + +static inline BOOL ntfs_desx_key_expand_test(void) +{ + return TRUE; +} + +static inline BOOL ntfs_des_test(void) +{ + return TRUE; +} + +#endif /* !defined(DO_CRYPTO_TESTS) */ + +static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, + unsigned fek_size __attribute__((unused))) +{ + ntfs_fek *fek; + u32 key_size, wanted_key_size, gcry_algo; + gcry_error_t err; + + // TODO: Sanity checking of sizes and offsets. + key_size = *(u32*)fek_buf; + fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) + + sizeof(gcry_cipher_hd_t)); + if (!fek) { + errno = ENOMEM; + return NULL; + } + fek->alg_id = *(u32*)(fek_buf + 8); + fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7); + memcpy(fek->key_data, fek_buf + 16, key_size); + fek->des_gcry_cipher_hd_ptr = NULL; + *(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) = + &fek->des_gcry_cipher_hd_ptr; + switch (fek->alg_id) { + case CALG_DESX: + if (!ntfs_desx_module) { + if (!ntfs_desx_key_expand_test() || !ntfs_des_test()) { + err = EINVAL; + goto out; + } + err = gcry_cipher_register(&ntfs_desx_cipher, + &ntfs_desx_algorithm_id, + &ntfs_desx_module); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to register desx " + "cipher: %s\n", + gcry_strerror(err)); + err = EINVAL; + goto out; + } + } + wanted_key_size = 16; + gcry_algo = ntfs_desx_algorithm_id; + break; + case CALG_3DES: + wanted_key_size = 24; + gcry_algo = GCRY_CIPHER_3DES; + break; + case CALG_AES_256: + wanted_key_size = 32; + gcry_algo = GCRY_CIPHER_AES256; + break; + default: + wanted_key_size = 8; + gcry_algo = GCRY_CIPHER_DES; + fprintf(stderr, "DES is not supported at present. Please " + "email linux-ntfs-dev@lists.sourceforge.net " + "and say that you saw this message. We will " + "then implement support for DES.\n"); + err = EOPNOTSUPP; + goto out; + } + if (key_size != wanted_key_size) { + fprintf(stderr, "%s key of %u bytes but needed size is %u " + "bytes, assuming corrupt key. Aborting.\n", + gcry_cipher_algo_name(gcry_algo), key_size, + wanted_key_size); + err = EIO; + goto out; + } + err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo, + GCRY_CIPHER_MODE_CBC, 0); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "gcry_cipher_open() failed: %s\n", + gcry_strerror(err)); + err = EINVAL; + goto out; + } + err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data, key_size); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "gcry_cipher_setkey() failed: %s\n", + gcry_strerror(err)); + gcry_cipher_close(fek->gcry_cipher_hd); + err = EINVAL; + goto out; + } + return fek; +out: + free(fek); + errno = err; + return NULL; +} + +static void ntfs_fek_release(ntfs_fek *fek) +{ + if (fek->des_gcry_cipher_hd_ptr) + gcry_cipher_close(*fek->des_gcry_cipher_hd_ptr); + gcry_cipher_close(fek->gcry_cipher_hd); + free(fek); +} + +static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, + ntfs_rsa_private_key rsa_key) +{ + EFS_DF_HEADER *df_header; + EFS_DF_CREDENTIAL_HEADER *df_cred; + EFS_DF_CERTIFICATE_HEADER *df_cert; + u8 *fek_buf; + ntfs_fek *fek; + u32 df_count, fek_size; + unsigned i; + + df_header = (EFS_DF_HEADER*)(df_array + 1); + df_count = le32_to_cpu(df_array->df_count); + for (i = 0; i < df_count; i++) { + df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + + le32_to_cpu(df_header->cred_header_offset)); + df_cert = (EFS_DF_CERTIFICATE_HEADER*)((u8*)df_cred + + le32_to_cpu(df_cred->cert_header_offset)); + fek_size = le32_to_cpu(df_header->fek_size); + fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); + /* Decrypt the FEK. Note: This is done in place. */ + fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key); + if (fek_size) { + /* Convert the FEK to our internal format. */ + fek = ntfs_fek_import_from_raw(fek_buf, fek_size); + if (fek) + return fek; + fprintf(stderr, "Failed to convert the decrypted file " + "encryption key to internal format.\n"); + } else + fprintf(stderr, "Failed to decrypt the file " + "encryption key.\n"); + df_header = (EFS_DF_HEADER*)((u8*)df_header + + le32_to_cpu(df_header->df_length)); + } + return NULL; +} + +/** + * ntfs_inode_fek_get - + */ +static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode, + ntfs_rsa_private_key rsa_key) +{ + ntfs_attr *na; + EFS_ATTR_HEADER *efs; + EFS_DF_ARRAY_HEADER *df_array; + ntfs_fek *fek = NULL; + + /* Obtain the $EFS contents. */ + na = ntfs_attr_open(inode, AT_LOGGED_UTILITY_STREAM, EFS, 4); + if (!na) { + perror("Failed to open $EFS attribute"); + return NULL; + } + efs = malloc(na->data_size); + if (!efs) { + perror("Failed to allocate internal buffer"); + ntfs_attr_close(na); + return NULL; + } + if (ntfs_attr_pread(na, 0, na->data_size, efs) != na->data_size) { + perror("Failed to read $EFS attribute"); + free(efs); + ntfs_attr_close(na); + return NULL; + } + ntfs_attr_close(na); + /* Iterate through the DDFs & DRFs until we obtain a key. */ + if (efs->offset_to_ddf_array) { + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_ddf_array)); + fek = ntfs_df_array_fek_get(df_array, rsa_key); + } + if (!fek && efs->offset_to_drf_array) { + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_drf_array)); + fek = ntfs_df_array_fek_get(df_array, rsa_key); + } + free(efs); + return fek; +} + +int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset) +{ + gcry_error_t err; + + err = gcry_cipher_reset(fek->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Failed to reset cipher: %s\n", + gcry_strerror(err)); + return -1; + } + /* + * Note: You may wonder why we are not calling gcry_cipher_setiv() here + * instead of doing it by hand after the decryption. The answer is + * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give + * it a length of 16 for AES256 so it does not like it. + */ + err = gcry_cipher_decrypt(fek->gcry_cipher_hd, data, 512, NULL, 0); + if (err != GPG_ERR_NO_ERROR) { + fprintf(stderr, "Decryption failed: %s\n", gcry_strerror(err)); + return -1; + } + /* Apply the IV. */ + if (fek->alg_id == CALG_AES_256) { + ((u64*)data)[0] ^= 0x5816657be9161312ULL + offset; + ((u64*)data)[1] ^= 0x1989adbe44918961ULL + offset; + } else { + /* All other algos (Des, 3Des, DesX) use the same IV. */ + ((u64*)data)[0] ^= 0x169119629891ad13ULL + offset; + } + return 512; +} + /** * cat */ -static int cat_decrypt(ntfs_inode *inode, ntfs_decrypt_data_key *fek) +// TODO: +static int ntfs_cat_decrypt(ntfs_inode *inode, ntfs_fek *fek) { int bufsize = 512; char *buffer; ntfs_attr *attr; s64 bytes_read, written, offset, total; s64 old_data_size, old_initialized_size; - unsigned i; + int i; buffer = malloc(bufsize); if (!buffer) @@ -247,8 +1073,8 @@ static int cat_decrypt(ntfs_inode *inode, ntfs_decrypt_data_key *fek) } if (!bytes_read) break; - if ((i = ntfs_decrypt_data_key_decrypt_sector(fek, buffer, - offset)) < bytes_read) { + if ((i = ntfs_fek_decrypt_sector(fek, buffer, offset)) < + bytes_read) { perror("ERROR: Couldn't decrypt all data!"); Eprintf("%u/%lld/%lld/%lld\n", i, (long long)bytes_read, (long long)offset, (long long)total); @@ -272,108 +1098,6 @@ static int cat_decrypt(ntfs_inode *inode, ntfs_decrypt_data_key *fek) return 0; } -static ntfs_decrypt_data_key *get_fek_from_df_array( - ntfs_decrypt_user_key_session *session, - EFS_DF_ARRAY_HEADER *df_array) -{ - u32 df_count, hash_size, fek_size; - EFS_DF_CREDENTIAL_HEADER *df_cred; - EFS_DF_CERTIFICATE_HEADER *df_cert; - EFS_DF_HEADER *df_header; - ntfs_decrypt_user_key *key; - u8 *hash_data, *fek_buf; - unsigned i; - - df_header = (EFS_DF_HEADER*)(df_array + 1); - df_count = le32_to_cpu(df_array->df_count); - //Eprintf("df_count 0x%x\n", df_count); - for (i = 0; i < df_count; i++) { - //Eprintf("ddf 0x%x\n", i); - df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + - le32_to_cpu(df_header->cred_header_offset)); - df_cert = (EFS_DF_CERTIFICATE_HEADER*)((u8*)df_cred + - le32_to_cpu(df_cred->cert_header_offset)); - hash_size = le32_to_cpu(df_cert->thumbprint_size); - hash_data = (u8*)df_cert + - le32_to_cpu(df_cert->thumbprint_offset); - fek_size = le32_to_cpu(df_header->fek_size); - fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); - if ((key = ntfs_decrypt_user_key_open(session, hash_data, - hash_size))) { - fek_size = ntfs_decrypt_user_key_decrypt(key, fek_buf, - fek_size); - ntfs_decrypt_user_key_close(key); - if (fek_size) - return ntfs_decrypt_data_key_open(fek_buf, - fek_size); - fprintf(stderr, "Failed to decrypt the FEK.\n"); - } else - perror("Failed to open user key"); - df_header = (EFS_DF_HEADER*)((u8*)df_header + - le32_to_cpu(df_header->df_length)); - } - return NULL; -} - -/** - * get_fek - */ -static ntfs_decrypt_data_key *get_fek(ntfs_inode *inode) -{ - ntfs_decrypt_user_key_session *session; - EFS_DF_ARRAY_HEADER *efs_df_array; - ntfs_decrypt_data_key *fek; - EFS_ATTR_HEADER *efs_attr; - u8 *efs_buffer; - ntfs_attr *na; - - /* Obtain the $EFS contents. */ - na = ntfs_attr_open(inode, AT_LOGGED_UTILITY_STREAM, - EFS, EFS_name_length); - if (!na) { - perror("Failed to open $EFS attribute"); - return NULL; - } - efs_buffer = malloc(na->data_size); - if (!efs_buffer) { - perror("Failed to allocate internal buffer"); - ntfs_attr_close(na); - return NULL; - } - if (ntfs_attr_pread(na, 0, na->data_size, efs_buffer) != - na->data_size) { - perror("Failed to read $EFS attribute"); - free(efs_buffer); - ntfs_attr_close(na); - return NULL; - } - ntfs_attr_close(na); - - /* Init the CryptoAPI. */ - if (!(session = ntfs_decrypt_user_key_session_open())) { - perror("Failed to initialize the cryptoAPI"); - free(efs_buffer); - return NULL; - } - /* Iterate through the DDFs & DRFs until we obtain a key. */ - efs_attr = (EFS_ATTR_HEADER*)efs_buffer; - fek = NULL; - if (efs_attr->offset_to_ddf_array) { - efs_df_array = (EFS_DF_ARRAY_HEADER*)(efs_buffer + - le32_to_cpu(efs_attr->offset_to_ddf_array)); - fek = get_fek_from_df_array(session, efs_df_array); - } - if (!fek && efs_attr->offset_to_drf_array) { - efs_df_array = (EFS_DF_ARRAY_HEADER*)(efs_buffer + - le32_to_cpu(efs_attr->offset_to_drf_array)); - fek = get_fek_from_df_array(session, efs_df_array); - } - /* Close all and return. */ - ntfs_decrypt_user_key_session_close(session); - free(efs_buffer); - return fek; -} - /** * main - Begin here * @@ -384,39 +1108,82 @@ static ntfs_decrypt_data_key *get_fek(ntfs_inode *inode) */ int main(int argc, char *argv[]) { + u8 *pfx_buf, *password; + ntfs_rsa_private_key rsa_key; ntfs_volume *vol; ntfs_inode *inode; - ntfs_decrypt_data_key *fek; - int result = 1; + ntfs_fek *fek; + int pfx_size, res; if (!parse_options(argc, argv)) return 1; utils_set_locale(); - //XXX quieten errors, temporarily - - vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); - if (!vol) { - perror("ERROR: couldn't mount volume"); + /* Initialize crypto in ntfs. */ + if (ntfs_crypto_init()) { + fprintf(stderr, "Failed to initialize crypto. Aborting.\n"); return 1; } + /* Load the PKCS#12 (.pfx) file containing the user's private key. */ + if (ntfs_pkcs12_load_pfxfile(opts.keyfile, &pfx_buf, &pfx_size)) { + fprintf(stderr, "Failed to load key file. Aborting.\n"); + ntfs_crypto_deinit(); + return 1; + } + /* Ask the user for their password. */ + password = getpass("Enter the password with which the private key was " + "encrypted: "); + if (!password) { + perror("Failed to obtain user password"); + free(pfx_buf); + ntfs_crypto_deinit(); + return 1; + } + /* Obtain the user's private RSA key from the key file. */ + rsa_key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, password); + /* Destroy the password. */ + memset(password, 0, strlen(password)); + /* No longer need the pfx file contents. */ + free(pfx_buf); + if (!rsa_key) { + fprintf(stderr, "Failed to extract the private RSA key. Did " + "you perhaps mistype the password?\n"); + ntfs_crypto_deinit(); + return 1; + } + /* Mount the ntfs volume. */ + vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + if (!vol) { + fprintf(stderr, "Failed to mount ntfs volume. Aborting.\n"); + ntfs_rsa_private_key_release(rsa_key); + ntfs_crypto_deinit(); + return 1; + } + /* Open the encrypted ntfs file. */ if (opts.inode != -1) inode = ntfs_inode_open(vol, opts.inode); else inode = ntfs_pathname_to_inode(vol, NULL, opts.file); if (!inode) { - perror("ERROR: Couldn't open inode"); + fprintf(stderr, "Failed to open encrypted file. Aborting.\n"); + ntfs_umount(vol, FALSE); + ntfs_rsa_private_key_release(rsa_key); + ntfs_crypto_deinit(); return 1; } - fek = get_fek(inode); + /* Obtain the file encryption key of the encrypted file. */ + fek = ntfs_inode_fek_get(inode, rsa_key); + ntfs_rsa_private_key_release(rsa_key); if (fek) { - result = cat_decrypt(inode, fek); - ntfs_decrypt_data_key_close(fek); + res = ntfs_cat_decrypt(inode, fek); + ntfs_fek_release(fek); } else { - Eprintf("Could not obtain FEK.\n"); - result = 1; + fprintf(stderr, "Failed to obtain file encryption key. " + "Aborting.\n"); + res = 1; } ntfs_inode_close(inode); ntfs_umount(vol, FALSE); - return result; + ntfs_crypto_deinit(); + return res; }