/*++ Copyright (c) 1997 Microsoft Corporation Module Name: encrypt.c Abstract: Provides a set of functions dealing with OWF hash values of passwords. Author: Ovidiu Temereanca (ovidiut) 14-Mar-2000 Revision History: --*/ // // Includes // #include #include "encrypt.h" // // Strings // // None // // Constants // // None // // Macros // // None // // Types // // None // // Globals // // None // // Macro expansion list // // None // // Private function prototypes // // None // // Macro expansion definition // // None // // Code // PSTR ConvertW2A ( IN PCWSTR Unicode, IN UINT CodePage ) /*++ Routine Description: Converts an UNICODE string to it's ANSI equivalent, using the given codepage. Arguments: Unicode - Specifies the string to be converted CodePage - Specifies the code page used for conversion Return value: A pointer to the ANSI string if successful, or NULL on error. Call GetLastError() to determine the cause of failure. --*/ { PSTR ansi = NULL; DWORD rc; rc = WideCharToMultiByte ( CodePage, WC_NO_BEST_FIT_CHARS, Unicode, -1, NULL, 0, NULL, NULL ); if (rc || *Unicode == L'\0') { ansi = (PSTR)HeapAlloc (GetProcessHeap (), 0, (rc + 1) * sizeof (CHAR)); if (ansi) { rc = WideCharToMultiByte ( CodePage, WC_NO_BEST_FIT_CHARS, Unicode, -1, ansi, rc + 1, NULL, NULL ); if (!(rc || *Unicode == L'\0')) { rc = GetLastError (); HeapFree (GetProcessHeap (), 0, (PVOID)ansi); ansi = NULL; SetLastError (rc); } } } return ansi; } PWSTR ConvertA2W ( IN PCSTR Ansi, IN UINT CodePage ) /*++ Routine Description: Converts an ANSI string to it's UNICODE equivalent, using the given codepage. Arguments: Ansi - Specifies the string to be converted CodePage - Specifies the code page used for conversion Return value: A pointer to the UNICODE string if successful, or NULL on error. Call GetLastError() to determine the cause of failure. --*/ { PWSTR unicode = NULL; DWORD rc; rc = MultiByteToWideChar ( CodePage, MB_ERR_INVALID_CHARS, Ansi, -1, NULL, 0 ); if (rc || *Ansi == '\0') { unicode = (PWSTR) HeapAlloc (GetProcessHeap (), 0, (rc + 1) * sizeof (WCHAR)); if (unicode) { rc = MultiByteToWideChar ( CodePage, MB_ERR_INVALID_CHARS, Ansi, -1, unicode, rc + 1 ); if (!(rc || *Ansi == '\0')) { rc = GetLastError (); HeapFree (GetProcessHeap (), 0, (PVOID)unicode); unicode = NULL; SetLastError (rc); } } } return unicode; } /*++ Routine Description: EncodeLmOwfPassword converts a password to the LM OWF format. Arguments: Password - Specifies the password to be hashed OwfPassword - Receives the hash form ComplexNtPassword - Receives TRUE if the password is complex (longer than 14 chars); optional Return value: TRUE on successful hashing --*/ BOOL EncodeLmOwfPasswordA ( IN PCSTR AnsiPassword, OUT PLM_OWF_PASSWORD OwfPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { CHAR oemPassword[LM_PASSWORD_SIZE_MAX]; CHAR password[LM_PASSWORD_SIZE_MAX]; BOOL complex; if (!AnsiPassword) { AnsiPassword = ""; } complex = lstrlenA (AnsiPassword) > LM20_PWLEN; if (ComplexNtPassword) { *ComplexNtPassword = complex; } if (complex) { password[0] = 0; } else { lstrcpyA (oemPassword, AnsiPassword); CharUpperA (oemPassword); CharToOemA (oemPassword, password); } return CalculateLmOwfPassword (password, OwfPassword); } BOOL EncodeLmOwfPasswordW ( IN PCWSTR Password, OUT PLM_OWF_PASSWORD OwfPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { PSTR ansi; BOOL b = FALSE; if (!Password) { Password = L""; } ansi = ConvertW2A (Password, CP_ACP); if (ansi) { b = EncodeLmOwfPasswordA (ansi, OwfPassword, ComplexNtPassword); HeapFree (GetProcessHeap (), 0, (PVOID)ansi); } return b; } /*++ Routine Description: StringEncodeLmOwfPassword converts a password to the LM OWF format, expressed as a string of characters (each byte converted to 2 hex digits). Arguments: Password - Specifies the password to be hashed EncodedPassword - Receives the hash form, as a string of hex digits ComplexNtPassword - Receives TRUE if the password is complex (longer than 14 chars); optional Return value: TRUE on successful hashing --*/ BOOL StringEncodeLmOwfPasswordA ( IN PCSTR Password, OUT PSTR EncodedPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { LM_OWF_PASSWORD owfPassword; PBYTE start; PBYTE end; PSTR dest; if (!EncodeLmOwfPasswordA (Password, &owfPassword, ComplexNtPassword)) { return FALSE; } // // each byte will be represented as 2 chars, so it will be twice as long // start = (PBYTE)&owfPassword; end = start + sizeof (LM_OWF_PASSWORD); dest = EncodedPassword; while (start < end) { dest += wsprintfA (dest, "%02x", (UINT)(*start)); start++; } return TRUE; } BOOL StringEncodeLmOwfPasswordW ( IN PCWSTR Password, OUT PWSTR EncodedPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { LM_OWF_PASSWORD owfPassword; PBYTE start; PBYTE end; PWSTR dest; if (!EncodeLmOwfPasswordW (Password, &owfPassword, ComplexNtPassword)) { return FALSE; } // // each byte will be represented as 2 chars, so it will be twice as long // start = (PBYTE)&owfPassword; end = start + sizeof (LM_OWF_PASSWORD); dest = EncodedPassword; while (start < end) { dest += wsprintfW (dest, L"%02x", (UINT)(*start)); start++; } return TRUE; } /*++ Routine Description: EncodeNtOwfPassword converts a password to the NT OWF format. Arguments: Password - Specifies the password to be hashed OwfPassword - Receives the hash form Return value: TRUE on successful hashing --*/ BOOL EncodeNtOwfPasswordA ( IN PCSTR Password, OUT PNT_OWF_PASSWORD OwfPassword ) { PWSTR unicode; BOOL b = FALSE; unicode = ConvertA2W (Password, CP_ACP); if (unicode) { b = EncodeNtOwfPasswordW (unicode, OwfPassword); HeapFree (GetProcessHeap (), 0, unicode); } return b; } BOOL EncodeNtOwfPasswordW ( IN PCWSTR Password, OUT PNT_OWF_PASSWORD OwfPassword ) { NT_PASSWORD pwd; if (Password) { pwd.Buffer = (PWSTR)Password; pwd.Length = (USHORT)lstrlenW (Password) * (USHORT)sizeof (WCHAR); pwd.MaximumLength = pwd.Length + (USHORT) sizeof (WCHAR); } else { ZeroMemory (&pwd, sizeof (pwd)); } return CalculateNtOwfPassword (&pwd, OwfPassword); } /*++ Routine Description: StringEncodeNtOwfPassword converts a password to the NT OWF format, expressed as a string of characters (each byte converted to 2 hex digits). Arguments: Password - Specifies the password to be hashed EncodedPassword - Receives the hash form, as a string of hex digits Return value: TRUE on successful hashing --*/ BOOL StringEncodeNtOwfPasswordA ( IN PCSTR Password, OUT PSTR EncodedPassword ) { NT_OWF_PASSWORD owfPassword; PBYTE start; PBYTE end; PSTR dest; if (!EncodeNtOwfPasswordA (Password, &owfPassword)) { return FALSE; } // // each byte will be represented as 2 chars, so it will be twice as long // start = (PBYTE)&owfPassword; end = start + sizeof (NT_OWF_PASSWORD); dest = EncodedPassword; while (start < end) { dest += wsprintfA (dest, "%02x", (UINT)(*start)); start++; } return TRUE; } BOOL StringEncodeNtOwfPasswordW ( IN PCWSTR Password, OUT PWSTR EncodedPassword ) { NT_OWF_PASSWORD owfPassword; PBYTE start; PBYTE end; PWSTR dest; if (!EncodeNtOwfPasswordW (Password, &owfPassword)) { return FALSE; } // // each byte will be represented as 2 chars, so it will be twice as long // start = (PBYTE)&owfPassword; end = start + sizeof (NT_OWF_PASSWORD); dest = EncodedPassword; while (start < end) { dest += wsprintfW (dest, L"%02x", (UINT)(*start)); start++; } return TRUE; } /*++ Routine Description: StringDecodeLmOwfPassword converts a hashed password to the LM OWF format Arguments: EncodedOwfPassword - Specifies the password to be hashed OwfPassword - Receives the hash form Return value: TRUE on successful decoding of the string --*/ BOOL StringDecodeLmOwfPasswordA ( IN PCSTR EncodedOwfPassword, OUT PLM_OWF_PASSWORD OwfPassword ) { DWORD nible; PCSTR p; PBYTE dest; CHAR ch; if (lstrlenA (EncodedOwfPassword) != sizeof (LM_OWF_PASSWORD) * 2) { return FALSE; } nible = 0; p = EncodedOwfPassword; dest = (PBYTE)OwfPassword; ch = 0; while (*p) { if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F'))) { return FALSE; } if (*p <= '9') { ch |= *p - '0'; } else if (*p <= 'F') { ch |= *p - 'A' + 10; } else { ch |= *p - 'a' + 10; } p++; nible++; if ((nible & 1) == 0) { *dest++ = ch; ch = 0; } else { ch <<= 4; } } return TRUE; } BOOL StringDecodeLmOwfPasswordW ( IN PCWSTR EncodedOwfPassword, OUT PLM_OWF_PASSWORD OwfPassword ) { DWORD nible; PCWSTR p; PBYTE dest; WCHAR ch; if (lstrlenW (EncodedOwfPassword) != sizeof (LM_OWF_PASSWORD) * 2) { return FALSE; } nible = 0; p = EncodedOwfPassword; dest = (PBYTE)OwfPassword; ch = 0; while (*p) { if (!((*p >= L'0' && *p <= L'9') || (*p >= L'a' && *p <= L'f') || (*p >= L'A' && *p <= L'F'))) { return FALSE; } if (*p <= L'9') { ch |= *p - L'0'; } else if (*p <= L'F') { ch |= *p - L'A' + 10; } else { ch |= *p - L'a' + 10; } p++; nible++; if ((nible & 1) == 0) { *dest++ = (BYTE)ch; ch = 0; } else { ch <<= 4; } } return TRUE; } /*++ Routine Description: StringDecodeNtOwfPassword converts a hashed password to the NT OWF format Arguments: EncodedOwfPassword - Specifies the password to be hashed OwfPassword - Receives the hash form Return value: TRUE on successful decoding of the string --*/ BOOL StringDecodeNtOwfPasswordA ( IN PCSTR EncodedOwfPassword, OUT PNT_OWF_PASSWORD OwfPassword ) { DWORD nible; PCSTR p; PBYTE dest; CHAR ch; if (lstrlenA (EncodedOwfPassword) != sizeof (NT_OWF_PASSWORD) * 2) { return FALSE; } nible = 0; p = EncodedOwfPassword; dest = (PBYTE)OwfPassword; ch = 0; while (*p) { if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F'))) { return FALSE; } if (*p <= '9') { ch |= *p - '0'; } else if (*p <= 'F') { ch |= *p - 'A' + 10; } else { ch |= *p - 'a' + 10; } p++; nible++; if ((nible & 1) == 0) { *dest++ = ch; ch = 0; } else { ch <<= 4; } } return TRUE; } BOOL StringDecodeNtOwfPasswordW ( IN PCWSTR EncodedOwfPassword, OUT PNT_OWF_PASSWORD OwfPassword ) { DWORD nible; PCWSTR p; PBYTE dest; WCHAR ch; if (lstrlenW (EncodedOwfPassword) != sizeof (NT_OWF_PASSWORD) * 2) { return FALSE; } nible = 0; p = EncodedOwfPassword; dest = (PBYTE)OwfPassword; ch = 0; while (*p) { if (!((*p >= L'0' && *p <= L'9') || (*p >= L'a' && *p <= L'f') || (*p >= L'A' && *p <= L'F'))) { return FALSE; } if (*p <= L'9') { ch |= *p - L'0'; } else if (*p <= L'F') { ch |= *p - L'A' + 10; } else { ch |= *p - L'a' + 10; } p++; nible++; if ((nible & 1) == 0) { *dest++ = (BYTE)ch; ch = 0; } else { ch <<= 4; } } return TRUE; } /*++ Routine Description: StringEncodeOwfPassword converts a password to its hashed format, expressed as a string of characters (each byte converted to 2 hex digits). The result is obtained joining the 2 substrings, one representing LM OWF and the other NT OWF Arguments: Password - Specifies the password to be hashed EncodedPassword - Receives the hash form, as a string of hex digits; the buffer MUST be at least STRING_ENCODED_PASSWORD_SIZE chars (including nul) ComplexNtPassword - Receives TRUE if the password is complex (longer than 14 chars); optional Return value: TRUE on successful hashing --*/ BOOL StringEncodeOwfPasswordA ( IN PCSTR Password, OUT PSTR EncodedPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { return StringEncodeLmOwfPasswordA (Password, EncodedPassword, ComplexNtPassword) && StringEncodeNtOwfPasswordA (Password, EncodedPassword + STRING_ENCODED_LM_OWF_PWD_LENGTH); } BOOL StringEncodeOwfPasswordW ( IN PCWSTR Password, OUT PWSTR EncodedPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { return StringEncodeLmOwfPasswordW (Password, EncodedPassword, ComplexNtPassword) && StringEncodeNtOwfPasswordW (Password, EncodedPassword + STRING_ENCODED_LM_OWF_PWD_LENGTH); } /*++ Routine Description: StringDecodeOwfPassword decodes a password's LM OWF and NT OWF forms from its hashed format, expressed as a string of hex digits. Arguments: EncodedOwfPassword - Specifies the password to be hashed LmOwfPassword - Receives the LM OWF hash form NtOwfPassword - Receives the NT OWF hash form ComplexNtPassword - Receives TRUE if the password is complex (longer than 14 chars); optional Return value: TRUE on successful hashing --*/ BOOL StringDecodeOwfPasswordA ( IN PCSTR EncodedOwfPassword, OUT PLM_OWF_PASSWORD LmOwfPassword, OUT PNT_OWF_PASSWORD NtOwfPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { PSTR p; CHAR ch; BOOL b; CHAR buffer[sizeof (LM_OWF_PASSWORD) * 2 + sizeof (NT_OWF_PASSWORD) * 2 + 2]; LM_OWF_PASSWORD lmNull; NT_OWF_PASSWORD ntNull; if (lstrlenA (EncodedOwfPassword) != sizeof (LM_OWF_PASSWORD) * 2 + sizeof (NT_OWF_PASSWORD) * 2) { return FALSE; } lstrcpyA (buffer, EncodedOwfPassword); // // split the string in two // p = buffer + (sizeof (LM_OWF_PASSWORD) * 2); ch = *p; *p = 0; b = StringDecodeLmOwfPasswordA (EncodedOwfPassword, LmOwfPassword); *p = ch; if (b) { b = StringDecodeNtOwfPasswordA (p, NtOwfPassword); } if (b && ComplexNtPassword) { b = EncodeLmOwfPasswordA ("", &lmNull, NULL) && EncodeNtOwfPasswordA ("", &ntNull); if (b) { // // it's a complex password if the LM hash is for NULL pwd // but NT hash it's not // *ComplexNtPassword = CompareLmPasswords (LmOwfPassword, &lmNull) == 0 && CompareNtPasswords (NtOwfPassword, &ntNull) != 0; } } return b; } BOOL StringDecodeOwfPasswordW ( IN PCWSTR EncodedOwfPassword, OUT PLM_OWF_PASSWORD LmOwfPassword, OUT PNT_OWF_PASSWORD NtOwfPassword, OUT PBOOL ComplexNtPassword OPTIONAL ) { PWSTR p; WCHAR ch; BOOL b; WCHAR buffer[sizeof (LM_OWF_PASSWORD) * 2 + sizeof (NT_OWF_PASSWORD) * 2 + 2]; LM_OWF_PASSWORD lmNull; NT_OWF_PASSWORD ntNull; if (lstrlenW (EncodedOwfPassword) != sizeof (LM_OWF_PASSWORD) * 2 + sizeof (NT_OWF_PASSWORD) * 2) { return FALSE; } lstrcpyW (buffer, EncodedOwfPassword); // // split the string in two // p = buffer + (sizeof (LM_OWF_PASSWORD) * 2); ch = *p; *p = 0; b = StringDecodeLmOwfPasswordW (buffer, LmOwfPassword); *p = ch; if (b) { b = StringDecodeNtOwfPasswordW (p, NtOwfPassword); } if (b && ComplexNtPassword) { b = EncodeLmOwfPasswordW (L"", &lmNull, NULL) && EncodeNtOwfPasswordW (L"", &ntNull); if (b) { // // it's a complex password if the LM hash is for NULL pwd // but NT hash it's not // *ComplexNtPassword = CompareLmPasswords (LmOwfPassword, &lmNull) == 0 && CompareNtPasswords (NtOwfPassword, &ntNull) != 0; } } return b; }