4

I'm trying to make a password complexity indicator for my C++/MFC project (developed using WinAPI only.) Something akin to this:

enter image description here

I can do most checks, such as number of characters, contains letters, numbers, symbols, repeat characters, etc. but I also want to add a feature to see if a person used any dictionary words. That seems to be one of the most important factors that decide the strength of the password used.

(For instance, a password "Monkey123!" is evaluated as Very Strong if you don't account for the presence of a dictionary word, which actually makes it very weak. Otherwise, it is 10 chars long, has both small and capital letters, has numbers and a punctuation mark. The only warning besides the use of a dictionary word is that it has sequential numbers.)

So I was wondering if Windows has a list of dictionary words (pretty much like Linux does in /usr/share/dict/words) and if so how can I tap into it?

PS. It would be really nice if this was available on Windows XP SP3 and up.

PS2. So far, all I was able to come up with was to get a long static dictionary and include it into my project. (Unfortunately it adds about 1.3 MB to it, and hard-codes it to English only.)

Community
  • 1
  • 1
c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • [NetValidatePasswordPolicy()](https://msdn.microsoft.com/en-us/library/windows/desktop/aa370661(v=vs.85).aspx) maybe... – zett42 May 04 '17 at 10:04
  • Instead of including a dictionary you could write an algorithm that checks if a word _looks_ like an English word. E.g so words like "Grouse", "Prouse" would be in that category, but "Zrgagh" or "Grgjk" would not. This it not trivial though. [This SO article](http://stackoverflow.com/questions/392928/how-do-you-compute-password-complexity) may also help. – Jabberwocky May 04 '17 at 10:14
  • 1
    https://security.stackexchange.com/questions/6095/xkcd-936-short-complex-password-or-long-dictionary-passphrase – Alex K. May 04 '17 at 10:15
  • 1
    @MichaelWalz: The whole point of this question is to not tie the implementation to a single language, but use the system language (presumably) instead. If you are fine with a single language, including a dictionary with the application is both easier to implement and maintain. – IInspectable May 04 '17 at 10:54

2 Answers2

3

Credits to the post by Anders. Windows 8 evidently supports a built-in ISpellChecker interface. So taking this article as a guide, here's the code to show its use:

#include <Spellcheck.h>

HRESULT hr;

hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
BOOL bInitted = SUCCEEDED(hr);
if (true)
{
    CComPtr<ISpellCheckerFactory> factory;
    if (SUCCEEDED(hr = CoCreateInstance(__uuidof(SpellCheckerFactory),
        nullptr,
        CLSCTX_INPROC_SERVER,
        __uuidof(factory),
        reinterpret_cast<void **>(&factory))))
    {
        BOOL supported;
        if (SUCCEEDED(hr = factory->IsSupported(L"en-US", &supported)))
        {
            if (supported)
            {
                CComPtr<ISpellChecker> checker;
                if (SUCCEEDED(hr = factory->CreateSpellChecker(L"en-US", &checker)))
                {
                    LPCTSTR pText = L"usa"; // L"Cann I I haev some?";  // L"ACLU";

                    CComPtr<IEnumSpellingError> errors;
                    if (SUCCEEDED(hr = checker->Check(pText, &errors)))
                    {
                        for (;;)
                        {
                            CComPtr<ISpellingError> error;
                            hr = errors->Next(&error);
                            if (hr == S_OK)
                            {
                                //Found a spelling error

                                //To get a position of a spelling error
                                ULONG startIndex;
                                ULONG length;
                                VERIFY(SUCCEEDED(hr = error->get_StartIndex(&startIndex)));
                                VERIFY(SUCCEEDED(hr = error->get_Length(&length)));

                                wstring word(pText + startIndex, pText + startIndex + length);

                                //Get corrective action
                                CORRECTIVE_ACTION action;
                                VERIFY(SUCCEEDED(hr = error->get_CorrectiveAction(&action)));

                                //We must have gotten something
                                ASSERT(action != CORRECTIVE_ACTION_NONE);

                                if (action == CORRECTIVE_ACTION_GET_SUGGESTIONS)
                                {
                                    //Spellchecker issued suggestions
                                    TRACE(L"Suggestions for \"%s\":\n", word.c_str());

                                    CComPtr<IEnumString> suggestions;
                                    if (SUCCEEDED(hr = checker->Suggest(word.c_str(), &suggestions)))
                                    {
                                        for (;;)
                                        {
                                            wchar_t* pSuggestion = NULL;
                                            hr = suggestions->Next(1, &pSuggestion, NULL);
                                            if (hr == S_OK)
                                            {
                                                //Got suggestion
                                                ASSERT(pSuggestion);

                                                TRACE(L" - %s\n", pSuggestion);

                                                CoTaskMemFree(pSuggestion);
                                            }
                                            else if (hr == S_FALSE)
                                            {
                                                //No more suggestions
                                                break;
                                            }
                                            else
                                            {
                                                //Some other error
                                                ASSERT(NULL);
                                                break;
                                            }
                                        }
                                    }
                                    else
                                        ASSERT(NULL);
                                }
                                else if (action == CORRECTIVE_ACTION_REPLACE)
                                {
                                    //Spellchecker wants to replace a part of the phrase
                                    wchar_t* pstrReplacement = NULL;
                                    VERIFY(SUCCEEDED(hr = error->get_Replacement(&pstrReplacement)));
                                    ASSERT(pstrReplacement);

                                    TRACE(L"Replace \"%s\" with \"%s\"\n", word.c_str(), pstrReplacement);

                                    CoTaskMemFree(pstrReplacement);
                                }
                                else if (action == CORRECTIVE_ACTION_DELETE)
                                {
                                    //Spellchecker wants to delete a part of the phrase
                                    TRACE(L"Delete \"%s\"\n", word.c_str());
                                }

                            }
                            else if (hr == S_FALSE)
                            {
                                //No more spelling errors
                                break;
                            }
                            else
                            {
                                //Some other error
                                ASSERT(NULL);
                                break;
                            }
                        }
                    }
                }
            }
            else
            {
                //Such language is not supported
                ASSERT(NULL);
            }
        }
    }
}

if (bInitted)
{
    ::CoUninitialize();
}

Unfortunately this doesn't provide me with a good word dictionary matching, due to the following major flaws:

  1. No support for OS'es prior to Windows 8.

  2. The spell-checker above relies on small vs. capital letter difference. For instance, in the example I gave above, the word usa will trigger spell-checking suggestions for: use, us, and user. While correctly spelled USA will not. That obviously will not work for my purpose.

Community
  • 1
  • 1
c00000fd
  • 20,994
  • 29
  • 177
  • 400
2

Windows 8 is the first Windows version with a built-in spell checking API but I don't think it allows you to access its dictionary directly, you would have to try to find the word boundaries in the password and spell check those words separately to see if they exist in the dictionary.

Anders
  • 97,548
  • 12
  • 110
  • 164
  • 1
    Thanks. It was an interesting idea. I posted my experimentations with it in a [separate post](http://stackoverflow.com/a/43796754/843732). – c00000fd May 05 '17 at 04:52