diff --git a/PowerEditor/src/MISC/Common/verifySignedfile.cpp b/PowerEditor/src/MISC/Common/verifySignedfile.cpp index 2553d971..0247c8fb 100644 --- a/PowerEditor/src/MISC/Common/verifySignedfile.cpp +++ b/PowerEditor/src/MISC/Common/verifySignedfile.cpp @@ -38,16 +38,61 @@ #include #include "VerifySignedFile.h" #include "Common.h" +#include "sha-256.h" using namespace std; -bool VerifySignedLibrary(const wstring& filepath, - const wstring& cert_key_id_hex, - const wstring& cert_subject, - const wstring& cert_display_name, - bool doCheckRevocation, - bool doCheckChainOfTrust, - bool displayErrorMessage) +SecurityMode SecurityGard::_securityMode = sm_sha256; +//SecurityMode SecurityGard::_securityMode = sm_certif; + +SecurityGard::SecurityGard() +{ + _scilexerSha256.push_back(TEXT("03c9177631d2b32de3d32c73a8841cf68fc2cb17f306825489dc3df98000db85")); // v3.5.6 32 bit + _scilexerSha256.push_back(TEXT("9896c4089275e21412fd80421827912ebd80e357394b05145a613d190462e211")); // v3.5.6 64 bit + + _gupSha256.push_back(TEXT("4c8191f511c2ad67148ef809b40c1108aaa074130547157c335a959404d8d6f6")); // v5.1 32 bit + _gupSha256.push_back(TEXT("268a65829e86d5c3d324eea79b51e59f0a7d07c69d3ba0f700c9cb3aa772566f")); // v5.1 64 bit +} + +bool SecurityGard::checkModule(std::wstring filePath, NppModule module2check) +{ + if (_securityMode == sm_certif) + return verifySignedLibrary(filePath, module2check); + else if (_securityMode == sm_sha256) + return checkSha256(filePath, module2check); + else + return false; +} + +bool SecurityGard::checkSha256(std::wstring filePath, NppModule module2check) +{ + std::string content = getFileContent(filePath.c_str()); + + uint8_t sha2hash[32]; + calc_sha_256(sha2hash, reinterpret_cast(content.c_str()), content.length()); + + wchar_t sha2hashStr[65] = { '\0' }; + for (size_t i = 0; i < 32; i++) + wsprintf(sha2hashStr + i * 2, TEXT("%02x"), sha2hash[i]); + + std::vector* moduleSha256 = nullptr; + if (module2check == nm_scilexer) + moduleSha256 = &_scilexerSha256; + else if (module2check == nm_gup) + moduleSha256 = &_gupSha256; + else if (module2check == nm_pluginList) + moduleSha256 = &_pluginListSha256; + else + return false; + + for (auto i : *moduleSha256) + if (i == sha2hashStr) + return true; + + return false; +} + +bool SecurityGard::verifySignedLibrary(const std::wstring& filepath, NppModule module2check) { wstring display_name; wstring key_id_hex; @@ -56,7 +101,7 @@ bool VerifySignedLibrary(const wstring& filepath, wstring dmsg(TEXT("VerifyLibrary: ")); dmsg += filepath; dmsg += TEXT("\n"); - + OutputDebugString(dmsg.c_str()); // @@ -72,13 +117,13 @@ bool VerifySignedLibrary(const wstring& filepath, // Initialise WinTrust data WINTRUST_DATA winTEXTrust_data = { 0 }; winTEXTrust_data.cbStruct = sizeof(winTEXTrust_data); - winTEXTrust_data.dwUIChoice = WTD_UI_NONE; // do not display optional dialog boxes - winTEXTrust_data.dwUnionChoice = WTD_CHOICE_FILE; // we are not checking catalog signed files - winTEXTrust_data.dwStateAction = WTD_STATEACTION_VERIFY; // only checking + winTEXTrust_data.dwUIChoice = WTD_UI_NONE; // do not display optional dialog boxes + winTEXTrust_data.dwUnionChoice = WTD_CHOICE_FILE; // we are not checking catalog signed files + winTEXTrust_data.dwStateAction = WTD_STATEACTION_VERIFY; // only checking winTEXTrust_data.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; // verify the whole certificate chain - winTEXTrust_data.pFile = &file_data; + winTEXTrust_data.pFile = &file_data; - if (!doCheckRevocation) + if (!_doCheckRevocation) { winTEXTrust_data.fdwRevocationChecks = WTD_REVOKE_NONE; OutputDebugString(TEXT("VerifyLibrary: certificate revocation checking is disabled\n")); @@ -102,7 +147,7 @@ bool VerifySignedLibrary(const wstring& filepath, } } - if (doCheckChainOfTrust) + if (_doCheckChainOfTrust) { // Verify signature and cert-chain validity GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2; @@ -128,52 +173,52 @@ bool VerifySignedLibrary(const wstring& filepath, // // Certificate verification // - HCERTSTORE hStore = nullptr; - HCRYPTMSG hMsg = nullptr; + HCERTSTORE hStore = nullptr; + HCRYPTMSG hMsg = nullptr; PCMSG_SIGNER_INFO pSignerInfo = nullptr; DWORD dwEncoding, dwContentType, dwFormatType; DWORD dwSignerInfo = 0L; - bool status = true; + bool status = true; try { BOOL result = ::CryptQueryObject(CERT_QUERY_OBJECT_FILE, filepath.c_str(), - CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, - &dwEncoding, &dwContentType, &dwFormatType, - &hStore, &hMsg, NULL); + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, + &dwEncoding, &dwContentType, &dwFormatType, + &hStore, &hMsg, NULL); if (!result) { - throw wstring( TEXT("Checking certificate of ") ) + filepath + TEXT(" : ") + GetLastErrorAsString(GetLastError()); + throw wstring(TEXT("Checking certificate of ")) + filepath + TEXT(" : ") + GetLastErrorAsString(GetLastError()); } // Get signer information size. result = ::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo); if (!result) - { - throw wstring( TEXT("CryptMsgGetParam first call: ")) + GetLastErrorAsString(GetLastError()); + { + throw wstring(TEXT("CryptMsgGetParam first call: ")) + GetLastErrorAsString(GetLastError()); } // Get Signer Information. pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo); - if (NULL == pSignerInfo ) + if (NULL == pSignerInfo) { - throw wstring( TEXT("Failed to allocate memory for signature processing")); + throw wstring(TEXT("Failed to allocate memory for signature processing")); } result = ::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo); if (!result) { - throw wstring( TEXT("CryptMsgGetParam: ")) + GetLastErrorAsString(GetLastError()); + throw wstring(TEXT("CryptMsgGetParam: ")) + GetLastErrorAsString(GetLastError()); } // Get the signer certificate from temporary certificate store. CERT_INFO cert_info = { 0 }; - cert_info.Issuer = pSignerInfo->Issuer; + cert_info.Issuer = pSignerInfo->Issuer; cert_info.SerialNumber = pSignerInfo->SerialNumber; - PCCERT_CONTEXT context = ::CertFindCertificateInStore( hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&cert_info, NULL); + PCCERT_CONTEXT context = ::CertFindCertificateInStore(hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&cert_info, NULL); if (!context) { - throw wstring( TEXT("Certificate context: ")) + GetLastErrorAsString(GetLastError()); + throw wstring(TEXT("Certificate context: ")) + GetLastErrorAsString(GetLastError()); } // Getting the full subject @@ -192,51 +237,53 @@ bool VerifySignedLibrary(const wstring& filepath, // Getting key_id DWORD key_id_sze = 0; - if (!::CertGetCertificateContextProperty( context, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &key_id_sze)) + if (!::CertGetCertificateContextProperty(context, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &key_id_sze)) { - throw wstring( TEXT("x509 property not found")) + GetLastErrorAsString(GetLastError()); + throw wstring(TEXT("x509 property not found")) + GetLastErrorAsString(GetLastError()); } - std::unique_ptr key_id_buff( new BYTE[key_id_sze] ); - if (!::CertGetCertificateContextProperty( context, CERT_KEY_IDENTIFIER_PROP_ID, key_id_buff.get(), &key_id_sze)) + std::unique_ptr key_id_buff(new BYTE[key_id_sze]); + if (!::CertGetCertificateContextProperty(context, CERT_KEY_IDENTIFIER_PROP_ID, key_id_buff.get(), &key_id_sze)) { - throw wstring( TEXT("Getting certificate property problem.")) + GetLastErrorAsString(GetLastError()); + throw wstring(TEXT("Getting certificate property problem.")) + GetLastErrorAsString(GetLastError()); } wstringstream ss; for (unsigned i = 0; i < key_id_sze; i++) { - ss << std::uppercase << std::setfill(TCHAR('0')) << std::setw(2) << std::hex - << key_id_buff[i]; + ss << std::uppercase << std::setfill(TCHAR('0')) << std::setw(2) << std::hex + << key_id_buff[i]; } key_id_hex = ss.str(); wstring dbg = key_id_hex + TEXT("\n"); - OutputDebugString( dbg.c_str() ); + OutputDebugString(dbg.c_str()); // Getting the display name - auto sze = ::CertGetNameString( context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); + auto sze = ::CertGetNameString(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); if (sze <= 1) { - throw wstring( TEXT("Getting data size problem.")) + GetLastErrorAsString(GetLastError()); + throw wstring(TEXT("Getting data size problem.")) + GetLastErrorAsString(GetLastError()); } - + // Get display name. - std::unique_ptr display_name_buffer( new TCHAR[sze] ); - if (::CertGetNameString( context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, display_name_buffer.get(), sze) <= 1) + std::unique_ptr display_name_buffer(new TCHAR[sze]); + if (::CertGetNameString(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, display_name_buffer.get(), sze) <= 1) { - throw wstring( TEXT("Cannot get certificate info.")) + GetLastErrorAsString(GetLastError()); + throw wstring(TEXT("Cannot get certificate info.")) + GetLastErrorAsString(GetLastError()); } display_name = display_name_buffer.get(); - } catch (const wstring& s) { - if (displayErrorMessage) + } + catch (const wstring& s) { + if (module2check == nm_scilexer) ::MessageBox(NULL, s.c_str(), TEXT("DLL signature verification failed"), MB_ICONERROR); OutputDebugString(TEXT("VerifyLibrary: error while getting certificate informations\n")); status = false; - } catch (...) { + } + catch (...) { // Unknown error OutputDebugString(TEXT("VerifyLibrary: error while getting certificate informations\n")); - if (displayErrorMessage) + if (module2check == nm_scilexer) { wstring errMsg(TEXT("Unknown exception occurred. ")); errMsg += GetLastErrorAsString(GetLastError()); @@ -248,31 +295,29 @@ bool VerifySignedLibrary(const wstring& filepath, // // fields verifications - if status is true, and string to compare (from the parameter) is not empty, then do compare // - if ( status && !cert_display_name.empty() && cert_display_name != display_name ) + if (status && _signer_display_name != display_name) { status = false; OutputDebugString(TEXT("VerifyLibrary: Invalid certificate display name\n")); } - if ( status && !cert_subject.empty() && cert_subject != subject) + if (status && _signer_subject != subject) { status = false; OutputDebugString(TEXT("VerifyLibrary: Invalid certificate subject\n")); } - if ( status && !cert_key_id_hex.empty() && cert_key_id_hex != key_id_hex ) + if (status && _signer_key_id != key_id_hex) { status = false; OutputDebugString(TEXT("VerifyLibrary: Invalid certificate key id\n")); } // Clean up. - + if (hStore != NULL) CertCloseStore(hStore, 0); - if (hMsg != NULL) CryptMsgClose(hMsg); + if (hMsg != NULL) CryptMsgClose(hMsg); if (pSignerInfo != NULL) LocalFree(pSignerInfo); return status; } - -#undef VerifySignedLibrary_DISABLE_REVOCATION_CHECK diff --git a/PowerEditor/src/MISC/Common/verifySignedfile.h b/PowerEditor/src/MISC/Common/verifySignedfile.h index 74db47ee..5f1b9c8e 100644 --- a/PowerEditor/src/MISC/Common/verifySignedfile.h +++ b/PowerEditor/src/MISC/Common/verifySignedfile.h @@ -26,9 +26,6 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -#pragma once - - //#define VerifySignedLibrary_DISABLE_REVOCATION_CHECK "Dont check certificat revocation" /* @@ -57,17 +54,36 @@ * state of the certificates will *not* be checked. * */ +#pragma once #include +#include -#define NPP_COMPONENT_SIGNER_DISPLAY_NAME TEXT("Notepad++") -#define NPP_COMPONENT_SIGNER_SUBJECT TEXT("C=FR, S=Ile-de-France, L=Saint Cloud, O=\"Notepad++\", CN=\"Notepad++\"") -#define NPP_COMPONENT_SIGNER_KEY_ID TEXT("42C4C5846BB675C74E2B2C90C69AB44366401093") +enum SecurityMode { sm_certif = 0, sm_sha256 = 1 }; +enum NppModule { nm_scilexer = 0, nm_gup = 1, nm_pluginList = 2 }; + +class SecurityGard final +{ +public: + SecurityGard(); + bool checkModule(std::wstring filePath, NppModule module2check); + +private: + // SHA256 + static SecurityMode _securityMode; + std::vector _scilexerSha256; + std::vector _gupSha256; + std::vector _pluginListSha256; + + bool checkSha256(std::wstring filePath, NppModule module2check); + + // Code signing certificate + std::wstring _signer_display_name = TEXT("Notepad++"); + std::wstring _signer_subject = TEXT("C=FR, S=Ile-de-France, L=Saint Cloud, O=\"Notepad++\", CN=\"Notepad++\""); + std::wstring _signer_key_id = TEXT("42C4C5846BB675C74E2B2C90C69AB44366401093"); + bool _doCheckRevocation = false; + bool _doCheckChainOfTrust = false; + + bool verifySignedLibrary(const std::wstring& filepath, NppModule module2check); +}; -bool VerifySignedLibrary(const std::wstring& filepath, - const std::wstring& key_id_hex, - const std::wstring& cert_subject, - const std::wstring& display_name, - bool doCheckRevocation, - bool doCheckChainOfTrust, - bool displayErrorMessage = true); diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp index fc33be7e..6165a0ce 100644 --- a/PowerEditor/src/NppCommands.cpp +++ b/PowerEditor/src/NppCommands.cpp @@ -2823,7 +2823,8 @@ void Notepad_plus::command(int id) bool isCertifVerified = true; #else //RELEASE // check the signature on updater - bool isCertifVerified = VerifySignedLibrary(updaterFullPath.c_str(), NPP_COMPONENT_SIGNER_KEY_ID, NPP_COMPONENT_SIGNER_SUBJECT, NPP_COMPONENT_SIGNER_DISPLAY_NAME, false, false, false); + SecurityGard securityGard; + bool isCertifVerified = securityGard.checkModule(updaterFullPath, nm_gup); #endif if (isCertifVerified) { diff --git a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp index e32c4024..5a5e15c1 100644 --- a/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp +++ b/PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp @@ -206,12 +206,13 @@ HMODULE loadSciLexerDll() // This is helpful for developers to skip signature checking // while analyzing issue or modifying the lexer dll #ifndef _DEBUG - bool isOK = VerifySignedLibrary(sciLexerPath, NPP_COMPONENT_SIGNER_KEY_ID, NPP_COMPONENT_SIGNER_SUBJECT, NPP_COMPONENT_SIGNER_DISPLAY_NAME, false, false); + SecurityGard securityGard; + bool isOK = securityGard.checkModule(sciLexerPath, nm_scilexer); if (!isOK) { ::MessageBox(NULL, - TEXT("Authenticode check failed: signature or signing certificate are not recognized"), + TEXT("Authenticode check failed:\rsigning certificate or hash is not recognized"), TEXT("Library verification failed"), MB_OK | MB_ICONERROR); return nullptr; diff --git a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp index c0d386cd..d0868c1b 100644 --- a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp +++ b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp @@ -714,11 +714,13 @@ bool PluginsAdminDlg::isValide() // check the signature on default location : %APPDATA%\Notepad++\plugins\config\pl\nppPluginList.dll or NPP_INST_DIR\plugins\config\pl\nppPluginList.dll - bool isOK = VerifySignedLibrary(_pluginListFullPath.c_str(), NPP_COMPONENT_SIGNER_KEY_ID, NPP_COMPONENT_SIGNER_SUBJECT, NPP_COMPONENT_SIGNER_DISPLAY_NAME, false, false, false); + SecurityGard securityGard; + bool isOK = securityGard.checkModule(_pluginListFullPath, nm_pluginList); + if (!isOK) return isOK; - isOK = VerifySignedLibrary(_updaterFullPath.c_str(), NPP_COMPONENT_SIGNER_KEY_ID, NPP_COMPONENT_SIGNER_SUBJECT, NPP_COMPONENT_SIGNER_DISPLAY_NAME, false, false, false); + isOK = securityGard.checkModule(_updaterFullPath, nm_gup); return isOK; #endif } diff --git a/PowerEditor/src/winmain.cpp b/PowerEditor/src/winmain.cpp index 65496927..f8965c71 100644 --- a/PowerEditor/src/winmain.cpp +++ b/PowerEditor/src/winmain.cpp @@ -494,7 +494,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) winVer ver = pNppParameters->getWinVersion(); bool isGtXP = ver > WV_XP; - bool isSignatureOK = VerifySignedLibrary(updaterFullPath.c_str(), NPP_COMPONENT_SIGNER_KEY_ID, NPP_COMPONENT_SIGNER_SUBJECT, NPP_COMPONENT_SIGNER_DISPLAY_NAME, false, false, false); + SecurityGard securityGard; + bool isSignatureOK = securityGard.checkModule(updaterFullPath, nm_gup); if (TheFirstOne && isUpExist && doUpdate && isGtXP && isSignatureOK) {