[Bug 29449] USPS shipping assistant version 3.8 won't start (Microsoft SQL Server Compact database metadata incorrectly decrypted, enh. RSA AES-128 provider)

wine-bugs at winehq.org wine-bugs at winehq.org
Fri Jan 13 17:20:43 CST 2012


http://bugs.winehq.org/show_bug.cgi?id=29449

Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |dotnet, download
             Status|UNCONFIRMED                 |NEW
                URL|                            |https://www.usps.com/busine
                   |                            |ss/shipping-assistant.htm
                 CC|                            |focht at 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 at 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

-- 
Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
Do not reply to this email, post in Bugzilla using the
above URL to reply.
------- You are receiving this mail because: -------
You are watching all bug changes.



More information about the wine-bugs mailing list