From 8c54a0b1a91e4038b6fb0dad46543a8895679c61 Mon Sep 17 00:00:00 2001 From: antona Date: Wed, 27 Jul 2005 15:55:42 +0000 Subject: [PATCH] First pass at massive cleanup of ntfsdectypt/decrypt. --- ntfsprogs/decrypt.c | 700 +++++++++++++++++++++------------------- ntfsprogs/decrypt.h | 40 ++- ntfsprogs/ntfsdecrypt.c | 85 ++--- 3 files changed, 447 insertions(+), 378 deletions(-) diff --git a/ntfsprogs/decrypt.c b/ntfsprogs/decrypt.c index 1d155f49..aa1bfcc1 100644 --- a/ntfsprogs/decrypt.c +++ b/ntfsprogs/decrypt.c @@ -68,7 +68,7 @@ static LPFN_CertFindCertificateInStore fnCertFindCertificateInStore; static LPFN_CertFreeCertificateContext fnCertFreeCertificateContext; static LPFN_CertOpenStore fnCertOpenStore; -/* global variable: handle to crypt32.dll */ +/* Global variable: Handle to crypt32.dll */ static HMODULE hCrypt32 = INVALID_HANDLE_VALUE; #else /* !defined(__CYGWIN__) */ @@ -92,113 +92,28 @@ typedef struct { #else /* !defined(__CYGWIN__) */ int nothing; /* unused */ #endif /* !defined(__CYGWIN__) */ -} DECRYPT_SESSION; +} NTFS_DECRYPT_USER_KEY_SESSION; + +typedef struct { + gcry_sexp_t sexp_key; // the user's RSA key. +} NTFS_DECRYPT_USER_KEY; typedef struct { - u64 desx_key[3]; u8 *key_data; u32 alg_id; gcry_cipher_hd_t gcry_cipher_hd; - gcry_sexp_t sexp_key; // the user's RSA key. -} DECRYPT_KEY; + gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; +} NTFS_DECRYPT_DATA_KEY; /* DESX-MS128 implementation for libgcrypt. */ -static gcry_module_t desx_module; -static int desx_algorithm_id = -1; +static gcry_module_t ntfs_desx_module; +static int ntfs_desx_algorithm_id = -1; +static int ntfs_desx_module_count; -typedef struct desx_ctx { +typedef struct { + u64 in_whitening, out_whitening; gcry_cipher_hd_t gcry_cipher_hd; - u8 in_whitening[8], out_whitening[8]; -} desx_ctx; - -/** - * desx_key_expand - expand a 128-bit desx key to the needed 192-bit key - * @src: source buffer containing 128-bit key - * @dst: destination buffer to write 192-bit key to - * - * Expands the on-disk 128-bit desx key to the needed full 192-bit desx key - * required to perform desx {de,en}cryption. - * - * FIXME: Is this endianness safe? I think so but I may be wrong... - */ -static void desx_key_expand(const u8 *src, u8 *in_whitening, u8 *out_whitening, - u8 *des_key) -{ - static const int salt_len = 12; - static const u8 *salt1 = "Dan Simon "; - static const u8 *salt2 = "Scott Field"; - u8 md[16]; - 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(md, &ctx1); - ((u32*)des_key)[0] = ((u32*)md)[0] ^ ((u32*)md)[1]; - ((u32*)des_key)[1] = ((u32*)md)[2] ^ ((u32*)md)[3]; - - /* Hash with the second salt and store the result. */ - MD5_Update(&ctx2, salt2, salt_len); - MD5_Final(md, &ctx2); - memcpy(out_whitening, md, 8); - memcpy(in_whitening, md + 8, 8); -} - -static gcry_err_code_t do_desx_setkey(void *context, const u8 *key, - unsigned keylen) -{ - struct desx_ctx *ctx = (desx_ctx*)context; - gcry_error_t err; - u8 des_key[8]; - - if (keylen != 16) { - fprintf(stderr, "not 16\n"); - return GPG_ERR_INV_KEYLEN; - } - if ((err = gcry_cipher_open(&ctx->gcry_cipher_hd, GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_ECB, 0)) != GPG_ERR_NO_ERROR) - return err; - if ((err = gcry_cipher_reset(ctx->gcry_cipher_hd))) - fprintf(stderr, "err is %u.\n", err); - desx_key_expand(key, ctx->in_whitening, ctx->out_whitening, des_key); -#if 0 - fprintf(stderr, "expanded keys (hex) =\n\t0x%llx (des)\n\t" - "0x%llx (in-whitening)\n\t" - "0x%llx (out-whitening)\n", *(u64*)des_key, - *(u64*)ctx->in_whitening, *(u64*)ctx->out_whitening); -#endif - if ((err = gcry_cipher_setkey(ctx->gcry_cipher_hd, des_key, 8))) { - fprintf(stderr, "do_desx_setkey: error %u.\n", err); - // TODO: destroy gcry_cipher_hd - } - return GPG_ERR_NO_ERROR; -} - -static void do_desx_decrypt(void *context, u8 *outbuf, const u8 *inbuf) -{ - struct desx_ctx *ctx = (desx_ctx*)context; - gcry_error_t err; - u8 buf[8]; - - *((u64*)buf) = *((const u64*)inbuf) ^ *(const u64*)ctx->out_whitening; - if ((err = gcry_cipher_encrypt(ctx->gcry_cipher_hd, outbuf, 8, buf, 8))) - fprintf(stderr, "desx decryption failed: %u.\n", err); - *((u64*)outbuf) ^= *(const u64*)ctx->in_whitening; -} - -static gcry_cipher_spec_t cipher = { - .name = "DES-X-MS128", - .blocksize = 8, - .keylen = 128, - .contextsize = sizeof(struct desx_ctx), - .setkey = do_desx_setkey, - .decrypt = do_desx_decrypt, -}; +} ntfs_desx_ctx; #ifdef __CYGWIN__ static int cryptoAPI_init_imports(void) @@ -225,170 +140,62 @@ static int cryptoAPI_init_imports(void) fnCertOpenStore = (LPFN_CertOpenStore)GetProcAddress(hCrypt32, "CertOpenStore"); return fnCryptAcquireCertificatePrivateKey && fnCertCloseStore && - fnCertFindCertificateInStore && fnCertFreeCertificateContext && - fnCertOpenStore; + fnCertFindCertificateInStore && + fnCertFreeCertificateContext && fnCertOpenStore; } #endif /* defined(__CYGWIN__) */ -//#define DO_CRYPTO_TESTS 1 - -#ifdef DO_CRYPTO_TESTS -/* Do not remove this test code from this file! AIA */ -static BOOL desx_key_expand_test(void) +ntfs_decrypt_user_key_session *ntfs_decrypt_user_key_session_open(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_desx_expanded_key[24] = { - 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f, - 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d, - 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e - }; - u8 test_desx_expanded_key[24]; - int res; - - desx_key_expand(known_desx_on_disk_key, test_desx_expanded_key); - res = !memcmp(test_desx_expanded_key, known_desx_expanded_key, - sizeof(known_desx_expanded_key)); - fprintf(stderr, "Testing whether desx_key_expand() works: %s\n", - res ? "SUCCESS" : "FAILED"); - return res; -} - -static BOOL 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 gcry_error2; - gcry_cipher_hd_t gcry_cipher_hd; - - if ((gcry_error2 = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_ECB, 0)) != GPG_ERR_NO_ERROR) { - fprintf(stderr, "Failed to open des cipher (gcry_error2 is " - "%u).\n", gcry_error2); - return FALSE; - } - if ((gcry_error2 = gcry_cipher_setkey(gcry_cipher_hd, known_des_key, - sizeof(known_des_key)))) { - fprintf(stderr, "Failed to set des key (gcry_error2 is %u).\n", - gcry_error2); - gcry_cipher_close(gcry_cipher_hd); - return FALSE; - } - memcpy(test_decrypted_data, known_des_encrypted_data, - sizeof(known_des_encrypted_data)); - /* - * Apply DES decyption (ntfs actually uses encryption when decrypting). - */ - gcry_error2 = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data, - sizeof(test_decrypted_data), NULL, 0); - gcry_cipher_close(gcry_cipher_hd); - if (gcry_error2) { - fprintf(stderr, "Failed to des decrypt test data (gcry_error2 " - "is %u).\n", gcry_error2); - 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 desx_key_expand_test(void) -{ - return TRUE; -} - -static inline BOOL des_test(void) -{ - return TRUE; -} - -#endif /* !defined(DO_CRYPTO_TESTS) */ - -decrypt_session *decrypt_open(void) -{ - decrypt_session *session; - - /* TODO: refcount 'module' */ - if (desx_algorithm_id == -1) { - if (!desx_key_expand_test()) - return NULL; - if (!des_test()) - return NULL; - if (gcry_cipher_register(&cipher, &desx_algorithm_id, - &desx_module)) - return NULL; - } - //fprintf(stderr, "desx_algorithm_id: %d\n", desx_algorithm_id); - - gcry_control(GCRYCTL_DISABLE_SECMEM, 0); - + 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 = -1; + errno = EINVAL; return NULL; } - - if (!(hSystemStore = fnCertOpenStore(((LPCSTR) CERT_STORE_PROV_SYSTEM), - 0, (HCRYPTPROV) NULL, CERT_SYSTEM_STORE_CURRENT_USER, - L"MY"))) { + if (!(hSystemStore = fnCertOpenStore(((LPCSTR)CERT_STORE_PROV_SYSTEM), + 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"MY"))) { fprintf(stderr, "Could not open system store.\n"); - errno = -1; + errno = EINVAL; return NULL; } #endif /* defined(__CYGWIN__) */ - session = (decrypt_session*)malloc(sizeof(DECRYPT_SESSION)); + session = malloc(sizeof(NTFS_DECRYPT_USER_KEY_SESSION)); #ifdef __CYGWIN__ - ((DECRYPT_SESSION *)session)->hSystemStore = hSystemStore; + ((NTFS_DECRYPT_USER_KEY_SESSION*)session)->hSystemStore = hSystemStore; #endif /* defined(__CYGWIN__) */ return session; } -void decrypt_close(decrypt_session *session) +void ntfs_decrypt_user_key_session_close(ntfs_decrypt_user_key_session *session) { #ifdef __CYGWIN__ - if (((DECRYPT_SESSION*)session)->hSystemStore) - fnCertCloseStore(((DECRYPT_SESSION*)session)->hSystemStore, - CERT_CLOSE_STORE_CHECK_FLAG); - /* fixme: racy */ - FreeLibrary(hCrypt32); + 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); } -static inline void reverse_buffer(unsigned char *buf, unsigned int buf_size) -{ - unsigned char t; - unsigned int 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; - } -} - -decrypt_key *decrypt_user_key_open( - decrypt_session *session __attribute__ ((unused)), - int thumb_size, void *thumb_print) +ntfs_decrypt_user_key *ntfs_decrypt_user_key_open( + ntfs_decrypt_user_key_session *session __attribute__((unused)), + unsigned char *thumb_print, unsigned thumb_size) { #ifdef __CYGWIN__ CRYPT_HASH_BLOB hash_blob; @@ -396,7 +203,7 @@ decrypt_key *decrypt_user_key_open( PCCERT_CONTEXT pCert; BOOL fCallerFreeProv; HCRYPTKEY hCryptKey; - decrypt_key *key; + ntfs_decrypt_user_key *key; DWORD dwKeySpec; DWORD key_size; BYTE key_blob[1000]; @@ -434,12 +241,10 @@ decrypt_key *decrypt_user_key_open( if (!CryptExportKey(hCryptKey, 0, PRIVATEKEYBLOB, 0, key_blob, &key_size)) { fprintf(stderr, "Could not export key: Error 0x%x\n", - (unsigned int)GetLastError()); + (unsigned)GetLastError()); errno = -1; return NULL; } - if (!(key = (decrypt_key*)malloc(sizeof(DECRYPT_KEY)))) - goto decrypt_key_open_err; CryptDestroyKey(hCryptKey); rsa_pub_key = (RSAPUBKEY*)(key_blob + sizeof(PUBLICKEYSTRUC)); if ((err = gcry_ac_open(&gcry_handle, GCRY_AC_RSA, 0))) { @@ -468,21 +273,22 @@ decrypt_key *decrypt_user_key_open( 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 = -1; - return FALSE; + errno = EINVAL; + return NULL; } - ((DECRYPT_KEY*)key)->sexp_key = sexp_key; + if ((key = (decrypt_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: @@ -490,45 +296,51 @@ decrypt_key_open_err: CryptDestroyKey(hCryptKey); if (pCert) fnCertFreeCertificateContext(pCert); -#endif // defined(__CYGWIN__) +#endif /* defined(__CYGWIN__) */ errno = ENOTSUP; return NULL; } -void decrypt_user_key_close(decrypt_key *key) +void ntfs_decrypt_user_key_close(ntfs_decrypt_user_key *key) { - DECRYPT_KEY *dkey = (DECRYPT_KEY*)key; - if (dkey->gcry_cipher_hd) - gcry_cipher_close(dkey->gcry_cipher_hd); + gcry_sexp_release(((NTFS_DECRYPT_USER_KEY*)key)->sexp_key); free(key); } +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; + } +} + /** - * decrypt_decrypt - * * warning: decrypting into the input buffer! */ -unsigned int decrypt_decrypt(decrypt_key *key, unsigned int data_size, - unsigned char *data) +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 int size, padding_length, i; + 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 = -1; - return FALSE; + 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))) @@ -537,31 +349,30 @@ unsigned int decrypt_decrypt(decrypt_key *key, unsigned int data_size, "(rsa (a %m)))", mpi_buf))) { fprintf(stderr, "Could build sexp from data, (error = 0x%x)\n", rc); - errno = -1; - return FALSE; + errno = EINVAL; + return 0; } if ((rc = gcry_pk_decrypt(&sexp_plain_data, sexp_enc_data, - ((DECRYPT_KEY*)key)->sexp_key))) { + ((NTFS_DECRYPT_USER_KEY*)key)->sexp_key))) { fprintf(stderr, "Could not decrypt fek via s-exp, (error = " "0x%x)\n", rc); - errno = -1; - return FALSE; + 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 = -1; - return FALSE; + 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 = -1; - return FALSE; + errno = EINVAL; + return 0; } // remove the pkcs1 padding for (padding_length = 1; (padding_length < size) && @@ -577,14 +388,305 @@ unsigned int decrypt_decrypt(decrypt_key *key, unsigned int data_size, return size - padding_length; } -unsigned int decrypt_decrypt_sector(decrypt_key *key, void *data, - unsigned long long offset) +/** + * 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) { - gcry_error_t err; - DECRYPT_KEY *dkey = (DECRYPT_KEY*)key; + 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); +} + +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; + } + ntfs_desx_key_expand(key, (u32*)des_key, &ctx->out_whitening, + &ctx->in_whitening); + 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; + + *(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 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_des_key, test_out_whitening, test_in_whitening; + BOOL res; + + desx_key_expand(known_desx_on_disk_key, (u32*)test_des_key, + (u64*)test_out_whitening, (u64*)test_in_whitening); + res = test_des_key != *(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 desx_key_expand_test(void) +{ + return TRUE; +} + +static inline BOOL 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 = (gcry_cipher_hd_t*)(key->key_data + + ((key_size + 7) & ~7)); + *key->des_gcry_cipher_hd_ptr = NULL; + switch (key->alg_id) { + case CALG_DESX: + /* FIXME: This really needs locking so it is safe from races. */ + if (!ntfs_desx_module_count++) { + gcry_error_t err; + + if (!desx_key_expand_test() || !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; + } + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + } + 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 = ENOTSUP; + 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; - if ((err = gcry_cipher_reset(dkey->gcry_cipher_hd))) - fprintf(stderr, "sector_decrypt: error is %u.\n", err); // FIXME: Why are we not calling gcry_cipher_setiv() here instead of // doing it by hand after the decryption? // It wants iv length 8 but we give it 16 for AES256 so it does not @@ -602,53 +704,3 @@ unsigned int decrypt_decrypt_sector(decrypt_key *key, void *data, } return 512; } - -decrypt_key *decrypt_make_key(decrypt_session *session __attribute__ ((unused)), - unsigned int data_size __attribute__ ((unused)), - unsigned char *data) -{ - DECRYPT_KEY *key; - unsigned int key_size, gcry_algo; - gcry_error_t err; - - key_size = *(u32*)data; - - if (!(key = (DECRYPT_KEY*)malloc(sizeof(DECRYPT_KEY)))) { - errno = -1; - return NULL; - } - key_size = *(u32*)data; - key->alg_id = *(u32*)(data + 8); - key->key_data = data + 16; - - switch (key->alg_id) { - case CALG_DESX: - //fprintf(stderr, "DESX key of %u bytes\n", key_size); - gcry_algo = desx_algorithm_id; - break; - case CALG_3DES: - //fprintf(stderr, "3DES Key of %u bytes\n", key_size); - gcry_algo = GCRY_CIPHER_3DES; - break; - case CALG_AES_256: - //fprintf(stderr, "AES Key of %u bytes\n", key_size); - gcry_algo = GCRY_CIPHER_AES256; - break; - default: - fprintf(stderr, "DES key of %u bytes\n", key_size); - fprintf(stderr, "This probably will not work... " - "It is completely untested.\n"); - gcry_algo = GCRY_CIPHER_DES; - break; - } - if ((err = gcry_cipher_open(&key->gcry_cipher_hd, gcry_algo, - GCRY_CIPHER_MODE_CBC, 0)) != GPG_ERR_NO_ERROR) { - fprintf(stderr, "gcry_cipher_open failed with 0x%x.\n", err); - errno = -1; - return 0; - } - if ((err = gcry_cipher_setkey(key->gcry_cipher_hd, key->key_data, - key_size))) - fprintf(stderr, "gcry_cipher_setkey failed with 0x%x.\n", err); - return (decrypt_key*)key; -} diff --git a/ntfsprogs/decrypt.h b/ntfsprogs/decrypt.h index c1cb8c80..56fe168d 100644 --- a/ntfsprogs/decrypt.h +++ b/ntfsprogs/decrypt.h @@ -1,8 +1,9 @@ /* - * decrypt.h - interface for decryption rutines. - * Part of the Linux-NTFS project. + * 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 @@ -23,20 +24,27 @@ #ifndef _NTFS_DECRYPT_H #define _NTFS_DECRYPT_H -typedef void *decrypt_session; -typedef void *decrypt_key; +typedef void *ntfs_decrypt_user_key_session; +typedef void *ntfs_decrypt_user_key; +typedef void *ntfs_decrypt_data_key; -extern decrypt_session *decrypt_open(void); -extern void decrypt_close(decrypt_session * session); -extern decrypt_key *decrypt_user_key_open(decrypt_session * session, - int thumb_size, void *thumb_print); -extern void decrypt_user_key_close(decrypt_key * key); -extern unsigned int decrypt_decrypt(decrypt_key * key, unsigned int data_size, - unsigned char *data); -extern unsigned int decrypt_decrypt_sector(decrypt_key * key, void *data, - unsigned long long offset); -extern decrypt_key *decrypt_make_key(decrypt_session * session, - unsigned int data_size, unsigned char *data); -extern int decrypt_get_block_size(decrypt_key * 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 6cf3d618..3a7ec04c 100644 --- a/ntfsprogs/ntfsdecrypt.c +++ b/ntfsprogs/ntfsdecrypt.c @@ -210,13 +210,14 @@ static int parse_options(int argc, char **argv) /** * cat */ -static int cat_decrypt(ntfs_inode *inode, decrypt_key *fek) +static int cat_decrypt(ntfs_inode *inode, ntfs_decrypt_data_key *fek) { int bufsize = 512; char *buffer; ntfs_attr *attr; s64 bytes_read, written, offset, total; - unsigned int i; + s64 old_data_size, old_initialized_size; + unsigned i; buffer = malloc(bufsize); if (!buffer) @@ -233,6 +234,8 @@ static int cat_decrypt(ntfs_inode *inode, decrypt_key *fek) // clear the encrypted bit, otherwise the library won't allow reading. NAttrClearEncrypted(attr); // extend the size, we may need to read past the end of the stream. + old_data_size = attr->data_size; + old_initialized_size = attr->initialized_size; attr->data_size = attr->initialized_size = attr->allocated_size; offset = 0; @@ -244,8 +247,8 @@ static int cat_decrypt(ntfs_inode *inode, decrypt_key *fek) } if (!bytes_read) break; - if ((i = decrypt_decrypt_sector(fek, buffer, offset)) < - bytes_read) { + if ((i = ntfs_decrypt_data_key_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); @@ -261,6 +264,9 @@ static int cat_decrypt(ntfs_inode *inode, decrypt_key *fek) offset += bytes_read; total -= bytes_read; } + attr->data_size = old_data_size; + attr->initialized_size = old_initialized_size; + NAttrSetEncrypted(attr); ntfs_attr_close(attr); free(buffer); return 0; @@ -269,41 +275,44 @@ static int cat_decrypt(ntfs_inode *inode, decrypt_key *fek) /** * get_fek */ -static decrypt_key *get_fek(ntfs_inode * inode) +static ntfs_decrypt_data_key *get_fek(ntfs_inode *inode) { ntfs_attr *na; - char *efs_buffer, *ddf, *certificate, *hash_data, *fek_buf; + unsigned char *efs_buffer, *ddf, *certificate, *hash_data, *fek_buf; u32 ddf_count, hash_size, fek_size; - unsigned int i; - decrypt_session *session; - decrypt_key *key; + unsigned i; + ntfs_decrypt_user_key_session *session; + ntfs_decrypt_user_key *key; - /* obtain the $EFS contents */ + /* Obtain the $EFS contents. */ na = ntfs_attr_open(inode, AT_LOGGED_UTILITY_STREAM, EFS, EFS_name_length); if (!na) { - perror("Error"); + perror("Failed to open $EFS attribute"); return NULL; } efs_buffer = malloc(na->data_size); if (!efs_buffer) { - perror("malloc failed"); + 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("ntfs_attr_pread failed"); + perror("Failed to read $EFS attribute"); free(efs_buffer); + ntfs_attr_close(na); return NULL; } ntfs_attr_close(na); /* Init the CryptoAPI. */ - if (!(session = decrypt_open())) { - perror("Could not 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 you obtain a key. */ + /* Iterate through the DDFs & DRFs until we obtain a key. */ ddf = efs_buffer + le32_to_cpu(*(u32*)(efs_buffer + 0x40)); ddf_count = le32_to_cpu(*(u32*)ddf); @@ -315,32 +324,32 @@ static decrypt_key *get_fek(ntfs_inode * inode) le32_to_cpu(*(u32*)(ddf + 0x18))); else certificate = (ddf + 0x30); - - hash_size = (unsigned int)le32_to_cpu(*(u32*)certificate); - hash_data = certificate + (unsigned int) + hash_size = (unsigned)le32_to_cpu(*(u32*)certificate); + hash_data = certificate + (unsigned) le32_to_cpu(*(u32*)(certificate + 0x04)); - fek_size = (unsigned int)le32_to_cpu(*(u32*)(ddf + 0x08)); - fek_buf = ddf + (unsigned int)le32_to_cpu(*(u32*)(ddf + 0x0c)); + fek_size = (unsigned)le32_to_cpu(*(u32*)(ddf + 0x08)); + fek_buf = ddf + (unsigned)le32_to_cpu(*(u32*)(ddf + 0x0c)); + 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) { + ntfs_decrypt_data_key *fek; - if ((key = decrypt_user_key_open(session, hash_size, - hash_data))) { - if ((fek_size = decrypt_decrypt(key, fek_size, - fek_buf))) - return decrypt_make_key(session, fek_size, - fek_buf); - perror("error decrypting the FEK."); - decrypt_user_key_close(key); - decrypt_close(session); - errno = -1; - return NULL; - decrypt_user_key_close(key); + ntfs_decrypt_user_key_session_close(session); + fek = ntfs_decrypt_data_key_open(fek_buf, + fek_size); + free(efs_buffer); + return fek; + } + fprintf(stderr, "Failed to decrypt the FEK.\n"); } else - Eprintf("Could not open key.\n"); - + perror("Failed to open user key"); ddf = ddf + le32_to_cpu(*(u32*)(ddf + 0x08)) + le32_to_cpu(*(u32*)(ddf + 0x0c)); } - decrypt_close(session); + ntfs_decrypt_user_key_session_close(session); return NULL; } @@ -356,7 +365,7 @@ int main(int argc, char *argv[]) { ntfs_volume *vol; ntfs_inode *inode; - decrypt_key *fek; + ntfs_decrypt_data_key *fek; int result = 1; if (!parse_options(argc, argv)) @@ -381,7 +390,7 @@ int main(int argc, char *argv[]) fek = get_fek(inode); if (fek) { result = cat_decrypt(inode, fek); - decrypt_user_key_close(fek); + ntfs_decrypt_data_key_close(fek); } else { Eprintf("Could not obtain FEK.\n"); result = 1;