Skip to content

Instantly share code, notes, and snippets.

@leecher1337
Created November 8, 2021 01:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leecher1337/c7ccfaf65f3bb6045c9033f964ca1f10 to your computer and use it in GitHub Desktop.
Save leecher1337/c7ccfaf65f3bb6045c9033f964ca1f10 to your computer and use it in GitHub Desktop.
C implementation of .NET PasswordDeriveBytes function
// C implementation of .NET PasswordDeriveBytes function for Win32
// Written by leecher@dose.0wnz.at
/*
PBKDF1_MS (P, S, c, dkLen)
Options: Hash underlying hash function
Input: P password, an octet string
S salt, an eight-octet string
c iteration count, a positive integer
dkLen intended length in octets of derived key,
a positive integer, bounded to 100 times
the output size of Hash
Output: DK derived key, a dkLen-octet string
Steps:
1. If dkLen > 100 * Hash output
"derived key too long" and stop.
2. Apply the underlying hash function Hash for c iterations to the
concatenation of the password P and the salt S,
apply the Microsoft extension by hashing the concatenation of
an encoded counter and the output of the hash produced for
iteration c - 1, then extract the first dkLen octets to produce
a derived key DK:
T_1 = Hash (P || S) ,
T_2 = Hash (T_1) ,
...
T_c = Hash (T_{c-1}) ,
R_1 = T_c ,
R_2 = Hash (Ctr(1) || T_{c-1}) ,
...
R_n = Hash (Ctr(n-1) || T_{c-1}) ,
R = R_1 || R_2 || ... || R_n
DK = R<0..dkLen-1>
3. Output the derived key DK.
The Ctr function converts the given number to a decimal
representation in ASCII.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <stdlib.h>
#define SHA1_BYTES_LEN 20
struct stPasswordDeriveBytes
{
// Input parameters
PBYTE password;
int password_length;
PBYTE salt;
int salt_length;
int iterations;
// Internal states
BYTE _baseValue[SHA1_BYTES_LEN];
PBYTE _extra;
int _extraLength;
int _extraCount;
int hashnumber;
HCRYPTPROV hProv;
};
static BOOL SHA1(HCRYPTPROV hProv, PBYTE pData, DWORD cbData, PBYTE rgbHash)
{
HCRYPTHASH hHash;
DWORD cbHash = SHA1_BYTES_LEN;
BOOL bRet = FALSE;
if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
{
bRet =
CryptHashData(hHash, pData, cbData, 0) &&
CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0);
CryptDestroyHash(hHash);
}
return bRet;
}
static BOOL ComputeBaseValue(struct stPasswordDeriveBytes *stPW)
{
HCRYPTHASH hHash;
DWORD cbHash = SHA1_BYTES_LEN;
int i;
BOOL bRet;
if (!CryptCreateHash(stPW->hProv, CALG_SHA1, 0, 0, &hHash)) return FALSE;
if (bRet = CryptHashData(hHash, stPW->password, stPW->password_length, 0))
{
if (stPW->salt)
bRet = CryptHashData(hHash, stPW->salt, stPW->salt_length, 0);
if (bRet) bRet = CryptGetHashParam(hHash, HP_HASHVAL, stPW->_baseValue, &cbHash, 0);
}
CryptDestroyHash(hHash);
for (i=0; i<stPW->iterations-2; i++)
{
if (!SHA1(stPW->hProv, stPW->_baseValue, sizeof(stPW->_baseValue), stPW->_baseValue))
return FALSE;
}
return bRet;
}
static PBYTE ComputeBytes(struct stPasswordDeriveBytes *stPW, int cb, int *piSize)
{
int len_extra;
PBYTE rgb, p;
*piSize = ((cb+SHA1_BYTES_LEN-1)/SHA1_BYTES_LEN)*SHA1_BYTES_LEN;
if (!(rgb = p = malloc(*piSize + 10)))
return NULL;
do
{
len_extra = stPW->hashnumber?sprintf (p, "%d", stPW->hashnumber):0;
memcpy(p + len_extra, stPW->_baseValue, SHA1_BYTES_LEN);
if (!SHA1(stPW->hProv, p, SHA1_BYTES_LEN + len_extra, p))
return NULL;
p += SHA1_BYTES_LEN;
stPW->hashnumber++;
} while (cb > (p-rgb));
return rgb;
}
BOOL PasswordDeriveBytes_Prepare(struct stPasswordDeriveBytes *stPW, PBYTE password, PBYTE salt)
{
memset(stPW, 0, sizeof(struct stPasswordDeriveBytes));
if (!CryptAcquireContext(&stPW->hProv,NULL,NULL,PROV_RSA_AES,0)) return FALSE;
if (password)
{
stPW->password = password;
stPW->password_length = strlen(password);
}
if (salt)
{
stPW->salt = salt;
stPW->salt_length = strlen(salt);
}
stPW->iterations = 100;
return TRUE;
}
BOOL PasswordDeriveBytes_GetBytes(struct stPasswordDeriveBytes *stPW, unsigned char *result, int cb)
{
int ib = 0, rgbLength;
PBYTE rgb;
if (cb<1) return FALSE;
if (!*(PDWORD)stPW->_baseValue)
ComputeBaseValue(stPW);
else if (stPW->_extra)
{
ib = stPW->_extraLength - stPW->_extraCount;
if (ib >= cb)
{
memcpy(result, stPW->_extra + stPW->_extraCount, cb);
if (ib > cb) stPW->_extraCount += cb;
else
{
free(stPW->_extra);
stPW->_extra = NULL;
}
return TRUE;
} else {
// NB: The source offset should really be _extraCount instead
// However, changing this would constitute a breaking change compared
// to what has shipped in .NET V1.x.
memcpy(result, stPW->_extra + ib, ib);
free(stPW->_extra);
stPW->_extra = NULL;
}
}
if (rgb = ComputeBytes(stPW, cb-ib, &rgbLength))
{
memcpy(result + ib, rgb, cb-ib);
if (rgbLength + ib > cb)
{
stPW->_extra = rgb;
stPW->_extraLength = rgbLength;
stPW->_extraCount = cb-ib;
}
else free(rgb);
}
return TRUE;
}
void PasswordDeriveBytes_End(struct stPasswordDeriveBytes *stPW)
{
if (stPW->hProv) CryptReleaseContext(stPW->hProv, 0);
}
/* Usage example:
static void GetPasswordDerivedKey(char *pszPassword, char *pszSalt, PBYTE keyPassword, PBYTE ivPassword)
{
struct stPasswordDeriveBytes stPW;
PasswordDeriveBytes_Prepare(&stPW, pszPassword, pszSalt);
PasswordDeriveBytes_GetBytes(&stPW, keyPassword, 32);
PasswordDeriveBytes_GetBytes(&stPW, ivPassword, 16);
PasswordDeriveBytes_End(&stPW);
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment