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; }