
PWGen 2.6
contraseñas Aleatorias y Seguras
PWGen es un profesional generador de contraseñas capaz de generar grandes cantidades de contraseñas criptográficamente, passwords seguros "clásicos", las contraseñas pronunciables, contraseñas basadas en patrones y frases de acceso que consta de palabras de las listas de palabras. Utiliza una técnica de "Random Pool", basada en criptografía fuerte para generar datos aleatorios de entradas de usuario indeterministas (pulsaciones de teclado, manejo del ratón) y los parámetros del sistema volátiles. PWGen ofrece un montón de opciones para personalizar las contraseñas a las distintas necesidades de los usuarios. Además, ofrece una fuerte encriptación de texto y la creación de archivos de datos aleatorios (que puede ser utilizado como archivos clave para las utilidades de cifrado, por ejemplo).
Características notables
- Software libre y de código abierto
- Completo Soporte Unicode
- Discreto: fácil de usar, no instala extraños archivos DLL, no escribe en el registro de Windows, ni siquiera escribe en el disco duro si no quieres, se puede desinstalar fácilmente
- Utiliza hasta la fecha criptografía (AES, SHA-2) para generar datos aleatorios para las contraseñas de alta calidad.
- Numerosas opciones de contraseña para diversos propositos
- Generación de grandes cantidades de contraseñas a la vez
- Generación de contraseñas compuestas de palabras de una lista de palabras
- La generación de contraseñas esta basado en patrones (contraseñas con formato) ofrece casi infinitas posibilidades de personalizar las contraseñas a las necesidades del usuario
- "Contraseña hasher" funcionalmente: Genera contraseñas basado en una contraseña maestra y una serie de parámetros (por ejemplo, el nombre de un sitio web), similar a "HashApass"
- Texto cifrado Seguro
- Soporte multilenguaje
- Manual profundo (52 páginas)
- Funciona con todas las versiones de Windows (32 bits y 64 bits, a partir de Windows 95 OEM Service Release 2)
Versión 2.6.0 (02/09/2015) cuenta con nuevas opciones para generar contraseñas y frases de acceso en la que cada carácter o palabra, respectivamente, se produce una sola vez; rangos de números en las contraseñas con formato; un nuevo "Proporcionar adicional entropía" de diálogo, que permite entrar o pegar cualquier texto con el fin de alimentar la entropía adicional en el "Random Pool"; y por último pero no menos importante, algunas correcciones de errores.
Codigo Fuente
PasswGen.cpp
Código:
// PasswGen.cpp
//
// PWGEN FOR WINDOWS
// Copyright (c) 2002-2015 by Christian Thoeing <c.thoeing@web.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//---------------------------------------------------------------------------
#include <vcl.h>
#include <algorithm>
#include <set>
#include <Math.hpp>
#pragma hdrstop
#include "PasswGen.h"
#include "Main.h"
#include "TntSysUtils.hpp"
#include "PhoneticTrigram.h"
#include "Language.h"
#include "StringFileStreamW.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
extern "C" char* getDiceWd(int n);
static const char* CHARSET_CODES[PASSWGEN_NUMCHARSETCODES] =
{ "<AZ>", "<az>", "<09>", "<Hex>", "<hex>", "<base64>", "<easytoread>",
"<symbols>", "<brackets>", "<punct>", "<high>" };
static const char* CHARSET_DECODES[PASSWGEN_NUMCHARSETCODES] =
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"abcdefghijklmnopqrstuvwxyz",
"0123456789",
"0123456789ABCDEF",
"0123456789abcdef",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
"",
"",
"()[]{}<>",
",.;:",
"" };
static const char* CHARSET_CODE_PHONETIC = "<phonetic>";
static const char* CHARSET_CODE_PHONETIC_MIXEDCASE = "<phoneticx>";
static const int
CHARSET_CODES_AZ = 0,
CHARSET_CODES_az = 1,
CHARSET_CODES_09 = 2,
CHARSET_CODES_Hex = 3,
CHARSET_CODES_hex = 4,
CHARSET_CODES_BASE64 = 5,
CHARSET_CODES_EASYTOREAD = 6,
CHARSET_CODES_SYMBOLS = 7,
CHARSET_CODES_BRACKETS = 8,
CHARSET_CODES_PUNCTUATION = 9,
CHARSET_CODES_HIGHANSI = 10;
static const int
CHARSET_INCLUDE_AZ = 0,
CHARSET_INCLUDE_az = 1,
CHARSET_INCLUDE_09 = 2,
CHARSET_INCLUDE_SPECIAL = 3;
static const char* CHARSET_AMBIGUOUS =
"B8G6I1l|0OQDS5Z2";
static const char* CHARSET_SYMBOLS =
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
static const char* FORMAT_PLACEHOLDERS =
"xaAUEdhHlLuvVZcCzpbsSy";
static const char* CHARSET_FORMAT[PASSWGEN_NUMFORMATCHARSETS] =
{ "", // 'x' = custom char set
"abcdefghijklmnopqrstuvwxyz0123456789",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"", // 'E' = <easytoread> character set
"0123456789",
"0123456789abcdef",
"0123456789ABCDEF",
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"aeiou",
"AEIOUaeiou",
"AEIOU",
"bcdfghjklmnpqrstvwxyz",
"BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
"BCDFGHJKLMNPQRSTVWXYZ",
",.;:",
"()[]{}<>",
"", // 's' = special symbols
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", // 'S' + special symbols
"" // 'y' = higher ANSI characters
};
static const int
CHARSET_FORMAT_x = 0,
CHARSET_FORMAT_E = 4,
CHARSET_FORMAT_s = 19,
CHARSET_FORMAT_S = 20,
CHARSET_FORMAT_y = 21;
static const int CHARSET_FORMAT_CONST[] =
{ 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
static const int FORMAT_REPEAT_MAXDEPTH = 4;
template<class T> inline int strchpos(const T* pStr, T c)
{
for (int nI = 0; pStr[nI] != '\0'; nI++) {
if (pStr[nI] == c)
return nI;
}
return -1;
}
template<class T> inline int strchpos(const T* pStr, int nLen, T c)
{
for (int nI = 0; nI < nLen; nI++) {
if (pStr[nI] == c)
return nI;
}
return -1;
}
//---------------------------------------------------------------------------
PasswordGenerator::PasswordGenerator(RandomGenerator* pRandGen)
: m_pRandGen(pRandGen) //, m_pWordList(NULL)
{
for (int nI = 0; nI < PASSWGEN_NUMCHARSETCODES; nI++)
m_charSetDecodes[nI] = CharToW32String(CHARSET_DECODES[nI]);
WString sCharSetDef = CHARSET_FORMAT[2];
SetupCharSets(sCharSetDef);
LoadWordListFile();
LoadTrigramFile();
};
//---------------------------------------------------------------------------
PasswordGenerator::~PasswordGenerator()
{
// if (m_pWordList != NULL)
// delete m_pWordList;
}
//---------------------------------------------------------------------------
w32string PasswordGenerator::MakeCharSetUnique(const w32string& sSrc,
w32string* psAmbigChars,
std::list<w32string>* pAmbigGroups,
bool blMakeAmbChSet)
{
std::set<word32> chset;
if (blMakeAmbChSet) {
std::list<w32string> groups;
int nSepPos;
if ((nSepPos = psAmbigChars->find(' ', 0)) >= 2 &&
nSepPos <= int(psAmbigChars->length()) - 3)
{
word32 lChar;
w32string sGroup;
for (int nI = 0; nI <= int(psAmbigChars->length()); nI++) {
lChar = (*psAmbigChars)[nI];
if (lChar == ' ' || lChar == '\0') {
if (sGroup.length() >= 2)
groups.push_back(sGroup);
sGroup.clear();
}
else {
std::pair<std::set<word32>::iterator,bool> ret;
if ((ret = chset.insert(lChar)).second)
sGroup.push_back(lChar);
}
}
}
else
chset.insert(psAmbigChars->begin(), psAmbigChars->end());
psAmbigChars->assign(chset.begin(), chset.end());
if (groups.size() >= 2)
*pAmbigGroups = groups;
else
pAmbigGroups->clear();
return w32string();
}
// make a first set by inserting all chars in sSrc
chset.insert(sSrc.begin(), sSrc.end());
if (pAmbigGroups != NULL && pAmbigGroups->size() >= 2) {
std::list<w32string>::iterator list_it;
for (list_it = pAmbigGroups->begin(); list_it != pAmbigGroups->end(); list_it++)
{
int nMatches = 0;
w32string::iterator it;
for (it = list_it->begin(); it != list_it->end(); it++) {
if (chset.count(*it))
nMatches++;
}
if (nMatches >= 2) {
for (it = list_it->begin(); it != list_it->end(); it++) {
chset.erase(*it);
}
}
}
}
else if (psAmbigChars != NULL) {
for (w32string::iterator it = psAmbigChars->begin();
it != psAmbigChars->end(); it++)
chset.erase(*it);
}
// w32string y(chset.begin(),chset.end());
// WString x=W32StringToWString(y);
return w32string(chset.begin(), chset.end());
}
//---------------------------------------------------------------------------
w32string PasswordGenerator::ParseCharSet(const w32string& sInput,
CharSetType& charSetType)
{
if (sInput.length() < 2)
return w32string();
if (sInput == CharToW32String(CHARSET_CODE_PHONETIC)) {
charSetType = cstPhonetic;
return CharToW32String(CHARSET_DECODES[1]); // lower-case letters a..z
}
if (sInput == CharToW32String(CHARSET_CODE_PHONETIC_MIXEDCASE)) {
charSetType = cstPhoneticMixedCase;
return CharToW32String(CHARSET_DECODES[0]) +
CharToW32String(CHARSET_DECODES[1]);
}
w32string sTemp = sInput;
int nPos;
if (sTemp[0] == '[') {
if ((nPos = sTemp.find(']')) >= 2)
sTemp.erase(0, nPos);
}
w32string sParsed;
for (int nI = 0; nI < PASSWGEN_NUMCHARSETCODES; nI++) {
w32string sCode = CharToW32String(CHARSET_CODES[nI]);
if ((nPos = sTemp.find(sCode)) >= 0) {
sTemp.erase(nPos, sCode.length());
sParsed += m_charSetDecodes[nI];
}
}
sParsed += sTemp;
w32string sCharSet = MakeCharSetUnique(sParsed, &m_sAmbigCharSet, &m_ambigGroups);
int nSetSize = sCharSet.length();
if (nSetSize < 2)
return w32string();
charSetType = cstNormal;
return sCharSet;
}
//---------------------------------------------------------------------------
void PasswordGenerator::SetupCharSets(WString& sCustomChars,
const WString& sAmbigChars,
const WString& sSpecialSymbols,
bool blExcludeAmbigChars,
bool blDetermineSubsets)
{
w32string sAmbigCharSet, sSpecialSymCharSet;
std::list<w32string> ambigGroups;
if (!sAmbigChars.IsEmpty()) {
sAmbigCharSet = WStringToW32String(sAmbigChars);
w32string sDummy;
MakeCharSetUnique(sDummy, &sAmbigCharSet, &ambigGroups, true);
}
else
sAmbigCharSet = CharToW32String(CHARSET_AMBIGUOUS);
if (blExcludeAmbigChars) {
m_sAmbigCharSet = sAmbigCharSet;
m_ambigGroups = ambigGroups;
}
else {
m_sAmbigCharSet.clear();
m_ambigGroups.clear();
}
if (!sSpecialSymbols.IsEmpty())
sSpecialSymCharSet = MakeCharSetUnique(WStringToW32String(sSpecialSymbols));
else
sSpecialSymCharSet = CharToW32String(CHARSET_SYMBOLS);
m_charSetDecodes[CHARSET_CODES_EASYTOREAD] = MakeCharSetUnique(
CharToW32String(CHARSET_DECODES[CHARSET_CODES_AZ]) +
CharToW32String(CHARSET_DECODES[CHARSET_CODES_az]) +
CharToW32String(CHARSET_DECODES[CHARSET_CODES_09]),
&sAmbigCharSet);
m_charSetDecodes[CHARSET_CODES_SYMBOLS] = sSpecialSymCharSet;
int nI;
if (m_charSetDecodes[CHARSET_CODES_HIGHANSI].empty()) {
const int HIGHANSI_NUM = 129;
char szHighAnsi[HIGHANSI_NUM+1];
for (nI = 0; nI < HIGHANSI_NUM; nI++)
szHighAnsi[nI] = char(127 + nI);
szHighAnsi[nI] = '\0';
int nWLen = MultiByteToWideChar(CP_ACP, 0, szHighAnsi, -1, NULL, 0);
WString sWStr;
sWStr.SetLength(nWLen-1);
MultiByteToWideChar(CP_ACP, 0, szHighAnsi, -1, sWStr, nWLen);
m_charSetDecodes[CHARSET_CODES_HIGHANSI] = WStringToW32String(sWStr);
}
CharSetType charSetType;
w32string sCustomCharSet = ParseCharSet(WStringToW32String(sCustomChars),
charSetType);
// are there any non-lowercase letters in the set?
m_blCustomCharSetNonLC = false;
for (nI = 0; nI < int(sCustomCharSet.length()); nI++) {
if (sCustomCharSet[nI] < 'a' || sCustomCharSet[nI] > 'z') {
m_blCustomCharSetNonLC = true;
break;
}
}
sCustomChars = W32StringToWString(sCustomCharSet);
if (!sCustomCharSet.empty()) {
m_sCustomCharSet = sCustomCharSet;
m_customCharSetType = charSetType;
m_nCustomCharSetSize = m_sCustomCharSet.length();
switch (m_customCharSetType) {
case cstNormal:
m_dCustomCharSetEntropy = Log2(m_nCustomCharSetSize);
break;
case cstPhonetic:
m_dCustomCharSetEntropy = m_dPhoneticEntropy;
break;
case cstPhoneticMixedCase:
m_dCustomCharSetEntropy = m_dPhoneticEntropy + 1;
}
}
m_includeCharSets[CHARSET_INCLUDE_AZ] = m_charSetDecodes[CHARSET_CODES_AZ];
m_includeCharSets[CHARSET_INCLUDE_az] = m_charSetDecodes[CHARSET_CODES_az];
m_includeCharSets[CHARSET_INCLUDE_09] = m_charSetDecodes[CHARSET_CODES_09];
m_includeCharSets[CHARSET_INCLUDE_SPECIAL] = sSpecialSymCharSet;
if (blExcludeAmbigChars) {
for (nI = 0; nI < PASSWGEN_NUMINCLUDECHARSETS; nI++) {
w32string sNew = MakeCharSetUnique(m_includeCharSets[nI], &sAmbigCharSet);
if (sNew.length() >= 2)
m_includeCharSets[nI] = sNew;
}
}
for (nI = 0; nI < PASSWGEN_NUMFORMATCHARSETS; nI++)
m_formatCharSets[nI] = CharToW32String(CHARSET_FORMAT[nI]);
m_formatCharSets[CHARSET_FORMAT_x] = m_sCustomCharSet;
m_formatCharSets[CHARSET_FORMAT_E] = m_charSetDecodes[CHARSET_CODES_EASYTOREAD];
m_formatCharSets[CHARSET_FORMAT_s] = sSpecialSymCharSet;
m_formatCharSets[CHARSET_FORMAT_S] = MakeCharSetUnique(
CharToW32String(CHARSET_FORMAT[CHARSET_FORMAT_S]) + sSpecialSymCharSet);
m_formatCharSets[CHARSET_FORMAT_y] = m_charSetDecodes[CHARSET_CODES_HIGHANSI];
if (blExcludeAmbigChars) {
for (nI = 0; nI < sizeof(CHARSET_FORMAT_CONST)/sizeof(int); nI++) {
int nSetIdx = CHARSET_FORMAT_CONST[nI];
w32string sNew = MakeCharSetUnique(m_formatCharSets[nSetIdx], &sAmbigCharSet);
if (sNew.length() >= 2)
m_formatCharSets[nSetIdx] = sNew;
}
}
// determine character subsets in the custom character set
if (blDetermineSubsets) {
for (nI = 0; nI < PASSWGEN_NUMINCLUDECHARSETS; nI++) {
w32string sSubset;
word32 lPos = 0;
while ((lPos = m_sCustomCharSet.find_first_of(m_includeCharSets[nI], lPos))
!= w32string::npos)
sSubset.push_back(m_sCustomCharSet[lPos++]);
m_customSubsets[nI] = sSubset;
}
}
}
//---------------------------------------------------------------------------
int PasswordGenerator::LoadWordListFile(WString sFileName,
int nMaxWordLen,
bool blConvertToLC)
{
int nNumOfWords = WORDLIST_DEFAULT_SIZE;;
// TTntStringList* pWordList = NULL;
// std::vector<std::wstring> newList;
if (!sFileName.IsEmpty()) {
if (WideExtractFilePath(sFileName).IsEmpty())
sFileName = g_sExePath + sFileName;
TStringFileStreamW* pFile = NULL;
std::set<std::wstring> wordList;
try {
pFile = new TStringFileStreamW(sFileName, fmOpenRead, ceAnsi, true,
65536, "\n\t ");
/*pWordList = new TTntStringList;
pWordList->Sorted = true;
pWordList->CaseSensitive = true;
pWordList->Duplicates = dupIgnore;*/
const int WORDBUF_SIZE = 1024;
wchar_t wszWord[WORDBUF_SIZE];
while (pFile->ReadString(wszWord, WORDBUF_SIZE) > 0 &&
wordList.size() < WORDLIST_MAX_SIZE)
{
WString sWord = Trim(WString(wszWord));
if (sWord.IsEmpty())
continue;
int nWordLen = GetNumOfUnicodeChars(sWord.c_bstr());
if (nWordLen == 0 || nWordLen > nMaxWordLen)
continue;
if (blConvertToLC)
sWord = Tnt_WideLowerCase(sWord);
//pWordList->Add(sWord);
wordList.insert(sWord.c_bstr());
}
}
catch (EStreamError& e) {
/*if (pWordList != NULL)
delete pWordList;*/
if (pFile != NULL)
delete pFile;
return -1;
}
catch (...) {
/*if (pWordList != NULL)
delete pWordList;*/
if (pFile != NULL)
delete pFile;
throw;
}
delete pFile;
if ((nNumOfWords = wordList.size()) < 2) {
//delete pWordList;
return 0;
}
m_wordList.assign(wordList.begin(), wordList.end());
}
else if (!m_wordList.empty()) {
m_wordList.clear();
// force reallocation of vector by replacing its contents by an empty
// vector (clear() doesn't necessarily free the buffer but may only
// reduce the vector size to 0)
std::vector<std::wstring> temp;
m_wordList.swap(temp);
}
/*if (m_pWordList != NULL)
delete m_pWordList;
m_pWordList = pWordList;*/
m_nWordListSize = nNumOfWords;
m_dWordListEntropy = Log2(nNumOfWords);
return nNumOfWords;
}
//---------------------------------------------------------------------------
int PasswordGenerator::GetPassword(word32* pDest,
int nLength,
int nFlags)
{
int nI;
word32 lChar;
std::set<word32>* pPasswCharSet = NULL;
if ((nFlags & PASSW_FLAG_EACHCHARONLYONCE) &&
(nFlags & PASSW_FLAG_CHECKDUPLICATESBYSET))
pPasswCharSet = new std::set<word32>;
try {
for (nI = 0; nI < nLength; ) {
lChar = m_sCustomCharSet[m_pRandGen->GetNumRange(m_nCustomCharSetSize)];
if (nI == 0 && m_blCustomCharSetNonLC && nFlags & PASSW_FLAG_FIRSTCHARNOTLC
&& lChar >= 'a' && lChar <= 'z')
continue;
if (nFlags & PASSW_FLAG_EACHCHARONLYONCE) {
if (pPasswCharSet != NULL) {
std::pair<std::set<word32>::iterator, bool> ret =
pPasswCharSet->insert(lChar);
if (!ret.second)
continue;
}
else if (nI > 0 && strchpos(pDest, nI, lChar) >= 0)
continue;
}
else if (nI > 0 && nFlags & PASSW_FLAG_EXCLUDEREPCHARS && lChar == pDest[nI-1])
continue;
pDest[nI++] = lChar;
}
pDest[nLength] = '\0';
if (nFlags >= PASSW_FLAG_INCLUDEUCL) {
SecureMem<int> randPerm(PASSWGEN_NUMINCLUDECHARSETS);
const w32string* psCharSets;
int nJ, nRand;
if (nFlags & PASSW_FLAG_INCLUDESUBSET)
psCharSets = m_customSubsets;
else
psCharSets = m_includeCharSets;
for (nI = nJ = 0; nI < PASSWGEN_NUMINCLUDECHARSETS && nJ < nLength; nI++) {
int nFlagVal = PASSW_FLAG_INCLUDEUCL << nI;
if (nFlags & nFlagVal && !psCharSets[nI].empty()) {
// if (psCharSets[nI].find_first_of(pDest) != w32string::npos)
// continue;
do {
if (nFlagVal == PASSW_FLAG_INCLUDELCL && nFlags & PASSW_FLAG_FIRSTCHARNOTLC) {
if (nLength-nJ >= 2)
nRand = 1 + m_pRandGen->GetNumRange(nLength-1);
else {
nRand = -2;
break;
}
}
else
nRand = m_pRandGen->GetNumRange(nLength);
for (int nK = 0; nK < nJ; nK++) {
if (nRand == randPerm[nK]) {
nRand = -1;
break;
}
}
}
while (nRand < 0);
if (nRand < 0)
continue;
randPerm[nJ++] = nRand;
if (psCharSets[nI].find(pDest[nRand]) != w32string::npos)
continue;
if (nFlags & PASSW_FLAG_EACHCHARONLYONCE) {
if (psCharSets[nI].find_first_not_of(pDest) == w32string::npos)
continue;
}
int nSetSize = psCharSets[nI].length();
while (1) {
lChar = psCharSets[nI][m_pRandGen->GetNumRange(nSetSize)];
if (nFlags & PASSW_FLAG_EACHCHARONLYONCE) {
if (pPasswCharSet != NULL) {
std::pair<std::set<word32>::iterator, bool> ret =
pPasswCharSet->insert(lChar);
if (!ret.second)
continue;
}
else if (strchpos(pDest, nLength, lChar) >= 0)
continue;
}
else if (nFlags & PASSW_FLAG_EXCLUDEREPCHARS && nSetSize >= 3) {
if (nRand > 0 && lChar == pDest[nRand-1])
continue;
if (nRand < nLength-1 && lChar == pDest[nRand+1])
continue;
}
break;
}
pDest[nRand] = lChar;
}
}
nRand = 0;
}
}
__finally {
if (pPasswCharSet != NULL)
delete pPasswCharSet;
lChar = 0;
}
// debug stuff
/*if (nFlags & PASSW_FLAG_EACHCHARONLYONCE) {
std::set<word32> testSet;
for (nI = 0; nI < nLength; nI++) {
std::pair<std::set<word32>::iterator, bool> ret =
testSet.insert(pDest[nI]);
if (!ret.second)
ShowMessageFmt("Duplicate i=%d, c=%d",ARRAYOFCONST((nI,int(pDest[nI]))));
}
}*/
return nLength;
}
//---------------------------------------------------------------------------
int PasswordGenerator::GetPassphrase(word32* pDest,
int nWords,
const word32* pChars,
int nFlags)
{
int nCharsLen = (pChars != NULL) ? w32strlen(pChars) : 0;
int nLength = 0;
bool blAppendChars = false;
if (nCharsLen != 0 && !(nFlags & PASSPHR_FLAG_COMBINEWCH)) {
if (nFlags & PASSPHR_FLAG_REVERSEWCHORDER)
blAppendChars = true;
else {
memcpy(pDest, pChars, nCharsLen * sizeof(word32));
nLength = nCharsLen;
pDest[nLength++] = ' ';
}
}
int nRand;
int nCharsPerWord = nCharsLen / nWords;
int nCharsRest = nCharsLen % nWords;
int nCharsPos = 0;
SecureW32String sWord(WORDLIST_MAX_WORDLEN + 1);
std::set<SecureW32String>* pUniqueWordList = NULL;
if (nFlags & PASSPHR_FLAG_EACHWORDONLYONCE)
pUniqueWordList = new std::set<SecureW32String>;
try {
for (int nI = 0; nI < nWords; ) {
nRand = m_pRandGen->GetNumRange(m_nWordListSize);
int nWordLen;
if (m_wordList.empty())
nWordLen = CharToW32Char(sWord, getDiceWd(nRand));
else
nWordLen = WCharToW32Char(sWord, m_wordList[nRand].c_str());
if (pUniqueWordList != NULL) {
std::pair<std::set<SecureW32String>::iterator, bool> ret =
pUniqueWordList->insert(sWord);
if (!ret.second)
continue;
}
int nInsertWordIdx = nLength;
if (nFlags & PASSPHR_FLAG_COMBINEWCH && nCharsPos < nCharsLen) {
int nToCopy = nCharsPerWord;
if (nI < nCharsRest)
nToCopy++;
if (nFlags & PASSPHR_FLAG_REVERSEWCHORDER) {
memcpy(pDest + nLength, pChars + nCharsPos, nToCopy * sizeof(word32));
nLength += nToCopy;
if (!(nFlags & PASSPHR_FLAG_DONTSEPWCH))
pDest[nLength++] = '-';
nInsertWordIdx = nLength;
}
else {
if (!(nFlags & PASSPHR_FLAG_DONTSEPWCH))
pDest[nLength++ + nWordLen] = '-';
memcpy(pDest + nLength + nWordLen, pChars + nCharsPos, nToCopy * sizeof(word32));
nLength += nToCopy;
}
nCharsPos += nToCopy;
}
memcpy(pDest + nInsertWordIdx, sWord, nWordLen * sizeof(word32));
nLength += nWordLen;
if (++nI < nWords) {
if (!(nFlags & PASSPHR_FLAG_DONTSEPWORDS))
pDest[nLength++] = ' ';
}
else
pDest[nLength] = '\0';
}
if (blAppendChars) {
pDest[nLength++] = ' ';
memcpy(pDest + nLength, pChars, nCharsLen * sizeof(word32));
nLength += nCharsLen;
pDest[nLength] = '\0';
}
}
__finally {
if (pUniqueWordList != NULL)
delete pUniqueWordList;
nRand = 0;
}
return nLength;
}
//---------------------------------------------------------------------------
int PasswordGenerator::GetFormatPassw(word32* pDest,
int nDestSize,
const w32string& sFormat,
int nFlags,
const word32* pPassw,
int* pnPasswUsed,
word32* plInvalidSpec,
double* pdSecurity)
{
int nSrcIdx = 0, nDestIdx = 0, nI;
int nFormatLen = sFormat.length();
bool blComment = false;
bool blSpecMode = false;
bool blUnique = false;
bool blSecondNum = false;
char szNum[] = "00000";
int nNum;
int nNumIdx = 0;
int nRepeatIdx = 0;
int repeatNum[FORMAT_REPEAT_MAXDEPTH];
int repeatStart[FORMAT_REPEAT_MAXDEPTH];
int nPermNum = 0;
int nPermStart;
int nUserCharSetNum = 0;
int nUserCharSetStart;
int nToCopy;
word32 lRand;
double dPermSecurity;
if (pnPasswUsed != NULL)
*pnPasswUsed = PASSFORMAT_PWUSED_NOTUSED;
if (plInvalidSpec != NULL)
*plInvalidSpec = 0;
for ( ; nSrcIdx < nFormatLen && nDestIdx < nDestSize; nSrcIdx++) {
word32 lChar = sFormat[nSrcIdx];
if (nSrcIdx == 0 && lChar == '[') {
blComment = true;
continue;
}
if (blComment) {
if (lChar == ']')
blComment = false;
continue;
}
if (nUserCharSetNum > 0) {
if (lChar == '>' && sFormat[nSrcIdx-1] == '%')
blSpecMode = true;
else
continue;
}
if (blSpecMode) {
if (lChar == '*') {
blUnique = true;
continue;
}
if (lChar >= '0' && lChar <= '9') {
if (nNumIdx < 5) {
szNum[nNumIdx++] = lChar;
continue;
}
}
bool blNumDefault = true;
int nParsedNum = 1;
if (nNumIdx > 0) {
szNum[nNumIdx] = '\0';
nParsedNum = atoi(szNum);
if (nParsedNum > 0)
blNumDefault = false;
else
nParsedNum = 1;
}
if (blSecondNum) {
if (!blNumDefault && nNum != nParsedNum) {
nNum = std::min(nNum, nParsedNum) + m_pRandGen->GetNumRange(
abs(nParsedNum - nNum) + 1);
}
}
else {
nNum = nParsedNum;
if (!blNumDefault && lChar == '-') {
blSecondNum = true;
nNumIdx = 0;
continue;
}
}
SecureW32String sWord(WORDLIST_MAX_WORDLEN + 1);
w32string sUserCharSet;
const w32string* psCharSet = NULL;
CharSetType charSetType = cstNormal;
std::set<SecureW32String>* pUniqueWordList = NULL;
switch (lChar) {
case '%': // "%%" -> '%'
pDest[nDestIdx++] = lChar;
break;
case '<': // begin user-defined character set
nUserCharSetNum = nNum;
nUserCharSetStart = nSrcIdx + 1;
break;
case '>': // end
if (nUserCharSetNum > 0) {
int nUserCharSetLen = nSrcIdx - nUserCharSetStart - 1;
if (nUserCharSetLen >= 2) {
sUserCharSet = sFormat.substr(nUserCharSetStart, nUserCharSetLen);
sUserCharSet = ParseCharSet(sUserCharSet, charSetType);
if (!sUserCharSet.empty()) {
psCharSet = &sUserCharSet;
nNum = nUserCharSetNum;
}
}
}
nUserCharSetNum = 0;
break;
case 'P': // copy password to dest
if (pPassw != NULL) {
nToCopy = std::min(w32strlen(pPassw), nDestSize - nDestIdx);
memcpy(pDest + nDestIdx, pPassw, nToCopy * sizeof(word32));
nDestIdx += nToCopy;
if (pnPasswUsed != NULL)
*pnPasswUsed = nToCopy;
pPassw = NULL; // must be used only once
}
else if (pnPasswUsed != NULL && *pnPasswUsed <= 0)
*pnPasswUsed = PASSFORMAT_PWUSED_EMPTYPW;
break;
case 'q':
charSetType = cstPhonetic;
break;
case 'Q':
charSetType = cstPhoneticMixedCase;
break;
case 'W': // add word
case 'w': // add word + space
if (blUnique) {
nNum = (blNumDefault) ? m_nWordListSize : std::min(nNum, m_nWordListSize);
pUniqueWordList = new std::set<SecureW32String>;
}
for (nI = 0; nI < nNum && nDestIdx < nDestSize; ) {
lRand = m_pRandGen->GetNumRange(m_nWordListSize);
int nWordLen;
if (m_wordList.empty())
nWordLen = CharToW32Char(sWord, getDiceWd(lRand));
else
nWordLen = WCharToW32Char(sWord, m_wordList[lRand].c_str());
if (blUnique) {
std::pair<std::set<SecureW32String>::iterator, bool> ret =
pUniqueWordList->insert(sWord);
if (!ret.second)
continue;
}
nToCopy = std::min(nWordLen, nDestSize - nDestIdx);
memcpy(pDest + nDestIdx, sWord, nToCopy * sizeof(word32));
nDestIdx += nToCopy;
if (lChar == 'w' && nI < nNum-1 && nDestIdx < nDestSize)
pDest[nDestIdx++] = ' ';
nI++;
}
if (pUniqueWordList != NULL)
delete pUniqueWordList;
if (pdSecurity != NULL) {
if (blUnique)
*pdSecurity += CalcPermSetEntropy(m_nWordListSize, nI);
else
*pdSecurity += m_dWordListEntropy * nI;
}
break;
case '[': // start index for repeating a sequence in sFormat
if (nRepeatIdx < FORMAT_REPEAT_MAXDEPTH) {
repeatNum[nRepeatIdx] = nNum - 1;
repeatStart[nRepeatIdx] = nSrcIdx;
nRepeatIdx++;
}
break;
case ']': // end
if (nRepeatIdx > 0) {
if (repeatNum[nRepeatIdx-1] == 0 ||
nSrcIdx - repeatStart[nRepeatIdx-1] < 3)
nRepeatIdx--;
else if (repeatNum[nRepeatIdx-1]-- > 0)
nSrcIdx = repeatStart[nRepeatIdx-1];
}
break;
case '{': // start index for permuting a sequence in pszDest
nPermNum = (blNumDefault) ? 0 : nNum;
nPermStart = nDestIdx;
if (pdSecurity != NULL)
dPermSecurity = *pdSecurity;
break;
case '}': // end
if (nPermNum >= 0) {
int nPermSize = nDestIdx - nPermStart;
int nToUse = (nPermNum == 0) ? nPermSize : std::min(nPermNum, nPermSize);
if (nPermSize >= 2) { // now permute!
m_pRandGen->Permute<word32>(pDest + nPermStart, nPermSize);
nDestIdx = nPermStart + nToUse;
if (pdSecurity != NULL && nToUse < nPermSize)
*pdSecurity = dPermSecurity + (*pdSecurity - dPermSecurity) * nToUse / nPermSize;
}
nPermNum = -1;
}
break;
default:
int nPlaceholder = -1;
if (lChar >= 'A' && lChar <= 'z')
nPlaceholder = strchpos(FORMAT_PLACEHOLDERS, char(lChar));
if (nPlaceholder >= 0) {
if (nPlaceholder == CHARSET_FORMAT_x) {
psCharSet = &m_sCustomCharSet;
charSetType = charSetType;
}
else
psCharSet = &m_formatCharSets[nPlaceholder];
}
else if (plInvalidSpec != NULL) {
*plInvalidSpec = lChar;
plInvalidSpec = NULL;
}
}
if (charSetType == cstPhonetic || charSetType == cstPhoneticMixedCase) {
int nLen = GetPhoneticPassw(pDest + nDestIdx,
std::min(nNum, nDestSize - nDestIdx),
(charSetType == cstPhoneticMixedCase) ? PASSW_FLAG_PHONETICMIXEDCASE : 0);
nDestIdx += nLen;
if (pdSecurity != NULL)
*pdSecurity += (m_dPhoneticEntropy +
((charSetType == cstPhoneticMixedCase) ? 1 : 0)) * nLen;
}
else if (psCharSet != NULL) {
int nSetSize = psCharSet->length();
int nStartIdx = nDestIdx;
if (blUnique)
nNum = (blNumDefault) ? nSetSize : std::min(nNum, nSetSize);
for (nI = 0; nI < nNum && nDestIdx < nDestSize; ) {
lRand = (*psCharSet)[m_pRandGen->GetNumRange(nSetSize)];
bool checkRep = nDestIdx > 0;
if (blUnique && nI > 0) {
if (strchpos(pDest + nStartIdx, nI, lRand) >= 0)
continue;
checkRep = false;
}
if (checkRep &&
nFlags & PASSFORMAT_FLAG_EXCLUDEREPCHARS &&
lRand == pDest[nDestIdx-1])
continue;
pDest[nDestIdx++] = lRand;
nI++;
}
if (pdSecurity != NULL) {
if (blUnique)
*pdSecurity += CalcPermSetEntropy(nSetSize, nI);
else
*pdSecurity += Log2(nSetSize) * nI;
}
}
blSpecMode = false;
}
else if (lChar == '%') {
blSpecMode = true;
blUnique = false;
blSecondNum = false;
nNumIdx = 0;
}
else {
pDest[nDestIdx++] = lChar;
}
}
pDest[nDestIdx] = '\0';
lRand = 0;
return nDestIdx;
}
//---------------------------------------------------------------------------
WString PasswordGenerator::GetWord(int nIndex)
{
if (m_wordList.empty())
return getDiceWd(nIndex);
return WString(m_wordList[nIndex].c_str());
}
//---------------------------------------------------------------------------
void PasswordGenerator::CreateTrigramFile(const WString& sSrcFileName,
const WString& sDestFileName,
word32* plNumOfTris,
double* pdEntropy)
{
TStringFileStreamW* pSrcFile = NULL;
TTntFileStream* pDestFile = NULL;
try {
pSrcFile = new TStringFileStreamW(sSrcFileName, fmOpenRead, ceAnsi,
true, 65536, "\n\t ");
const int WORDBUF_SIZE = 1024;
wchar_t wszWord[WORDBUF_SIZE];
std::vector<word32> tris(PHONETIC_TRIS_NUM, 0);
word32 lSigma = 0;
int nWordLen, nI;
while ((nWordLen = pSrcFile->ReadString(wszWord, WORDBUF_SIZE)) > 0) {
WString sWord = Trim(WString(wszWord));
nWordLen = sWord.Length();
if (nWordLen < 3)
continue;
const wchar_t* pwszWord = sWord.c_bstr();
wchar_t wch;
char ch1 = -1, ch2 = -1, ch3;
while ((wch = *pwszWord++) != '\0') {
if (wch <= 'z' && (ch3 = tolower(wch) - 'a') >= 0 && ch3 <= 25) {
if (ch1 >= 0) {
tris[676*ch1+26*ch2+ch3]++;
lSigma++;
}
ch1 = ch2;
ch2 = ch3;
}
}
}
double dEntropy = 0;
if (lSigma != 0) {
double dProb;
for (nI = 0; nI < PHONETIC_TRIS_NUM; nI++) {
dProb = double(tris[nI]) / lSigma;
if (dProb > 0)
dEntropy += dProb * Log2(dProb);
}
dEntropy = -dEntropy / 3;
}
if (plNumOfTris != NULL)
*plNumOfTris = lSigma;
if (pdEntropy != NULL)
*pdEntropy = dEntropy;
if (lSigma == 0 || dEntropy < 1.0)
throw Exception(ETRL("Source file does not contain enough trigrams"));
pDestFile = new TTntFileStream(sDestFileName, fmCreate);
pDestFile->Write(&lSigma, sizeof(word32));
pDestFile->Write(&dEntropy, sizeof(double));
pDestFile->Write(tris.begin(), tris.size() * sizeof(word32));
/*
FILE* pDestFile = fopen(sDestFileName.c_str(), "wt");
if (pDestFile == NULL)
return 0;
fprintf(pDestFile, "%d\n", lSigma);
for (int nI = 0; nI < PHONETIC_TRIS_NUM; nI++)
fprintf(pDestFile, "%d,", tris[nI]);
fclose(pDestFile);
*/
/*
FILE* f = fopen(sDestFileName.c_str(), "wt");
fprintf(f, "const word32 PHONETIC_SIGMA = %d,\n", lSigma);
fprintf(f, " PHONETIC_TRIS_NUM = 17576;\n\n");
fprintf(f, "const word16 PHONETIC_TRIS[PHONETIC_TRIS_NUM] = {\n");
for (int i=0; i<676; i++) {
fprintf(f, " ");
for (int j=0; j<26; j++) {
fprintf(f, "%d,", tris[i*26+j]);
}
fprintf(f, "\n");
}
fprintf(f, "};");
fclose(f);
*/
}
__finally {
if (pSrcFile != NULL)
delete pSrcFile;
if (pDestFile != NULL)
delete pDestFile;
}
}
//---------------------------------------------------------------------------
int PasswordGenerator::LoadTrigramFile(WString sFileName)
{
std::vector<word32> tris;
word32 lSigma = PHONETIC_SIGMA;
double dEntropy = PHONETIC_ENTROPY;
if (!sFileName.IsEmpty()) {
if (WideExtractFilePath(sFileName).IsEmpty())
sFileName = g_sExePath + sFileName;
TTntFileStream* pFile = NULL;
try {
pFile = new TTntFileStream(sFileName, fmOpenRead);
if (pFile->Size != PHONETIC_TRIS_NUM * sizeof(word32) +
sizeof(word32) + sizeof(double)) {
delete pFile;
return 0;
}
tris.resize(PHONETIC_TRIS_NUM);
pFile->Read(&lSigma, sizeof(word32));
pFile->Read(&dEntropy, sizeof(double));
pFile->Read(tris.begin(), PHONETIC_TRIS_NUM * sizeof(word32));
delete pFile;
pFile = NULL;
if (lSigma == 0 || dEntropy < 1.0 || dEntropy > Log2(26))
return 0;
word32 lCheck = 0;
for (int nI = 0; nI < PHONETIC_TRIS_NUM; nI++)
lCheck += tris[nI];
if (lCheck != lSigma)
return 0;
}
catch (EStreamError& e) {
if (pFile != NULL)
delete pFile;
return -1;
}
catch (...) {
if (pFile != NULL)
delete pFile;
throw;
}
}
m_phoneticTris = tris;
m_lPhoneticSigma = lSigma;
m_dPhoneticEntropy = dEntropy;
return 1;
}
//---------------------------------------------------------------------------
int PasswordGenerator::GetPhoneticPassw(word32* pDest,
int nLength,
int nFlags)
{
// word32* pTris = (m_pPhoneticTris == NULL) ? (word32*) PHONETIC_TRIS : m_pPhoneticTris;
#define getLetter(x) x + ((blMixedCase) ? (m_pRandGen->GetNumRange(2) ? 'a' : 'A') : 'a')
bool blDefaultTris = m_phoneticTris.empty();
bool blMixedCase = nFlags & PASSW_FLAG_PHONETICMIXEDCASE;
word32 lSumFreq = m_lPhoneticSigma;
word32 lRand = m_pRandGen->GetNumRange(lSumFreq);
word32 lSum = 0;
int nChars = 0, nI;
char ch1, ch2, ch3;
for (nI = 0; nI < PHONETIC_TRIS_NUM; nI++) {
if (blDefaultTris)
lSum += PHONETIC_TRIS[nI];
else
lSum += m_phoneticTris[nI];
if (lSum > lRand) {
ch1 = nI / 676;
ch2 = (nI / 26) % 26;
ch3 = nI % 26;
break;
}
}
if (nLength >= 1)
pDest[nChars++] = getLetter(ch1);
if (nLength >= 2)
pDest[nChars++] = getLetter(ch2);
if (nLength >= 3)
pDest[nChars++] = getLetter(ch3);
while (nChars < nLength) {
ch1 = ch2;
ch2 = ch3;
lSumFreq = 0;
for (ch3 = 0; ch3 < 26; ch3++) {
if (blDefaultTris)
lSumFreq += PHONETIC_TRIS[676*ch1+26*ch2+ch3];
else
lSumFreq += m_phoneticTris[676*ch1+26*ch2+ch3];
}
if (lSumFreq == 0) {
// if we can't find anything, just insert a vowel...
static const char VOWELS[5] = { 0, 4, 8, 14, 20 };
ch3 = VOWELS[m_pRandGen->GetNumRange(sizeof(VOWELS))];
}
else {
lRand = m_pRandGen->GetNumRange(lSumFreq);
lSum = 0;
for (ch3 = 0; ch3 < 26; ch3++) {
if (blDefaultTris)
lSum += PHONETIC_TRIS[676*ch1+26*ch2+ch3];
else
lSum += m_phoneticTris[676*ch1+26*ch2+ch3];
if (lSum > lRand)
break;
}
}
pDest[nChars++] = getLetter(ch3);
}
pDest[nChars] = '\0';
lRand = 0;
if (nFlags >= PASSW_FLAG_INCLUDEUCL) {
SecureMem<int> randPerm(PASSWGEN_NUMINCLUDECHARSETS - 1); // except _INCLUDELCL
int nJ, nRand;
for (nI = nJ = 0; nI < PASSWGEN_NUMINCLUDECHARSETS && nJ < nLength; nI++) {
int nFlagVal = PASSW_FLAG_INCLUDEUCL << nI;
if (nFlagVal != PASSW_FLAG_INCLUDELCL && nFlags & nFlagVal) {
/*
if (nFlagVal == PASSW_FLAG_INCLUDEUCL) {
w32string sCharSetLC = m_includeCharSets[nI];
for (w32string::iterator it = sCharSetLC.begin();
it != sCharSetLC.end(); it++)
// we can use char-based tolower() here because the char set
// can only include letters A..Z and no higher Unicode chars
*it = tolower(*it);
if (sCharSetLC.find_first_of(pDest) == w32string::npos)
continue;
}
*/
do {
nRand = m_pRandGen->GetNumRange(nLength);
for (int nK = 0; nK < nJ; nK++) {
if (nRand == randPerm[nK]) {
nRand = -1;
break;
}
}
// if nI == 0 (nFlagVal == _INCLUDEUCL), nRand is ALWAYS >= 0!!
/*
if (nFlagVal == PASSW_FLAG_INCLUDEUCL &&
m_includeCharSets[nI].find(toupper(pDest[nRand])) == w32string::npos)
nRand = -1;
*/
}
while (nRand < 0);
randPerm[nJ++] = nRand;
if (nFlagVal == PASSW_FLAG_INCLUDEUCL)
pDest[nRand] = toupper(pDest[nRand]);
else {
int nSetSize = m_includeCharSets[nI].length();
pDest[nRand] = m_includeCharSets[nI][m_pRandGen->GetNumRange(nSetSize)];
}
}
}
nRand = 0;
}
return nChars;
}
//---------------------------------------------------------------------------
static double logFactorial(int nNum)
{
if (nNum < 2)
return 0;
static const double TWOPI = 2 * acos(-1);
double dNum = nNum;
return dNum * log(dNum) - dNum + 0.5 * log(TWOPI * dNum) + 1 / (12 * dNum);
}
//---------------------------------------------------------------------------
double PasswordGenerator::CalcPermSetEntropy(int nSetSize,
int nNumOfSamples)
{
static const double LOG2 = log(2);
return (logFactorial(nSetSize) - logFactorial(nSetSize - nNumOfSamples)) / LOG2;
}
//---------------------------------------------------------------------------
int PasswordGenerator::EstimatePasswSecurity(const wchar_t* pwszPassw)
{
if (pwszPassw == NULL || pwszPassw[0] == '\0')
return 0;
int nPasswLen = wcslen(pwszPassw);
SecureW32String sPassw(nPasswLen + 1);
WCharToW32Char(sPassw, pwszPassw);
// The following code is based on the function EstimatePasswordBits()
// from the KeePass source code (http://keepass.org) by Dominik Reichl.
// This algorithm tends to *under*rate random character sequences and
// *over*rate passphrases containing words from a word list. Nevertheless,
// it's really simple, fast and fully sufficient for our purpose.
// For longer random sequences, applying some statistical tests for randomness
// (autocorrelation coefficient, Runs test, entropy estimation, etc.) would be
// appropriate, but this is simply not realistic for short sequences like
// passwords/passphrases...
std::map<word32, int> chcount;
std::map<int, int> chdiff;
bool blControl = false, blUpper = false, blLower = false, blDigits = false,
blSpecial = false;
int nCharSpace = 0;
double dEffectiveLength = 0;
for (int nI = 0; nI < nPasswLen; nI++)
{
word32 lChar = sPassw[nI];
bool blOtherCharSet = false;
if (lChar > 0 && lChar < ' ') blControl = true; // control characters
else if (lChar >= 'A' && lChar <= 'Z') blUpper = true; // upper-case
else if (lChar >= 'a' && lChar <= 'z') blLower = true; // lower-case
else if (lChar >= '0' && lChar <= '9') blDigits = true; // digits
else if (lChar >= ' ' && lChar <= '/') blSpecial = true; // special, including space
else if (lChar >= ':' && lChar <= '@') blSpecial = true;
else if (lChar >= '[' && lChar <= '`') blSpecial = true;
else if (lChar >= '{' && lChar <= '~') blSpecial = true;
else blOtherCharSet = true;
double dDiffFactor = 1.0;
int nCount;
if (nI > 0) {
int nDiff = lChar - sPassw[nI-1];
std::map<int, int>::iterator it = chdiff.find(nDiff);
if (it == chdiff.end()) {
nCount = 1;
chdiff[nDiff] = nCount;
}
else {
nCount = it->second + 1;
it->second = nCount;
}
dDiffFactor /= nCount;
}
//std::map<word32, int>::iterator it = chcount.find(lChar);
std::pair<std::map<word32, int>::iterator, bool> ret =
chcount.insert(std::pair<word32, int>(lChar, 1));
if (ret.second) {
nCount = 1;
//chcount[lChar] = nCount;
if (blOtherCharSet)
nCharSpace++;
}
else {
nCount = ret.first->second + 1;
ret.first->second = nCount;
}
dEffectiveLength += dDiffFactor / nCount;
}
if (blControl) nCharSpace += 2; // control characters
// (only 2 accessible in edit controls: #30 and #31)
if (blUpper) nCharSpace += 26; // upper-case
if (blLower) nCharSpace += 26; // lower-case
if (blDigits) nCharSpace += 10; // digits
if (blSpecial) nCharSpace += 33; // special
// maximum ANSI space = 97
return Ceil(Log2(nCharSpace) * dEffectiveLength);
}
//---------------------------------------------------------------------------
Código:
// PasswGen.h
//
// PWGEN FOR WINDOWS
// Copyright (c) 2002-2015 by Christian Thoeing <c.thoeing@web.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//---------------------------------------------------------------------------
#ifndef PasswGenH
#define PasswGenH
//---------------------------------------------------------------------------
#include <vector>
#include <list>
#include "TntClasses.hpp"
#include "SecureMem.h"
#include "RandomGenerator.h"
#include "UnicodeUtil.h"
const int
WORDLIST_DEFAULT_SIZE = 8192,
WORDLIST_MAX_WORDLEN = 30,
WORDLIST_MAX_SIZE = 1048576,
PASSWGEN_NUMCHARSETCODES = 11,
PASSWGEN_NUMINCLUDECHARSETS = 4,
PASSWGEN_NUMFORMATCHARSETS = 22,
PASSW_FLAG_FIRSTCHARNOTLC = 0x0001, // first char must not be lower-case letter
PASSW_FLAG_EXCLUDEREPCHARS = 0x0002, // exclusion of repeating consecutive chars
PASSW_FLAG_INCLUDESUBSET = 0x0004, // include only additional characters
// if custom set contains appropriate subset
PASSW_FLAG_INCLUDEUCL = 0x0008, // include one char from additional charset
PASSW_FLAG_INCLUDELCL = 0x0010,
PASSW_FLAG_INCLUDEDIGIT = 0x0020,
PASSW_FLAG_INCLUDESPECIAL = 0x0040,
PASSW_FLAG_PHONETICMIXEDCASE = 0x0080, // mixed-case characters in phonetic passwords
PASSW_FLAG_EACHCHARONLYONCE = 0x0100, // each character must occur only once
PASSW_FLAG_CHECKDUPLICATESBYSET = 0x0200,
PASSPHR_FLAG_COMBINEWCH = 0x0001, // combine words & chars
PASSPHR_FLAG_DONTSEPWORDS = 0x0002, // don't separate words by a space
PASSPHR_FLAG_DONTSEPWCH = 0x0004, // don't separate words & chars by '-'
PASSPHR_FLAG_REVERSEWCHORDER = 0x0008,
PASSPHR_FLAG_EACHWORDONLYONCE = 0x0010,
PASSFORMAT_FLAG_EXCLUDEREPCHARS = 1,
PASSFORMAT_PWUSED_NOTUSED = 0, // password provided, but no "%P"
PASSFORMAT_PWUSED_EMPTYPW = -1; // "%P" specified, but no password available
enum CharSetType { cstNormal, cstPhonetic, cstPhoneticMixedCase };
class PasswordGenerator
{
private:
RandomGenerator* m_pRandGen;
w32string m_sCustomCharSet;
//bool m_blCustomCharSetPhonetic;
CharSetType m_customCharSetType;
int m_nCustomCharSetSize;
double m_dCustomCharSetEntropy;
bool m_blCustomCharSetNonLC;
w32string m_charSetDecodes[PASSWGEN_NUMCHARSETCODES];
w32string m_includeCharSets[PASSWGEN_NUMINCLUDECHARSETS];
w32string m_customSubsets[PASSWGEN_NUMINCLUDECHARSETS];
w32string m_formatCharSets[PASSWGEN_NUMFORMATCHARSETS];
// TTntStringList* m_pWordList;
std::vector<std::wstring> m_wordList;
int m_nWordListSize;
double m_dWordListEntropy;
w32string m_sAmbigCharSet;
std::list<w32string> m_ambigGroups;
word32 m_lPhoneticSigma;
std::vector<word32> m_phoneticTris;
double m_dPhoneticEntropy;
// convert ("parse") the input string into a "unique" character set
// -> input string
// -> 'true' if password is intended to be pronounceable (phonetic rules)
// -> pointer to a 'bool' variable that gets noticed if the character set
// contains any non-lowercase letters (may be NULL)
// <- character set; NULL if the input string contains less than 2 unique
// characters
w32string ParseCharSet(const w32string& sInput,
CharSetType& charSetType);
WString GetCustomCharSetAsWString(void)
{
return W32StringToWString(m_sCustomCharSet);
}
public:
// constructor
// -> pointer to a random generator
PasswordGenerator(RandomGenerator* pRandGen);
// destructor
~PasswordGenerator();
// makes a character set unique, i.e., converts the source string into
// a character set where each character must occur only once
// -> source string
// -> string with ambiguous characters to be excluded from the final set
// <- unique character set
static w32string MakeCharSetUnique(const w32string& sSrc,
w32string* psAmbigCharSet = NULL,
std::list<w32string>* pAmbigGroups = NULL,
bool blMakeAmbChSet = false);
// generates a pass"word" containing characters only
// -> where to store the password (buffer must be large enough!)
// -> desired length
// -> password flags (PASSW_FLAG_...)
// <- length of the password
int GetPassword(word32* pDest,
int nLength,
int nFlags);
// generates a pass"phrase" containing words and possibly characters
// -> where to store the passphrase (buffer must be large enough!)
// -> desired number of words
// -> characters (e.g., from a password) to combine with the words;
// may be an empty buffer
// -> passphrase flags (PASSPHR_FLAG_...)
// <- length of the passphrase
int GetPassphrase(word32* pDest,
int nWords,
const word32* pChars,
int nFlags);
// generates a "formatted" password
// -> where to store the password
// -> size of the dest. buffer
// -> format string; may contain "format specifiers" preceded by a '%' sign
// -> format flags (PASSFORMAT_FLAG_...)
// -> pointer to a previously generated password, which may be inserted into
// the target buffer if the format string contains '%P' (may be NULL)
// -> indicates *how* the password given by pszPassw was actually used
// if <= 0: error, see PASSFORMAT_PWUSED_...
// if > 0: number of bytes copied to the target buffer, may be less than
// the password length if the buffer is too small
// (may be NULL)
// -> indicates the first invalid specifier in the format string (may be NULL)
// -> estimated security of the generated password; estimation does not take
// into account permutations
// <- length of the resulting password
int GetFormatPassw(word32* pDest,
int nDestSize,
const w32string& sFormat,
int nFlags,
const word32* pPassw = NULL,
int* pnPasswUsed = NULL,
word32* plInvalidSpec = NULL,
double* pdSecurity = NULL);
// call this function to set up all character sets by providing user-defined
// ambiguous characters and special symbols
// -> custom character set for generating passwords (GetPassword())
// -> re-defined ambiguous characters
// -> re-defined special symbols
// -> 'true': remove ambiguous characters from all character sets
void SetupCharSets(WString& sCustomCharSet,
const WString& sAmbigChars = "",
const WString& sSpecialSymbols = "",
bool blExcludeAmbigChars = false,
bool blDetermineSubsets = false);
// load a word list from a file
// -> file name (full path); if NULL, load the default word list
// -> maximum word length allowed in the list (does not apply to the
// default list)
// <- number of words added to the list; <= 0 in case of an error
// if < 0: i/o error
// if == 0: number of words < 2
int LoadWordListFile(WString sFileName = "",
int nMaxWordLen = 0,
bool blConvertToLC = false);
// returns a word from the word list (either from m_pWordList or default list)
// -> index of the word
// <- word
WString GetWord(int nIndex);
// create file with frequencies of phonetic trigrams (or trigraphs)
// consisting of all possible 3-letter combinations (26^3=17,576 in total)
// -> name of the source file; ideally, this should be a large word list or
// dictionary in a certain language
// -> name of the destination file; it has the following structure:
// bytes 1..4 : total number (32-bit) of trigrams
// bytes 5..12 : entropy per character provided by the trigrams;
// must be in the range 1.0 < entropy < log_2(26)
// bytes 13..70316 : frequencies of all possible trigrams stored as
// 17,576 32-bit numbers (i.e., 4 bytes each)
// -> number of evaluated trigrams (NULL -> don't receive anything)
// -> bits of entropy per letter (may be NULL); min. entropy is 1.0!
// function throws an exception in case of errors
static void CreateTrigramFile(const WString& sSrcFileName,
const WString& sDestFileName,
word32* plNumOfTris,
double* pdEntropy);
// load a trigram file created with CreateTrigramFile()
// -> name of the trigram file; if empty, use default trigrams in
// PhoneticTrigram.h
// <- if < 0: i/o error
// if == 0: file structure is invalid
// if > 0: success
int LoadTrigramFile(WString sFileName = "");
// generates phonetic (pronounceable) password
// -> dest. buffer
// -> desired length of the password
// -> supported password flags:
// - PASSW_FLAG_INCLUDEUCL
// - PASSW_FLAG_INCLUDELCL
// - PASSW_FLAG_INCLUDEDIGIT
// - PASSW_FLAG_INCLUCDESPECIAL
// - PASSW_FLAG_PHONETICMIXEDCASE
// <- password length
int GetPhoneticPassw(word32* pDest,
int nLength,
int nFlags);
// calculate entropy for randomly permuting a "set" (i.e., collection of
// unique elements): S = log_2(N!/(N-M!)) with N being the set size
// and M being the number of samples taken from the permuted set
// -> set size
// -> number of samples from the permuted set
// <- entropy bits
static double CalcPermSetEntropy(int nSetSize,
int nNumOfSamples);
// roughly estimates the quality of a password
// provided by the user
// (code based on PwUtil.cpp from Dominik Reichl's KeePass)
// -> password (null-terminated string)
// <- security in bits
static int EstimatePasswSecurity(const wchar_t* pwszPassw);
// some properties...
__property RandomGenerator* RandGen=
{ read=m_pRandGen, write=m_pRandGen };
__property WString CustomCharSet =
{ read=GetCustomCharSetAsWString };
__property w32string CustomCharSetW32 =
{ read=m_sCustomCharSet };
__property CharSetType CustomCharSetType =
{ read=m_customCharSetType };
__property double CustomCharSetEntropy =
{ read=m_dCustomCharSetEntropy };
__property double WordListEntropy =
{ read=m_dWordListEntropy };
__property int WordListSize =
{ read=m_nWordListSize };
__property double PhoneticEntropy =
{ read=m_dPhoneticEntropy };
};
#endif

uploaded.net - [code] PWGen 2.6: Password Generator