428 lines
11 KiB
C++
428 lines
11 KiB
C++
// Crypto.cpp : Implementation of CCrypto
|
|
|
|
#include "stdafx.h"
|
|
#include "Crypto.h"
|
|
|
|
|
|
// CCrypto
|
|
|
|
#include "hmac_sha1.h"
|
|
#include "../Crypto/sha1.h"
|
|
#include "../Crypto/sha256.h"
|
|
#include "../Crypto/md5.h"
|
|
#include "../Crypto/hmac.h"
|
|
#include "../Crypto/base64.h"
|
|
|
|
#include <math.h>
|
|
#include <comdef.h>
|
|
|
|
// Copied from chromium's chrome_util.cc
|
|
CString base32_encode(const uint8_t* bytes, size_t size)
|
|
{
|
|
static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; // RFC 4648
|
|
|
|
// Eliminate special cases first.
|
|
if (size == 0)
|
|
{
|
|
return "";
|
|
}
|
|
else if (size == 1)
|
|
{
|
|
CString ret;
|
|
ret += (kEncoding[(bytes[0] & 0xf8) >> 3]);
|
|
ret += (kEncoding[(bytes[0] & 0x07) << 2]);
|
|
return ret;
|
|
}
|
|
/*
|
|
else if (size >= numeric_limits<size_t>::max() / 8)
|
|
{
|
|
// If |size| is too big, the calculation of |encoded_length| below will
|
|
// overflow.
|
|
return throw "ERROR Base32::encode(): Encoding doesn't match expected length.";
|
|
return "";
|
|
}
|
|
*/
|
|
// Overestimate the number of bits in the string by 4 so that dividing by 5
|
|
// is the equivalent of rounding up the actual number of bits divided by 5.
|
|
const size_t encoded_length = (size * 8 + 4) / 5;
|
|
|
|
CString ret;
|
|
ret.Preallocate((int)encoded_length);
|
|
|
|
// A bit stream which will be read from the left and appended to from the
|
|
// right as it's emptied.
|
|
uint16_t bit_stream = (bytes[0] << 8) + bytes[1];
|
|
size_t next_byte_index = 2;
|
|
int free_bits = 0;
|
|
while (free_bits < 16)
|
|
{
|
|
// Extract the 5 leftmost bits in the stream
|
|
ret += (kEncoding[(bit_stream & 0xf800) >> 11]);
|
|
bit_stream <<= 5;
|
|
free_bits += 5;
|
|
|
|
// If there is enough room in the bit stream, inject another byte (if there
|
|
// are any left...).
|
|
if (free_bits >= 8 && next_byte_index < size)
|
|
{
|
|
free_bits -= 8;
|
|
bit_stream += bytes[next_byte_index++] << free_bits;
|
|
}
|
|
}
|
|
|
|
if (ret.GetLength() != encoded_length)
|
|
{
|
|
throw "ERROR Base32::encode(): Encoding doesn't match expected length.";
|
|
return "";
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::base64(BSTR data, VARIANT_BOOL padding /*FALSE*/, VARIANT_BOOL urlsafe /*FALSE*/, BSTR* pVal)
|
|
{
|
|
CString adata(data);
|
|
CString res = base64_encode((BYTE *)adata.GetBuffer(), adata.GetLength(), !!padding, !!urlsafe).c_str();
|
|
|
|
CComBSTR bstrString(res);
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::base64_decode(BSTR data, BSTR* pVal)
|
|
{
|
|
CString cs(data);
|
|
|
|
std::string res = ::base64_decode((LPCTSTR)cs);
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
#define hex(c) (((c)>='a')?(c)-'a'+10:((c)>='A')?(c)-'A'+10:(c)-'0')
|
|
STDMETHODIMP CCrypto::hex2base64(BSTR data, VARIANT_BOOL padding /*FALSE*/, VARIANT_BOOL urlsafe /*FALSE*/, BSTR* pVal)
|
|
{
|
|
CString from(data);
|
|
unsigned int len = from.GetLength();
|
|
uint8_t* to = new uint8_t[len / 2];
|
|
//if (pbData == NULL) { fclose(fpIn); return false; }
|
|
|
|
for (unsigned int i = 0; i < len / 2; i++)
|
|
{
|
|
to[i] = hex(from.GetAt(i * 2)) * 16 + hex(from.GetAt(i * 2 + 1));
|
|
}
|
|
std::string res = base64_encode(to, len / 2, !!padding, !!urlsafe);
|
|
delete[] to;
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::base32(BSTR data, BSTR* pVal)
|
|
{
|
|
CString adata(data);
|
|
CString res = base32_encode((BYTE *)adata.GetBuffer(), adata.GetLength());
|
|
|
|
CComBSTR bstrString(res);
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::hex2base32(BSTR data, BSTR* pVal)
|
|
{
|
|
CString from(data);
|
|
unsigned int len = from.GetLength();
|
|
uint8_t* to = new uint8_t[len / 2];
|
|
//if (pbData == NULL) { fclose(fpIn); return false; }
|
|
|
|
for (unsigned int i = 0; i < len / 2; i++)
|
|
{
|
|
to[i] = hex(from.GetAt(i * 2)) * 16 + hex(from.GetAt(i * 2 + 1));
|
|
}
|
|
CString res = base32_encode(to, len / 2);
|
|
|
|
CComBSTR bstrString(res);
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::b64_hmac_sha1(BSTR key, BSTR data, BSTR* pVal)
|
|
{
|
|
BYTE digest[20];
|
|
CString akey(key);
|
|
CString adata(data);
|
|
|
|
CHMAC_SHA1 HMAC_SHA1 ;
|
|
HMAC_SHA1.HMAC_SHA1((BYTE *)adata.GetBuffer(), adata.GetLength(), (BYTE *)akey.GetBuffer(), akey.GetLength(), digest) ;
|
|
CString res = base64_encode(digest, 20, false).c_str(); // no padding
|
|
|
|
CComBSTR bstrString(res);
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
const char *hex_tab = "0123456789abcdef";
|
|
STDMETHODIMP CCrypto::hex_hmac_sha1(BSTR key, BSTR data, BSTR* pVal)
|
|
{
|
|
CString akey(key);
|
|
CString adata(data);
|
|
std::string res = hmac<SHA1>((BYTE *)adata.GetBuffer(), adata.GetLength(), (BYTE *)akey.GetBuffer(), akey.GetLength());
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::hex_sha1(BSTR data, BSTR* pVal)
|
|
{
|
|
CString adata(data);
|
|
|
|
SHA1 sha1;
|
|
sha1.reset();
|
|
sha1.add((UINT_8*)adata.GetBuffer(), adata.GetLength());
|
|
std::string res = sha1.getHash();
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
bstrString.ToLower();
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::hex_hmac_sha256(BSTR key, BSTR data, BSTR* pVal)
|
|
{
|
|
CString akey(key);
|
|
CString adata(data);
|
|
std::string res = hmac<SHA256>((BYTE *)adata.GetBuffer(), adata.GetLength(), (BYTE *)akey.GetBuffer(), akey.GetLength());
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::hex_sha256(BSTR data, BSTR* pVal)
|
|
{
|
|
CString adata(data);
|
|
|
|
SHA256 sha256;
|
|
sha256.reset();
|
|
sha256.add((UINT_8*)adata.GetBuffer(), adata.GetLength());
|
|
std::string res = sha256.getHash();
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
bstrString.ToLower();
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::hex_md5(BSTR data, BSTR* pVal)
|
|
{
|
|
CString adata(data);
|
|
|
|
MD5 md5;
|
|
md5.reset();
|
|
md5.add((UINT_8*)adata.GetBuffer(), adata.GetLength());
|
|
std::string res = md5.getHash();
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
bstrString.ToLower();
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
// Big Endian
|
|
static inline void be32enc(void *pp, uint32_t x)
|
|
{
|
|
uint8_t * p = (uint8_t *)pp;
|
|
p[3] = x & 0xff;
|
|
p[2] = (x >> 8) & 0xff;
|
|
p[1] = (x >> 16) & 0xff;
|
|
p[0] = (x >> 24) & 0xff;
|
|
}
|
|
|
|
/* F(P, S, c, i) = U1 xor U2 xor ... Uc
|
|
* U1 = PRF(P, S || i)
|
|
* U2 = PRF(P, U1)
|
|
* Uc = PRF(P, Uc-1)
|
|
*/
|
|
#define SHA1_MAC_LEN 20
|
|
STDMETHODIMP CCrypto::hex_pbkdf2(BSTR pPassword, BSTR pSalt, ULONG pCount, ULONG pLength, BSTR* pVal)
|
|
{
|
|
CString apass(pPassword);
|
|
CString asalt(pSalt);
|
|
int saltlen = asalt.GetLength();
|
|
BYTE *saltbuff = (BYTE *)asalt.GetBufferSetLength(saltlen + 4); // Ruimte maken voor counter
|
|
int passlen = apass.GetLength();
|
|
BYTE *passbuff = (BYTE *)apass.GetBuffer();
|
|
|
|
size_t i;
|
|
uint8_t U[SHA1_MAC_LEN];
|
|
uint8_t T[SHA1_MAC_LEN]; // Hierin komt het XOR resultaat
|
|
|
|
CString res;
|
|
|
|
/* Iterate through the blocks. */
|
|
for (i = 0; i * SHA1_MAC_LEN < pLength; i++)
|
|
{
|
|
/* Generate INT(i + 1). */
|
|
be32enc(saltbuff + saltlen, i + 1);
|
|
|
|
/* Compute U_1 = PRF(P, S || INT(i)). */
|
|
CHMAC_SHA1 HMAC_SHA1;
|
|
HMAC_SHA1.HMAC_SHA1(saltbuff, saltlen + 4, passbuff, passlen, U);
|
|
|
|
/* T_1 = U_1 ... */
|
|
memcpy(T, U, SHA1_MAC_LEN);
|
|
|
|
for (uint64_t j = 2; j <= pCount; j++)
|
|
{
|
|
/* Compute U_j. */
|
|
HMAC_SHA1.HMAC_SHA1(U, SHA1_MAC_LEN, passbuff, passlen, U);
|
|
|
|
/* ... xor U_j ... */
|
|
for (int k = 0; k < sizeof(U); k++)
|
|
T[k] ^= U[k];
|
|
}
|
|
|
|
/* Copy as many bytes as necessary into buf. */
|
|
int clen = pLength - i * SHA1_MAC_LEN;
|
|
if (clen > SHA1_MAC_LEN)
|
|
clen = SHA1_MAC_LEN;
|
|
|
|
for (int k = 0; k < clen; k++)
|
|
{
|
|
res = res + hex_tab[T[k] >> 4];
|
|
res = res + hex_tab[T[k] & 0xF];
|
|
}
|
|
}
|
|
|
|
CComBSTR bstrString(res);
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
#define SHA1_MAX_FILE_BUFFER (32 * 20 * 820)
|
|
bool HashFile(const TCHAR* tszFileName, std::string &result)
|
|
{
|
|
if (tszFileName == NULL) return false;
|
|
|
|
FILE* fpIn;
|
|
fpIn = _fsopen(tszFileName, _T("rb"), _SH_DENYNO);
|
|
if (fpIn == NULL) return false;
|
|
|
|
UINT_8* pbData = new UINT_8[SHA1_MAX_FILE_BUFFER];
|
|
if (pbData == NULL) { fclose(fpIn); return false; }
|
|
|
|
bool bSuccess = true;
|
|
SHA1 sha1;
|
|
sha1.reset();
|
|
while (true)
|
|
{
|
|
const size_t uRead = fread(pbData, 1, SHA1_MAX_FILE_BUFFER, fpIn);
|
|
|
|
if (uRead > 0)
|
|
sha1.add(pbData, static_cast<UINT_32>(uRead));
|
|
|
|
if (uRead < SHA1_MAX_FILE_BUFFER)
|
|
{
|
|
if (feof(fpIn) == 0) bSuccess = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose(fpIn);
|
|
delete[] pbData;
|
|
result = sha1.getHash();
|
|
return bSuccess;
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::hex_sha1_file(BSTR fname, BSTR* pVal)
|
|
{
|
|
std::string res;
|
|
if (!HashFile(CString(fname), res)) // Hash the contents of the file
|
|
return myAtlReportError (GetObjectCLSID(), "\nCCrypto::hex_sha1_file('%ls')", (LPCSTR)fname);
|
|
|
|
CComBSTR bstrString(res.c_str());
|
|
bstrString.ToLower();
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CCrypto::hotp(BSTR hexseed, LONGLONG moving_factor, BYTE digits, BSTR* pVal)
|
|
{
|
|
BYTE digest[20];
|
|
CSHA1 sha1;
|
|
|
|
if (digits < 6 || digits > 8)
|
|
return E_INVALIDARG;
|
|
|
|
CString sseed(hexseed);
|
|
if (sseed.GetLength() > 40)
|
|
return E_INVALIDARG;
|
|
|
|
BYTE seed[20];
|
|
for(int i = 0; i < sizeof(seed); i++)
|
|
{
|
|
BYTE hi=sseed[2*i];
|
|
hi-=(hi<'A' ? '0' : 'A'-10);
|
|
BYTE lo=sseed[2*i+1];
|
|
lo-=(lo<'A' ? '0' : 'A'-10);
|
|
seed[i] = (hi<<4) | (lo & 0x0F); // " & 0x0F" deals with lower-case characters
|
|
}
|
|
|
|
ATLASSERT(sizeof(moving_factor) == 8);
|
|
BYTE counter[sizeof(moving_factor)];
|
|
for (int i = 0; i < sizeof (counter); i++)
|
|
counter[i] = (BYTE)(moving_factor >> ((sizeof (moving_factor) - i - 1) * 8)) & 0xFF;
|
|
|
|
CHMAC_SHA1 HMAC_SHA1 ;
|
|
HMAC_SHA1.HMAC_SHA1((BYTE *)(&counter), sizeof(counter), seed, sizeof(seed), digest) ;
|
|
|
|
CString res;
|
|
|
|
BYTE offset = digest[sizeof (digest) - 1] & 0x0f;
|
|
|
|
uint64_t S = (((digest[offset] & 0x7f) << 24)
|
|
| ((digest[offset + 1] & 0xff) << 16)
|
|
| ((digest[offset + 2] & 0xff) << 8) | ((digest[offset + 3] & 0xff)));
|
|
|
|
res.Format("%lld",1000000000L + S);
|
|
res = res.Right(digits);
|
|
/*
|
|
for(int i = 0; i < 20; i++)
|
|
{
|
|
res = res + hex_tab[digest[i] >> 4];
|
|
res = res + hex_tab[digest[i] & 0xF];
|
|
}
|
|
*/
|
|
CComBSTR bstrString(res);
|
|
bstrString.ToLower();
|
|
|
|
return bstrString.CopyTo(pVal);
|
|
}
|
|
|
|
// RtlGenRandom is wel enigszins gedocumenteerd door Microsoft
|
|
// Onwaarschijnlijk dat ze die ooit verwijderen
|
|
#define RtlGenRandom SystemFunction036
|
|
extern "C" BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
|
|
# pragma comment(lib, "advapi32.lib")
|
|
|
|
// pLength is het aantal bytes. De resulterende hex zal dubbel zo groot zijn
|
|
STDMETHODIMP CCrypto::hex_random(ULONG pLength, BSTR* pVal)
|
|
{
|
|
if (pLength < 1 || pLength > 32)
|
|
return E_INVALIDARG;
|
|
BYTE bytes[32];
|
|
|
|
if (!RtlGenRandom(bytes, pLength))
|
|
return myAtlReportError(GetObjectCLSID(), "ERROR CCrypto::hex_random: RtlGenRandom failed");
|
|
|
|
CString res;
|
|
res.GetBufferSetLength(pLength * 2);
|
|
for (unsigned int i = 0; i < pLength; i++)
|
|
{
|
|
res.SetAt(i * 2, hex_tab[bytes[i] >> 4]);
|
|
res.SetAt(i * 2 + 1, hex_tab[bytes[i] & 0xF]);
|
|
}
|
|
SecureZeroMemory(bytes, pLength);
|
|
res.ReleaseBuffer(pLength * 2);
|
|
|
|
CComBSTR bstrString(res);
|
|
return bstrString.CopyTo(pVal);
|
|
}
|