Saturday, 7 September 2013

CNG AES -> C# AES

CNG AES -> C# AES

I want to make a program in C# that can open KeePass 1.x kdb files. I
downloaded sources and trying to port password database reading
functionality. Database contents is encrypted. Encryption key is obtained
the following way:
User enters password;
SHA256 hash of password is calculated and split in two 128-but halves;
Several rounds of AES is applied to each half of hash using key from
database header;
Halves are concatenated back;
Result is salted with salt from database header;
SHA256 hash of step 4 result is calculated. That is the encryption key.
I'm stuck on step 3. KeePass uses CNG for AES. Simplified source (for half
of hash, other half had the same applied to it):
BCRYPT_ALG_HANDLE hAes = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BYTE pbKey32[32] = <encryption key>;
BYTE pbData16[16] = <half of hash from step 2>;
BCryptOpenAlgorithmProvider(&hAes, BCRYPT_AES_ALGORITHM, NULL, 0);
DWORD dwKeyObjLen = 0;
ULONG uResult = 0;
BCryptGetProperty(hAes, BCRYPT_OBJECT_LENGTH, (PUCHAR)&dwKeyObjLen,
sizeof(DWORD), &uResult, 0);
BCryptSetProperty(hAes, BCRYPT_CHAINING_MODE,
(PUCHAR)BCRYPT_CHAIN_MODE_ECB,
static_cast<ULONG>((wcslen(BCRYPT_CHAIN_MODE_ECB) + 1) * sizeof(wchar_t)),
0);
BCRYPT_KEY_DATA_BLOB_32 keyBlob;
ZeroMemory(&keyBlob, sizeof(BCRYPT_KEY_DATA_BLOB_32));
keyBlob.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
keyBlob.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
keyBlob.cbKeyData = 32;
memcpy(keyBlob.pbData, pbKey32, 32);
pKeyObj = new UCHAR[dwKeyObjLen];
BCryptImportKey(hAes, NULL, BCRYPT_KEY_DATA_BLOB, &hKey, pKeyObj.get(),
dwKeyObjLen, (PUCHAR)&keyBlob, sizeof(BCRYPT_KEY_DATA_BLOB_32), 0);
for (int i = 0; i < rounds; ++i)
{
BCryptEncrypt(hKey, pbData16, 16, NULL, NULL, 0, pbData16, 16,
&uResult, 0);
}
So, as far as I understand, it uses AES algorithm with ECB chaining mode
and it passes NULL and 0 as 5th and 6th argument of BCryptEncrypt function
meaning it will not use initialization vector.
Now, how do I do the same in C#? I wrote following function to do one
round of transformation (based on MSDN sample):
public static byte[] KeyTransform(byte[] buffer, byte[] key)
{
Aes aes = Aes.Create();
aes.Key = key;
aes.BlockSize = 128;
aes.KeySize = key.Length * 8;
aes.Mode = CipherMode.ECB;
//aes.IV = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0};
ICryptoTransform ct = aes.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, ct,
CryptoStreamMode.Write))
{
using (BinaryWriter bw = new BinaryWriter(cs))
{
bw.Write(buffer);
}
cs.Flush();
}
return ms.ToArray();
}
}
Then I compare buffers after one round of AES applied in original and in
my code. My code produces different results from original. How do I fix
it?
By the way, no matter if I specify IV or not my code produces different
result every time (so I believe IV is always generated and used). If I try
to set aes.IV to null it throws exception saying that I can't set it to
null.

No comments:

Post a Comment