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 "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<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 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.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;
@ -228,15 +273,17 @@ bool VerifySignedLibrary(const wstring& filepath,
}
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,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
//
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"));
@ -274,5 +321,3 @@ bool VerifySignedLibrary(const wstring& filepath,
return status;
}
#undef VerifySignedLibrary_DISABLE_REVOCATION_CHECK

View File

@ -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 <string>
#include <vector>
#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<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;
#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)
{

View File

@ -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;

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
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
}

View File

@ -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)
{