#include "stdafx.h"
#include "PasswordUtils.h"
#include <stdlib.h>
const int NUM_FILLER_CHARS = 8;
const CString s_strFillerChars = _T("SLWMTQYP");
const CString s_strPossibleChars = _T("0123456789ABCDEFSLWMTQYP");
// local functions
namespace PasswordUtilsLocals
{
void MakeLength(int nTargetLen, CString& rString);
long RandomNumber(int nMin, int nMax);
bool FindIllegalChars(CString& rStrKey);
void RemoveFillers(CString& rStrKey);
int HexStrToInt(const TCHAR *value);
}
void
SbjCore::PasswordUtils::EncryptDate(
int nMonth, // [in] month
int nDay, // [in] day
int nYear, // [in] year
int nType, // [in] type of password, defined by calling app
CString& rStrKey) // [out] resulting password
{
// Seed the random number generator with system time
CTime t = CTime::GetCurrentTime();
UINT nTime = static_cast<UINT>( t.GetTime() );
srand( nTime );
//for(int i = 0; i < 100; i ++)
//{
// TRACE(_T("%d\n"), EncryptPasswordLocals::RandomNumber(0, NUM_FILLER_CHARS - 1) );
//}
// multiply by 100
long nBigDay = nDay * 100;
long nBigMonth = nMonth * 100;
long nBigYear = (nYear - 2000) * 100;
// Get string versions in hex
CString strDay;
strDay.Format(_T("%X"), nBigDay);
CString strMonth;
strMonth.Format(_T("%X"), nBigMonth);
CString strYear;
strYear.Format(_T("%X"), nBigYear);
// Pad with filler characters to make it 6 chars long
PasswordUtilsLocals::MakeLength(6, strDay);
PasswordUtilsLocals::MakeLength(6, strMonth);
PasswordUtilsLocals::MakeLength(6, strYear);
// Now reverse each string and concatenate with dashes
rStrKey.Format(_T("%s-%s-%s-"), strYear.MakeReverse(), strDay.MakeReverse(), strMonth.MakeReverse());
// Get a 5 digit random number to use for the "type" section
long n5dig = PasswordUtilsLocals::RandomNumber(10001, 99990);
CString strTypeSection;
strTypeSection.Format(_T("%u"), n5dig);
CString strTypeVal;
strTypeVal.Format(_T("%u"), nType);
// Insert the type value in the 4th position in the type string
strTypeSection.Insert(3, strTypeVal);
rStrKey += strTypeSection;
}
// -------------------- DecryptPassword ---------------------
// Deciphers the password into a date. nType is retuned as
// the same in passed into CreatePassword. If the password
// cannot be deciphered, false is returned and all 'out' params
// are left unchanged.
// ------------------------------------------------------------
bool
SbjCore::PasswordUtils::DecryptPassword(
LPCTSTR pPassword, // [in] password to decipher
int& nMonth, // [out] month
int& nDay, // [out] day
int& nYear, // [out] year
int& nType) // [out] type, same value passed in to CreatePassword
{
bool bSuccess = true;
CString strKey(pPassword);
// Verify length
if(strKey.GetLength() != 27)
{
bSuccess = false;
}
// verify the dashes
if(bSuccess)
{
if( strKey[6] != _TCHAR('-') || strKey[13] != _TCHAR('-') || strKey[20] != _TCHAR('-') )
{
bSuccess = false;
}
}
// parse out the four strings
CString strDay;
CString strMonth;
CString strYear;
CString strType;
strYear = strKey.Left(6);
strDay = strKey.Mid(7, 6);
strMonth = strKey.Mid(14, 6);
strType = strKey.Mid(21, 6);
// verify only legal characters exist
if(bSuccess)
{
if( PasswordUtilsLocals::FindIllegalChars(strDay) ||
PasswordUtilsLocals::FindIllegalChars(strMonth)||
PasswordUtilsLocals::FindIllegalChars(strYear) ||
PasswordUtilsLocals::FindIllegalChars(strType) )
{
bSuccess = false;
}
}
// reverse the strings and remove the filler characters
if(bSuccess)
{
strDay = strDay.MakeReverse();
strMonth = strMonth.MakeReverse();
strYear = strYear.MakeReverse();
PasswordUtilsLocals::RemoveFillers(strDay);
PasswordUtilsLocals::RemoveFillers(strMonth);
PasswordUtilsLocals::RemoveFillers(strYear);
// What is left at this point is a hex version of the month, day and year
// still multiplied by 100.
// Convert it back to decimal numbers
_stscanf_s (strDay,_T("%x"), &nDay);
_stscanf_s (strMonth,_T("%x"), &nMonth);
_stscanf_s (strYear,_T("%x"), &nYear);
// Divide it back by 100
nDay /= 100;
nMonth /= 100;
// The year used a 2 digit format, so it gets 2000 added on
nYear = nYear/100 + 2000;
// Extract the type from its string
CString strTypeVal(strType[3]);
nType = atoi(strTypeVal);
}
return bSuccess;
}
void
PasswordUtilsLocals::MakeLength(int nTargetLen, CString& rString)
{
int nLen = rString.GetLength();
int nPos = 0;
while(nLen < nTargetLen)
{
int nIndex = PasswordUtilsLocals::RandomNumber(0, NUM_FILLER_CHARS - 1);
nLen = rString.Insert( nPos, s_strFillerChars.GetAt(nIndex) );
nPos += 2;
}
}
long
PasswordUtilsLocals::RandomNumber(int nMin, int nMax)
{
// Get the random number
double dRanNum = static_cast<double>( rand() );
// convert it to a range
double dRandMax = static_cast<double>(RAND_MAX) + 1.0;
double dMaxRange = static_cast<double>(nMax);
double dMinRange = static_cast<double>(nMin);
int n = static_cast<int>( dRanNum / dRandMax * (dMaxRange - dMinRange) + dMinRange );
return n;
}
bool
PasswordUtilsLocals::FindIllegalChars(CString& rStrKey)
{
bool bInvalid = false;
int nLen = rStrKey.GetLength();
for(int i = 0; i < nLen; i ++)
{
if( s_strPossibleChars.Find(rStrKey.GetAt(i)) == -1 )
{
bInvalid = true;
break;
}
}
return bInvalid;
}
void
PasswordUtilsLocals::RemoveFillers(CString& rStrKey)
{
CString strTemp(rStrKey);
rStrKey.Empty();
int nLen = strTemp.GetLength();
for(int i = 0; i < nLen; i ++)
{
// If it's not a filler add it into the string
if( s_strFillerChars.Find(strTemp.GetAt(i)) == -1 )
{
rStrKey += strTemp.GetAt(i);
}
}
}