diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index 65e2a8e7cbf..42d7bc3f912 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -149,7 +149,7 @@ @ stub CryptSignHashU @ stub CryptSignMessage @ stub CryptSignMessageWithKey -@ stub CryptUnprotectData +@ stdcall CryptUnprotectData(ptr ptr ptr ptr ptr long ptr) @ stub CryptUnregisterDefaultOIDFunction @ stub CryptUnregisterOIDFunction @ stub CryptUnregisterOIDInfo diff --git a/dlls/crypt32/protectdata.c b/dlls/crypt32/protectdata.c index 060393ae4d3..6a34819e4b2 100644 --- a/dlls/crypt32/protectdata.c +++ b/dlls/crypt32/protectdata.c @@ -963,3 +963,172 @@ finished: return rc; } + + +/*************************************************************************** + * CryptUnprotectData [CRYPT32.@] + * + * Generate Plain data and Description from given Cipher and Entropy data. + * + * PARAMS + * pDataIn [I] Cipher data to be decoded + * ppszDataDescr [O] Optional Unicode string describing the Plain data + * pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL + * pvReserved [I] Reserved, must be NULL + * pPromptStruct [I] Structure describing if/how to prompt during decoding + * dwFlags [I] Flags describing options to the decoding + * pDataOut [O] Resulting Plain data, from calls to CryptProtectData + * + * RETURNS + * TRUE If a Plain was generated. + * FALSE If something failed and no Plain is available. + * + * FIXME + * The true Windows encryption and keying mechanisms are unknown. + * + * dwFlags and pPromptStruct are currently ignored. + * + * NOTES + * Memory allocated in pDataOut and non-NULL ppszDataDescr must be freed + * with LocalFree. + * + */ +BOOL WINAPI CryptUnprotectData(DATA_BLOB* pDataIn, + LPWSTR * ppszDataDescr, + DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, + DWORD dwFlags, + DATA_BLOB* pDataOut) +{ + BOOL rc = FALSE; + + HCRYPTPROV hProv; + struct protect_data_t protect_data; + HCRYPTHASH hHash; + HCRYPTKEY hKey; + DWORD dwLength; + + const char * announce_bad_opaque_data = "CryptUnprotectData received a DATA_BLOB that seems to have NOT been generated by Wine. Please enable tracing ('export WINEDEBUG=crypt') to see details."; + + TRACE("called\n"); + + SetLastError(ERROR_SUCCESS); + + if (!pDataIn || !pDataOut) + { + SetLastError(ERROR_INVALID_PARAMETER); + goto finished; + } + + /* debug: show our arguments */ + report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags); + TRACE("\tppszDataDescr: 0x%x\n",(unsigned int)ppszDataDescr); + + /* take apart the opaque blob */ + if (!unserialize(pDataIn, &protect_data)) + { + SetLastError(ERROR_INVALID_DATA); + FIXME("%s\n",announce_bad_opaque_data); + goto finished; + } + + /* perform basic validation on the resulting structure */ + if (!valid_protect_data(&protect_data)) + { + SetLastError(ERROR_INVALID_DATA); + FIXME("%s\n",announce_bad_opaque_data); + goto free_protect_data; + } + + /* get a crypt context */ + if (!CryptAcquireContextW(&hProv,NULL,NULL,CRYPT32_PROTECTDATA_PROV,0)) + { + ERR("CryptAcquireContextW failed\n"); + goto free_protect_data; + } + + /* load key */ + if (!load_encryption_key(hProv,&protect_data.salt,pOptionalEntropy,&hKey)) + { + goto free_context; + } + + /* create a hash for the decryption validation */ + if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash)) + { + ERR("CryptCreateHash\n"); + goto free_key; + } + + /* prepare for plaintext */ + pDataOut->cbData=protect_data.cipher.cbData; + if (!(pDataOut->pbData=LocalAlloc( LPTR, pDataOut->cbData))) + { + ERR("HeapAlloc\n"); + goto free_hash; + } + memcpy(pDataOut->pbData,protect_data.cipher.pbData,protect_data.cipher.cbData); + + /* decrypt! */ + if (!CryptDecrypt(hKey, hHash, TRUE, 0, pDataOut->pbData, + &pDataOut->cbData) || + /* check the hash fingerprint */ + pDataOut->cbData > protect_data.cipher.cbData || + !hash_matches_blob(hHash, &protect_data.fingerprint)) + { + SetLastError(ERROR_INVALID_DATA); + + LocalFree( pDataOut->pbData ); + pDataOut->pbData = NULL; + pDataOut->cbData = 0; + + goto free_hash; + } + + /* Copy out the description */ + dwLength = (lstrlenW(protect_data.szDataDescr)+1) * sizeof(WCHAR); + if (ppszDataDescr) + { + if (!(*ppszDataDescr = LocalAlloc(LPTR,dwLength))) + { + ERR("LocalAlloc (ppszDataDescr)\n"); + goto free_hash; + } + else { + memcpy(*ppszDataDescr,protect_data.szDataDescr,dwLength); + } + } + + /* success! */ + rc = TRUE; + +free_hash: + CryptDestroyHash(hHash); +free_key: + CryptDestroyKey(hKey); +free_context: + CryptReleaseContext(hProv,0); +free_protect_data: + free_protect_data(&protect_data); +finished: + /* If some error occured, and no error code was set, force one. */ + if (!rc && GetLastError()==ERROR_SUCCESS) + { + SetLastError(ERROR_INVALID_DATA); + } + + if (rc) { + SetLastError(ERROR_SUCCESS); + + if (ppszDataDescr) + { + TRACE("szDataDescr: %s\n",debugstr_w(*ppszDataDescr)); + } + TRACE_DATA_BLOB(pDataOut); + } + + TRACE("returning %s\n", rc ? "ok" : "FAIL"); + + return rc; +}