Add SHA256 checking of the authentity of used modules

In order to remove the code signing certificate dependency, SHA256 checking of the authentity of used modules (Scilexer.dll, GUP.exe and nppPluginList.dll) is added.
See the followling link for the detail information:
https://notepad-plus-plus.org/community/topic/17184/remove-code-signing-from-notepad
This commit is contained in:
Don HO 2019-02-27 10:14:36 +01:00
parent 1f5ba1803f
commit 3baef49079
6 changed files with 139 additions and 73 deletions

View File

@ -38,16 +38,61 @@
#include <iomanip> #include <iomanip>
#include "VerifySignedFile.h" #include "VerifySignedFile.h"
#include "Common.h" #include "Common.h"
#include "sha-256.h"
using namespace std; using namespace std;
bool VerifySignedLibrary(const wstring& filepath, SecurityMode SecurityGard::_securityMode = sm_sha256;
const wstring& cert_key_id_hex, //SecurityMode SecurityGard::_securityMode = sm_certif;
const wstring& cert_subject,
const wstring& cert_display_name, SecurityGard::SecurityGard()
bool doCheckRevocation, {
bool doCheckChainOfTrust, _scilexerSha256.push_back(TEXT("03c9177631d2b32de3d32c73a8841cf68fc2cb17f306825489dc3df98000db85")); // v3.5.6 32 bit
bool displayErrorMessage) _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<const uint8_t*>(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<std::wstring>* 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 display_name;
wstring key_id_hex; wstring key_id_hex;
@ -78,7 +123,7 @@ bool VerifySignedLibrary(const wstring& filepath,
winTEXTrust_data.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; // verify the whole certificate chain 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; winTEXTrust_data.fdwRevocationChecks = WTD_REVOKE_NONE;
OutputDebugString(TEXT("VerifyLibrary: certificate revocation checking is disabled\n")); 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 // Verify signature and cert-chain validity
GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2; GUID policy = WINTRUST_ACTION_GENERIC_VERIFY_V2;
@ -228,15 +273,17 @@ bool VerifySignedLibrary(const wstring& filepath,
} }
display_name = display_name_buffer.get(); 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); ::MessageBox(NULL, s.c_str(), TEXT("DLL signature verification failed"), MB_ICONERROR);
OutputDebugString(TEXT("VerifyLibrary: error while getting certificate informations\n")); OutputDebugString(TEXT("VerifyLibrary: error while getting certificate informations\n"));
status = false; status = false;
} catch (...) { }
catch (...) {
// Unknown error // Unknown error
OutputDebugString(TEXT("VerifyLibrary: error while getting certificate informations\n")); OutputDebugString(TEXT("VerifyLibrary: error while getting certificate informations\n"));
if (displayErrorMessage) if (module2check == nm_scilexer)
{ {
wstring errMsg(TEXT("Unknown exception occurred. ")); wstring errMsg(TEXT("Unknown exception occurred. "));
errMsg += GetLastErrorAsString(GetLastError()); errMsg += GetLastErrorAsString(GetLastError());
@ -248,19 +295,19 @@ bool VerifySignedLibrary(const wstring& filepath,
// //
// fields verifications - if status is true, and string to compare (from the parameter) is not empty, then do compare // 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; status = false;
OutputDebugString(TEXT("VerifyLibrary: Invalid certificate display name\n")); OutputDebugString(TEXT("VerifyLibrary: Invalid certificate display name\n"));
} }
if ( status && !cert_subject.empty() && cert_subject != subject) if (status && _signer_subject != subject)
{ {
status = false; status = false;
OutputDebugString(TEXT("VerifyLibrary: Invalid certificate subject\n")); 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; status = false;
OutputDebugString(TEXT("VerifyLibrary: Invalid certificate key id\n")); OutputDebugString(TEXT("VerifyLibrary: Invalid certificate key id\n"));
@ -274,5 +321,3 @@ bool VerifySignedLibrary(const wstring& filepath,
return status; return status;
} }
#undef VerifySignedLibrary_DISABLE_REVOCATION_CHECK

View File

@ -26,9 +26,6 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#pragma once
//#define VerifySignedLibrary_DISABLE_REVOCATION_CHECK "Dont check certificat revocation" //#define VerifySignedLibrary_DISABLE_REVOCATION_CHECK "Dont check certificat revocation"
/* /*
@ -57,17 +54,36 @@
* state of the certificates will *not* be checked. * state of the certificates will *not* be checked.
* *
*/ */
#pragma once
#include <string> #include <string>
#include <vector>
#define NPP_COMPONENT_SIGNER_DISPLAY_NAME TEXT("Notepad++") enum SecurityMode { sm_certif = 0, sm_sha256 = 1 };
#define NPP_COMPONENT_SIGNER_SUBJECT TEXT("C=FR, S=Ile-de-France, L=Saint Cloud, O=\"Notepad++\", CN=\"Notepad++\"") enum NppModule { nm_scilexer = 0, nm_gup = 1, nm_pluginList = 2 };
#define NPP_COMPONENT_SIGNER_KEY_ID TEXT("42C4C5846BB675C74E2B2C90C69AB44366401093")
class SecurityGard final
{
public:
SecurityGard();
bool checkModule(std::wstring filePath, NppModule module2check);
private:
// SHA256
static SecurityMode _securityMode;
std::vector<std::wstring> _scilexerSha256;
std::vector<std::wstring> _gupSha256;
std::vector<std::wstring> _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);

View File

@ -2823,7 +2823,8 @@ void Notepad_plus::command(int id)
bool isCertifVerified = true; bool isCertifVerified = true;
#else //RELEASE #else //RELEASE
// check the signature on updater // 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 #endif
if (isCertifVerified) if (isCertifVerified)
{ {

View File

@ -206,12 +206,13 @@ HMODULE loadSciLexerDll()
// This is helpful for developers to skip signature checking // This is helpful for developers to skip signature checking
// while analyzing issue or modifying the lexer dll // while analyzing issue or modifying the lexer dll
#ifndef _DEBUG #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) if (!isOK)
{ {
::MessageBox(NULL, ::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"), TEXT("Library verification failed"),
MB_OK | MB_ICONERROR); MB_OK | MB_ICONERROR);
return nullptr; return nullptr;

View File

@ -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 // 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) if (!isOK)
return 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; return isOK;
#endif #endif
} }

View File

@ -494,7 +494,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
winVer ver = pNppParameters->getWinVersion(); winVer ver = pNppParameters->getWinVersion();
bool isGtXP = ver > WV_XP; 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) if (TheFirstOne && isUpExist && doUpdate && isGtXP && isSignatureOK)
{ {