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;
@ -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<BYTE[]> 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<BYTE[]> 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<TCHAR[]> 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<TCHAR[]> 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

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