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:
parent
1f5ba1803f
commit
3baef49079
@ -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
|
|
||||||
|
@ -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);
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user