http://bugs.winehq.org/show_bug.cgi?id=29449
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |dotnet, download Status|UNCONFIRMED |NEW URL| |https://www.usps.com/busine | |ss/shipping-assistant.htm CC| |focht@gmx.net Summary|USPS shipping assistant |USPS shipping assistant |version 3.8 won't start |version 3.8 won't start | |(Microsoft SQL Server | |Compact database metadata | |incorrectly decrypted, enh. | |RSA AES-128 provider) Ever Confirmed|0 |1
--- Comment #5 from Anastasius Focht focht@gmx.net 2012-01-13 17:20:43 CST --- Hello,
confirming.
Only clean WINEPREFIX and 'winetricks -q dotnet20' prerequisite needed. No MDAC 2.x needed!
Download: https://www.usps.com/shippingassistant/install/setup.exe
The .NET app shows an exception message box on startup.
There is a managed backtrace written to logfile which gives further hints.
"$WINEPREFIX/drive_c/users/focht/Local Settings/Application Data/ShippingAssistant/Logs/xxxShippingAssistant.log":
--- snip --- ... An exception of type 'System.Data.SqlServerCe.SqlCeException' occurred and was caught. -------------------------------------------------------------------------------------- 01/13/2012 22:27:47 Type : System.Data.SqlServerCe.SqlCeException, System.Data.SqlServerCe, Version=3.5.1.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91 Message : The password specified for the source database is incorrect. [ Data Source = C:\users\focht\Local Settings\Application Data\ShippingAssistant\Database\ShippingAssistant.sdf ] Source : SQL Server Compact ADO.NET Data Provider Help link : Errors : System.Data.SqlServerCe.SqlCeErrorCollection HResult : -2147467259 NativeError : 25140 Data : System.Collections.ListDictionaryInternal TargetSite : Void ProcessResults(IntPtr, Int32) Stack Trace : at System.Data.SqlServerCe.SqlCeEngine.ProcessResults(IntPtr pError, Int32 hr) at System.Data.SqlServerCe.SqlCeEngine.Repair(SEFIXOPTION option, String dstConnStr, RepairOption repairOption) at System.Data.SqlServerCe.SqlCeEngine.Compact(String connectionString) at USPS.SmartClient.DomainModel.Common.ConnectionStringProvider.VerifyDatabaseVersionAndIntegrity(String connString, String minusPassword) at USPS.SmartClient.DomainModel.Common.ConnectionStringProvider.GetConnectionString() at USPS.SmartClient.DomainModel.Common.RepositoryProvider..ctor() at USPS.SmartClient.DomainModel.Common.RepositoryProvider.get_Instance() at USPS.SmartClient.Presentation.Shell.ShippingAssistant.ShippingAssistantShell.InitializeConfiguration() at USPS.SmartClient.Presentation.Shell.ShippingAssistant.ShippingAssistantShell.Main() ... --- snip ---
The app uses Microsoft SQL Server Compact (SQL CE).
https://en.wikipedia.org/wiki/SQL_Server_Compact
--- quote --- SQL CE databases reside in a single .sdf file,[12] which can be up to 4 GB in size.[4] The .sdf file can be encrypted with 128-bit encryption for data security.[12] SQL CE runtime mediates concurrent multi-user access to the .sdf file. The .sdf file can simply be copied to the destination system for deployment, or be deployed through ClickOnce. SQL CE runtime has support for DataDirectories.[6] Applications using an SQL CE database need not specify the entire path to an .sdf file in the ADO.NET connection string, rather it can be specified as |DataDirectory|<database_name>.sdf, defining the data directory (where the .sdf database file resides) being defined in the assembly manifest for the application. --- quote ---
The database is located at:
--- snip --- "C:\users\focht\Local Settings\Application Data\ShippingAssistant\Database\ShippingAssistant.sdf" --- snip ---
The connection string (datasource, password phrase) is hard coded (cleartext) into "USPS.SmartClient.DomainModel.Common.dll" assembly.
Top level SQL CE API call failing, managed exception being thrown, caught and logged/displayed:
--- snip --- ... 002f:CALL SQLCEME35.ME_OpenStore(<unknown, check return>) ret=0039b904 ... 002f:RET SQLCEME35.ME_OpenStore(0020fa68,0053c642,00767ea4,00767ea8,00767eb0,00767eac,00767ebc,00767eb4,00767eb8,00767ec0,00767ec4) retval=80004005 ret=0039b904 ... 002f:CALL SQLCEER35.SSCEGetErrorString(000061c4,0021a530,00001000,0032f034) ret=7d354692 ... 002f:Call KERNEL32.RaiseException(e0434f4d,00000001,00000001,0032f098) ret=79f97065 002f:trace:seh:raise_exception code=e0434f4d flags=1 addr=0x7b838b77 ip=7b838b77 tid=002f 002f:trace:seh:raise_exception info[0]=80131501 002f:trace:seh:raise_exception eax=7b826171 ebx=7b8a97e8 ecx=80131501 edx=0032ef98 esi=0032f078 edi=0032eff0 002f:trace:seh:raise_exception ebp=0032efd8 esp=0032ef74 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00000203 ... --- snip ---
First, a handle for enhanced RSA/AES CSP and the SSCE key container (created on first run) is aquired:
--- snip --- ... 002f:Call advapi32.CryptAcquireContextW(00219f84,7d554314 L"SSCE Key Container 3.5",7d554160 L"Microsoft Enhanced RSA and AES Cryptographic Provider",00000018,00000000) ret=7d59990f 002f:trace:crypt:CryptAcquireContextW (0x219f84, L"SSCE Key Container 3.5", L"Microsoft Enhanced RSA and AES Cryptographic Provider", 24, 00000000) 002f:Call rsaenh.CPAcquireContext(0021a560,0021a648 "SSCE Key Container 3.5",00000000,0021a5e0) ret=31014bb6 002f:trace:crypt:RSAENH_CPAcquireContext (phProv=0x21a560, pszContainer="SSCE Key Container 3.5", dwFlags=00000000, pVTable=0x21a5e0) 002f:Call advapi32.RegOpenKeyExA(80000001,0032eab8 "Software\Wine\Crypto\RSA\SSCE Key Container 3.5",00000000,00020019,0032ec00) ret=5d406083 002f:Ret advapi32.RegOpenKeyExA() retval=00000000 ret=5d406083 002f:Call ntdll.RtlAllocateHeap(00110000,00000000,0000022c) ret=5d3fa6d4 002f:Ret ntdll.RtlAllocateHeap() retval=0021a668 ret=5d3fa6d4 002f:Call advapi32.RegCreateKeyExA(80000001,0032ea68 "Software\Wine\Crypto\RSA\SSCE Key Container 3.5",00000000,00000000,00000000,00020006,00000000,0032eba4,00000000) ret=5d405ff9 002f:Ret advapi32.RegCreateKeyExA() retval=00000000 ret=5d405ff9 002f:Call advapi32.RegCloseKey(00000288) ret=5d40653b 002f:Ret advapi32.RegCloseKey() retval=00000000 ret=5d40653b 002f:Call advapi32.RegQueryValueExA(00000280,5d41ff57 "KeyExchangeKeyPair",00000000,0032eba8,00000000,0032eba4) ret=5d4065b1 002f:Ret advapi32.RegQueryValueExA() retval=00000002 ret=5d4065b1 002f:Call advapi32.RegQueryValueExA(00000280,5d41ff6a "SignatureKeyPair",00000000,0032eba8,00000000,0032eba4) ret=5d4065b1 002f:Ret advapi32.RegQueryValueExA() retval=00000002 ret=5d4065b1 002f:Ret rsaenh.CPAcquireContext() retval=00000001 ret=31014bb6 002f:Ret advapi32.CryptAcquireContextW() retval=00000001 ret=7d59990f --- snip ---
Then an empty hash object is created:
--- snip --- 002f:Call advapi32.CryptCreateHash(0021a550,00008004,00000000,00000000,00219f88) ret=7d599bf6 002f:trace:crypt:CryptCreateHash (0x21a550, 0x8004, 0x0, 00000000, 0x219f88) 002f:Call rsaenh.CPCreateHash(0000000d,00008004,00000000,00000000,0021a650) ret=31015354 002f:trace:crypt:RSAENH_CPCreateHash (hProv=0000000d, Algid=00008004, hKey=00000000, dwFlags=00000000, phHash=0x21a650) 002f:Call ntdll.RtlAllocateHeap(00110000,00000000,00000170) ret=5d3fa6d4 002f:Ret ntdll.RtlAllocateHeap() retval=0021a8a0 ret=5d3fa6d4 002f:Call advapi32.A_SHAInit(0021a8c0) ret=5d3fa80c 002f:Ret advapi32.A_SHAInit() retval=0021a8c0 ret=5d3fa80c 002f:Ret rsaenh.CPCreateHash() retval=00000001 ret=31015354 002f:Ret advapi32.CryptCreateHash() retval=00000001 ret=7d599bf6 --- snip ---
Then the clear text of the password is hashed using CryptHashData:
--- snip --- 002f:Call ntdll.wcslen(0020dac0 L"@EtyL19Drt64Clmh80LKt$sXa239H&qL93*N5LK7") ret=7d599c03 002f:Ret ntdll.wcslen() retval=00000028 ret=7d599c03 002f:Call advapi32.CryptHashData(0021a648,0020dac0,00000050,00000000) ret=7d599c0f 002f:trace:crypt:CryptHashData (0x21a648, 0x20dac0, 80, 00000000) 002f:Call rsaenh.CPHashData(0000000d,0000000c,0020dac0,00000050,00000000) ret=31017316 002f:trace:crypt:RSAENH_CPHashData (hProv=0000000d, hHash=0000000c, pbData=0x20dac0, dwDataLen=80, dwFlags=00000000) 002f:Call advapi32.A_SHAUpdate(0021a8c0,0020dac0,00000050) ret=5d3fa929 002f:Ret advapi32.A_SHAUpdate() retval=0021a8f4 ret=5d3fa929 002f:Ret rsaenh.CPHashData() retval=00000001 ret=31017316 002f:Ret advapi32.CryptHashData() retval=00000001 ret=7d599c0f --- snip ---
Not sure about the next sequence. Normally a session key would be derived from hashed password using CryptDeriveKey.
Instead CryptDuplicateHash() is used to create duplicate hash from first (password hash) and 4 bytes data get added using CryptHashData:
--- snip --- 002f:Call KERNEL32.CreateFileW(001e95d8 L"C:\users\focht\Local Settings\Application Data\ShippingAssistant\Database\ShippingAssistant.sdf",00000000,00000000,0032eb28,00000003,00000000,00000000) ret=7d595163 002f:Ret KERNEL32.CreateFileW() retval=00000288 ret=7d595163 ... 002f:Call advapi32.CryptDuplicateHash(0021a648,00000000,00000000,0032efb8) ret=7d5999b0 002f:trace:crypt:CryptDuplicateHash (0x21a648, (nil), 00000000, 0x32efb8) 002f:Call rsaenh.CPDuplicateHash(0000000d,0000000c,00000000,00000000,0021a470) ret=31015a31 002f:trace:crypt:RSAENH_CPDuplicateHash (hUID=0000000d, hHash=0000000c, pdwReserved=(nil), dwFlags=00000000, phHash=0x21a470) ... 002f:Ret rsaenh.CPDuplicateHash() retval=00000001 ret=31015a31 002f:Ret advapi32.CryptDuplicateHash() retval=00000001 ret=7d5999b0 002f:Call advapi32.CryptHashData(0021a468,0032efe0,00000004,00000000) ret=7d5999c1 002f:trace:crypt:CryptHashData (0x21a468, 0x32efe0, 4, 00000000) ... 002f:Call rsaenh.CPHashData(0000000d,0000000f,0032efe0,00000004,00000000) ret=31017316 002f:trace:crypt:RSAENH_CPHashData (hProv=0000000d, hHash=0000000f, pbData=0x32efe0, dwDataLen=4, dwFlags=00000000) 002f:Call advapi32.A_SHAUpdate(0021bc48,0032efe0,00000004) ret=5d3fa929 002f:Ret advapi32.A_SHAUpdate() retval=0021bc8c ret=5d3fa929 002f:Ret rsaenh.CPHashData() retval=00000001 ret=31017316 002f:Ret advapi32.CryptHashData() retval=00000001 ret=7d5999c1 --- snip ---
Derive a session key from second hash:
--- snip --- 002f:Call advapi32.CryptDeriveKey(0021a550,0000660e,0021a468,00000000,0032efb4) ret=7d5999d8 002f:trace:crypt:CryptDeriveKey (0x21a550, 0x0000660e, 0x21a468, 0x00000000, 0x32efb4) 002f:Call rsaenh.CPDeriveKey(0000000d,0000660e,0000000f,00000000,0021a4b8) ret=3101564b 002f:trace:crypt:RSAENH_CPDeriveKey (hProv=0000000d, Algid=26126, hBaseData=0000000f, dwFlags=00000000 phKey=0x21a4b8) 002f:trace:crypt:new_key alg = "AES-128", dwKeyLen = 0 002f:Call ntdll.RtlAllocateHeap(00110000,00000000,000003e4) ret=5d3fa6d4 002f:Ret ntdll.RtlAllocateHeap() retval=0021bda0 ret=5d3fa6d4 002f:trace:crypt:RSAENH_CPGetHashParam (hProv=0000000d, hHash=0000000f, dwParam=00000002, pbData=0x32ecec, pdwDataLen=0x32ece8, dwFlags=00000000) 002f:Call advapi32.A_SHAFinal(0021bc48,0021bd18) ret=5d3faa9e 002f:Ret advapi32.A_SHAFinal() retval=0021bc48 ret=5d3faa9e 002f:Ret rsaenh.CPDeriveKey() retval=00000001 ret=3101564b 002f:Ret advapi32.CryptDeriveKey() retval=00000001 ret=7d5999d8 --- snip ---
Decrypt a number of bytes read from database using derived session key:
--- snip --- 002f:Call advapi32.CryptDecrypt(0021a4b0,00000000,00000000,00000000,0021ac6c,0032efb0) ret=7d599a0a 002f:trace:crypt:CryptDecrypt (0x21a4b0, 0x0, 0, 00000000, 0x21ac6c, 0x32efb0) 002f:Call rsaenh.CPDecrypt(0000000d,0000000e,00000000,00000000,00000000,0021ac6c,0032efb0) ret=310154d3 002f:trace:crypt:RSAENH_CPDecrypt (hProv=0000000d, hKey=0000000e, hHash=00000000, Final=0, dwFlags=00000000, pbData=0x21ac6c, pdwDataLen=0x32efb0) 002f:Ret rsaenh.CPDecrypt() retval=00000001 ret=310154d3 002f:Ret advapi32.CryptDecrypt() retval=00000001 ret=7d599a0a --- snip ---
Compare the clear text password with decrypted buffer contents:
--- snip --- 002f:Call ntdll.wcscmp(0020dac0 L"@EtyL19Drt64Clmh80LKt$sXa239H&qL93*N5LK7",0021ac6c L"\d85a\7901\88fb\56bf\6225\4f1c\82c5\1ee6\d595\45a9\5610\bb68\8dff\ab91\7cbb\b114\1c96\9216\6147\ef03\4cdc\be60\534c\90df\194d\f2f3\68ef\8409\adc2\cdb2\a813\ddb6\9762\dbe9\29d5\8ff9\fd6b\f036\fb5f\f4ad\2452\7d48\653d\66d5\f687\bf02\b98d\856b\0101\0006\0001") ret=7d585976 002f:Ret ntdll.wcscmp() retval=ffff27e6 ret=7d585976 --- snip ---
No match, hence the database error.
The trace log sequences shown above roughly translate to following pseudo code snippet:
NOTE: might not be 100% accurate (and compilable).
--- snip --- const WCHAR *containerW = L"SSCE Key Container 3.5"; const WCHAR *providerW = L"Microsoft Enhanced RSA and AES Cryptographic Provider";
HCRYPTPROV provider = NULL; CryptAcquireContext(&provider, containerW, providerW, PROV_RSA_AES, 0);
HCRYPTHASH hash = NULL; CryptCreateHash(provider, CALG_SHA1, 0, 0, &hash);
const WCHAR *passwordW = L"@EtyL19Drt64Clmh80LKt$sXa239H&qL93*N5LK7"; int len = wcslen(passwordW)*sizeof(WCHAR); CryptHashData(hash, (BYTE *)passwordW, len, 0);
HCRYPTHASH duphash = NULL; CryptDuplicateHash(hash, 0, 0, &duphash); /* not sure where this magic value comes from, passed through member data/hard coded somewhere? */ DWORD data2 = 0x99D106F9; len = sizeof(data2); CryptHashData(duphash, (BYTE *)&data2, len, 0);
HCRYPTKEY key; CryptDeriveKey( provider, CALG_AES_128, duphash, 0, &key);
/* encrypted buffer content read from database (dumped from memory using debugger and converted to C-like array) */ const BYTE crypt_bytes[] = { /* $+00 */ 0x44,0x28,0xCC,0x3B,0xAE,0xAA,0xFB,0x58,0x4F,0x8F,0x8C,0xB1,0xE6,0x1D,0x4B,0xBA, /* $+10 */ 0xB6,0x90,0x25,0xCA,0x79,0x58,0xDC,0x33,0x9D,0xEA,0x6D,0xEC,0x37,0x55,0xAB,0x03, /* $+20 */ 0xC8,0x90,0x13,0xC3,0x75,0xD9,0xA9,0xC0,0x78,0xC2,0x2A,0xFC,0x95,0x95,0xEA,0xA3, /* $+30 */ 0x07,0x01,0xA9,0x56,0x5E,0xBC,0xDF,0xA4,0xB8,0x01,0xFE,0x50,0xA9,0x07,0xD9,0x69, /* $+40 */ 0x65,0x10,0x03,0x9F,0x53,0x8F,0x6B,0x49,0x6F,0xDA,0xB3,0xB4,0xF6,0x74,0xB5,0x70, /* $+50 */ 0x65,0x7D,0x07,0xA9,0x23,0xF7,0x7B,0xD7,0xB3,0x4E,0xA1,0x2B,0x03,0xB8,0x66,0xCF, };
BYTE buffer[sizeof(crypt_bytes)]; len = sizeof(buffer); memcpy(buffer, crypt_bytes, len);
CryptDecrypt( key, 0, FALSE, 0, buffer, &len);
if( !wcscmp( password, (WCHAR*)buffer)) { /* ok, decrypted passphrase match */ } --- snip ---
The decrypted buffer should match the cleartext passphrase. Actually I don't know which part is at fault here ... RSA AES-128 provider (rsaenh), hash generation?
With that snippet given (and converted to small C program) it might be easier to reproduce without having to run/debug through this .NET app.
$ sha1sum setup.exe b375e91d4b54cd6017895e7eaee3e838349ebf0d setup.exe
$ wine --version wine-1.3.37
Regards