Files
Slnkdwf/SlnkDWFCom/Crypto.cpp
Jos Groot Lipman f38f6a3b93 FSN#37847 Versie 4.16 met fixje voor hash van niet bestaande files
svn path=/Slnkdwf/trunk/; revision=30883
2016-09-27 15:23:29 +00:00

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;
fopen_s(&fpIn, tszFileName, _T("rb"));
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);
}