Created
November 8, 2021 01:16
-
-
Save leecher1337/c7ccfaf65f3bb6045c9033f964ca1f10 to your computer and use it in GitHub Desktop.
C implementation of .NET PasswordDeriveBytes function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// C implementation of .NET PasswordDeriveBytes function for Win32 | |
// Written by [email protected] | |
/* | |
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