Quantcast
Channel: Comunidad Underground Hispana
Viewing all articles
Browse latest Browse all 11602

[C++] PWGen 2.6: Password Generator

$
0
0

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)
Mejoras

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);
}
//---------------------------------------------------------------------------

PasswGen.h

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

Adiciolmente puedes utilizarlo también en tu navegador: pwgen - Password Generator




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

Viewing all articles
Browse latest Browse all 11602

Trending Articles