diff --git a/PluginDefinition.cpp b/PluginDefinition.cpp index 12f2112..d290463 100755 --- a/PluginDefinition.cpp +++ b/PluginDefinition.cpp @@ -18,6 +18,24 @@ #include "PluginDefinition.h" #include "menuCmdID.h" +// +// Globals +// +HINSTANCE hInstance = NULL; +unsigned char cryptkey[MAX_CRYPT_KEY]; + +// +// Fwd decl +// +void EncryptDoc() { CryptDoc(CryptAction::Encrypt); } +void DecryptDoc() { CryptDoc(CryptAction::Decrypt); } +void EncryptSelection() { CryptSelection(CryptAction::Encrypt); } +void DecryptSelection() { CryptSelection(CryptAction::Decrypt); } +LRESULT CALLBACK DlgProcCryptKey(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +void StrCrypt(unsigned char *buf1, size_t buf1len, unsigned char *buf2, size_t buf2len, unsigned char *key, CryptAction action); +std::wstring widen(const std::string& str); +std::string narrow(const std::wstring& str); + // // The plugin data that Notepad++ needs // @@ -30,9 +48,10 @@ NppData nppData; // // Initialize your plugin data here -// It will be called while plugin loading -void pluginInit(HANDLE /*hModule*/) +// It will be called while plugin loading +void pluginInit(HANDLE hModule) { + hInstance = (HINSTANCE)hModule; } // @@ -58,8 +77,11 @@ void commandMenuInit() // ShortcutKey *shortcut, // optional. Define a shortcut to trigger this command // bool check0nInit // optional. Make this menu item be checked visually // ); - setCommand(0, TEXT("Hello Notepad++"), hello, NULL, false); - setCommand(1, TEXT("Hello (with dialog)"), helloDlg, NULL, false); + setCommand(0, TEXT("Encrypt Document"), EncryptDoc, NULL, false); + setCommand(1, TEXT("Decrypt Document"), DecryptDoc, NULL, false); + setCommand(2, TEXT("Encrypt Selected Text"), EncryptSelection, NULL, false); + setCommand(3, TEXT("Decrypt Selected Text"), DecryptSelection, NULL, false); + setCommand(4, TEXT("About SecurePad"), AboutDlg, NULL, false); } // @@ -67,14 +89,14 @@ void commandMenuInit() // void commandMenuCleanUp() { - // Don't forget to deallocate your shortcut here + // Don't forget to deallocate your shortcut here } // // This function help you to initialize your plugin commands // -bool setCommand(size_t index, TCHAR *cmdName, PFUNCPLUGINCMD pFunc, ShortcutKey *sk, bool check0nInit) +bool setCommand(size_t index, TCHAR *cmdName, PFUNCPLUGINCMD pFunc, ShortcutKey *sk, bool check0nInit) { if (index >= nbFunc) return false; @@ -93,24 +115,323 @@ bool setCommand(size_t index, TCHAR *cmdName, PFUNCPLUGINCMD pFunc, ShortcutKey //----------------------------------------------// //-- STEP 4. DEFINE YOUR ASSOCIATED FUNCTIONS --// //----------------------------------------------// -void hello() +void CryptDoc(CryptAction action) +{ + int currentEdit = -1; + HWND hCurrentEditView = NULL; + size_t textLength = 0L, textLengthOut = 0L; + TCHAR *textBuf = NULL, *textBufOut = NULL; + + // Prompt for crypt key + INT_PTR ret = DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLGKEY), nppData._nppHandle, reinterpret_cast(DlgProcCryptKey)); + + if(ret == 0) + { + return; + } + + // Get edit view handle + SendMessage(nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, (LPARAM)¤tEdit); + if(currentEdit == 0) hCurrentEditView = nppData._scintillaMainHandle; + else hCurrentEditView = nppData._scintillaSecondHandle; + + // Get document text length + textLength = (ULONG)SendMessage(hCurrentEditView, SCI_GETTEXTLENGTH, 0, 0); + + if(textLength == 0) + { + MessageBox(nppData._nppHandle, TEXT("The document is empty!"), TEXT("Error"), MB_OK); + return; + } + + textLength++; + textLengthOut = textLength; + + if(action == CryptAction::Encrypt) textLengthOut *= 2; + + // Create input buffer (round up to nearest 8 multiplier) + while(textLength % 8 != 0)textLength++; + textBuf = (TCHAR*)VirtualAlloc(NULL, textLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + // Create output buffer (round up to nearest 8 multiplier) + while(textLengthOut % 8 != 0)textLengthOut++; + textBufOut = (TCHAR*)VirtualAlloc(NULL, textLengthOut, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + // Get text + SendMessage(hCurrentEditView, SCI_GETTEXT, textLength, (LPARAM)textBuf); + + // Crypt the text + if(action == CryptAction::Encrypt) + StrCrypt((unsigned char*)textBuf, textLength, (unsigned char*)textBufOut, textLengthOut, (unsigned char*)cryptkey, CryptAction::Encrypt); + else + StrCrypt((unsigned char*)textBuf, textLength, (unsigned char*)textBufOut, textLengthOut, (unsigned char*)cryptkey, CryptAction::Decrypt); + + // Output to view + SendMessage(hCurrentEditView, SCI_SETTEXT, 0, (LPARAM)textBufOut); + + // Cleanup + if(textBuf) + { + VirtualFree(textBuf, 0, MEM_RELEASE); + } + if (textBufOut) + { + VirtualFree(textBufOut, 0, MEM_RELEASE); + } +} + +void CryptSelection(CryptAction action) { - // Open a new document - ::SendMessage(nppData._nppHandle, NPPM_MENUCOMMAND, 0, IDM_FILE_NEW); + int currentEdit = -1; + HWND hCurrentEditView = NULL; + size_t textLength = 0L, textLengthOut = 0L; + TCHAR *textBuf = NULL, *textBufOut = NULL; + + // Prompt for crypt key + INT_PTR ret = DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLGKEY), nppData._nppHandle, reinterpret_cast(DlgProcCryptKey)); + + if(ret == 0) + { + return; + } + + // Get edit view handle + SendMessage(nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, (LPARAM)¤tEdit); + if(currentEdit == 0) hCurrentEditView = nppData._scintillaMainHandle; + else hCurrentEditView = nppData._scintillaSecondHandle; + + // Get selected text length + Sci_PositionCR selectStart = SendMessage(hCurrentEditView, SCI_GETSELECTIONSTART, 0, 0); + Sci_PositionCR selectEnd = SendMessage(hCurrentEditView, SCI_GETSELECTIONEND, 0, 0); - // Get the current scintilla - int which = -1; - ::SendMessage(nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, (LPARAM)&which); - if (which == -1) + if(selectEnd == 0 || (selectEnd - selectStart) == 0) + { + MessageBox(nppData._nppHandle, TEXT("No text was selected!"), TEXT("Error"), MB_OK); return; - HWND curScintilla = (which == 0)?nppData._scintillaMainHandle:nppData._scintillaSecondHandle; + } + + textLength = (selectEnd - selectStart); + textLength++; + textLengthOut = textLength; + if(action == CryptAction::Encrypt) textLengthOut *= 2; + + // Create input buffer (round up to nearest 8 multiplier) + while(textLength % 8 != 0)textLength++; + textBuf = (TCHAR*)VirtualAlloc(NULL, textLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + // Create output buffer (round up to nearest 8 multiplier) + while(textLengthOut % 8 != 0)textLengthOut++; + textBufOut = (TCHAR*)VirtualAlloc(NULL, textLengthOut, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + // Get text range + struct Sci_TextRange tr = {{selectStart, selectEnd}, reinterpret_cast(textBuf)}; + SendMessage(hCurrentEditView, SCI_GETTEXTRANGE, 0, (LPARAM)&tr); + + // Crypt the text + if(action == CryptAction::Encrypt) + StrCrypt((unsigned char*)textBuf, textLength, (unsigned char*)textBufOut, textLengthOut, (unsigned char*)cryptkey, CryptAction::Encrypt); + else + StrCrypt((unsigned char*)textBuf, textLength, (unsigned char*)textBufOut, textLengthOut, (unsigned char*)cryptkey, CryptAction::Decrypt); + + // Output to view + SendMessage(hCurrentEditView, SCI_REPLACESEL, 0, (LPARAM)textBufOut); + + // Cleanup + if (textBuf) + { + VirtualFree(textBuf, 0, MEM_RELEASE); + } + if (textBufOut) + { + VirtualFree(textBufOut, 0, MEM_RELEASE); + } +} + +void AboutDlg() +{ + ::MessageBox(NULL, TEXT("SecurePad can be used to securely encrypt plaintext documents with a key of your choice. Be careful, once encrypted you will only be able to decrypt with the key you used!\r\n\r\nAny questions please visit www.dominictobias.com."), TEXT("SecurePad v2.4"), MB_OK); +} + +// =============================================== +// Dialog for key entry +// =============================================== + +LRESULT CALLBACK DlgProcCryptKey(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM /*lParam*/) +{ + switch(msg) + { + case WM_DESTROY: + case WM_CLOSE: + { + EndDialog(hWndDlg, 0); + return TRUE; + } + + case WM_INITDIALOG: + { + SetFocus(GetDlgItem(hWndDlg, IDC_EDIT1)); + break; + } + + case WM_COMMAND: + { + if(LOWORD(wParam) == IDOK) + { + TCHAR box1[MAX_CRYPT_KEY] = { 0 }; + TCHAR box2[MAX_CRYPT_KEY] = { 0 }; + LRESULT keyLen = 0; + + SendMessage(GetDlgItem(hWndDlg, IDC_EDIT1), WM_GETTEXT, sizeof(box1), (LPARAM)box1); + SendMessage(GetDlgItem(hWndDlg, IDC_EDIT2), WM_GETTEXT, sizeof(box2), (LPARAM)box2); + + if(_tcslen(box1) == 0 || _tcslen(box2) == 0) + { + MessageBox(nppData._nppHandle, TEXT("Both fields must be filled in!"), TEXT("Please try again"), MB_OK); + break; + } + + if(_tcscmp(box1, box2)) + { + MessageBox(nppData._nppHandle, TEXT("The keys did not match!"), TEXT("Please try again"), MB_OK); + break; + } + + keyLen = SendMessage(GetDlgItem(hWndDlg, IDC_EDIT1), WM_GETTEXTLENGTH, 0, 0); + + if(keyLen > MAX_CRYPT_KEY) + { + TCHAR buf[128]; + _stprintf(buf, TEXT("The key was too long! (key=%Id, max=%d)\0"), keyLen, MAX_CRYPT_KEY); + MessageBox(nppData._nppHandle, buf, TEXT("Please try again"), MB_OK); + break; + } + + if(keyLen < MIN_CRYPT_KEY) + { + TCHAR buf[128]; + _stprintf(buf, TEXT("The key was too short! (key=%Id, min=%d)\0"), keyLen, MIN_CRYPT_KEY); + MessageBox(nppData._nppHandle, buf, TEXT("Please try again"), MB_OK); + break; + } + + // Convert the key to narrow ANSII format + memset(&cryptkey, 0, sizeof(cryptkey)); + std::string narrowKey = narrow(box1); + memcpy(cryptkey, narrowKey.c_str(), narrowKey.size() > MAX_CRYPT_KEY ? MAX_CRYPT_KEY : narrowKey.size()); + + EndDialog(hWndDlg, 1); + } + + if(LOWORD(wParam) == IDCANCEL) + { + EndDialog(hWndDlg, 0); + return TRUE; + } + + break; + } + } + + return FALSE; +} - // Say hello now : - // Scintilla control has no Unicode mode, so we use (char *) here - ::SendMessage(curScintilla, SCI_SETTEXT, 0, (LPARAM)"Hello, Notepad++!"); +// =============================================== +// Blowfish cypher +// =============================================== + +//Function to convert unsigned char to string of length 2 +void Char2Hex(const unsigned char ch, char* szHex) +{ + unsigned char byte[2]{}; + byte[0] = ch/16; + byte[1] = ch%16; + for(int i=0; i<2; i++) + { + if(byte[i] >= 0 && byte[i] <= 9) + szHex[i] = '0' + byte[i]; + else + szHex[i] = 'A' + byte[i] - 10; + } + szHex[2] = 0; +} + +//Function to convert string of length 2 to unsigned char +void Hex2Char(const char* szHex, unsigned char& rch) +{ + rch = 0; + for(int i=0; i<2; i++) + { + if(*(szHex + i) >='0' && *(szHex + i) <= '9') + rch = (rch << 4) + (*(szHex + i) - '0'); + else if(*(szHex + i) >='A' && *(szHex + i) <= 'F') + rch = (rch << 4) + (*(szHex + i) - 'A' + 10); + else + break; + } } -void helloDlg() +//Function to convert string of unsigned chars to string of chars +void CharStr2HexStr(const unsigned char* pucCharStr, char* pszHexStr, size_t lSize) { - ::MessageBox(NULL, TEXT("Hello, Notepad++!"), TEXT("Notepad++ Plugin Template"), MB_OK); + char szHex[3]{}; + pszHexStr[0] = 0; + for(size_t i=0; iEncrypt(buf1, buf1, buf1len, CBlowFish::ECB); + CharStr2HexStr(buf1, (char*)buf2, buf1len); + } + + else if(action == CryptAction::Decrypt) + { + HexStr2CharStr((char*)buf1, buf2, buf2len/2); + BlowFish->Decrypt(buf2, buf2, buf2len, CBlowFish::ECB); + } + + delete BlowFish; +}; + +// =============================================== +// Helper funcs +// =============================================== + +std::wstring widen(const std::string& str) +{ + std::wostringstream wstm; + const std::ctype& ctfacet = + std::use_facet< std::ctype >(wstm.getloc()); + for( size_t i=0 ; i& ctfacet = + std::use_facet< std::ctype >(stm.getloc()); + for( size_t i=0 ; i +#include +#include +#include + +#include +#include +#include #include "PluginInterface.h" +#include "blowfish.h" +#include "resource1.h" + +constexpr auto MAX_CRYPT_KEY = 56; +constexpr auto MIN_CRYPT_KEY = 1; //-------------------------------------// //-- STEP 1. DEFINE YOUR PLUGIN NAME --// //-------------------------------------// // Here define your plugin name // -const TCHAR NPP_PLUGIN_NAME[] = TEXT("Notepad++ plugin template"); +const TCHAR NPP_PLUGIN_NAME[] = TEXT("SecurePad"); //-----------------------------------------------// //-- STEP 2. DEFINE YOUR PLUGIN COMMAND NUMBER --// @@ -36,7 +49,7 @@ const TCHAR NPP_PLUGIN_NAME[] = TEXT("Notepad++ plugin template"); // // Here define the number of your plugin commands // -const int nbFunc = 2; +const int nbFunc = 5; // @@ -62,7 +75,7 @@ void commandMenuInit(); void commandMenuCleanUp(); // -// Function which sets your command +// Function which sets your command // bool setCommand(size_t index, TCHAR *cmdName, PFUNCPLUGINCMD pFunc, ShortcutKey *sk = NULL, bool check0nInit = false); @@ -70,7 +83,13 @@ bool setCommand(size_t index, TCHAR *cmdName, PFUNCPLUGINCMD pFunc, ShortcutKey // // Your plugin command functions // -void hello(); -void helloDlg(); +enum class CryptAction { + Encrypt, + Decrypt +}; + +void CryptDoc(CryptAction action); +void CryptSelection(CryptAction action); +void AboutDlg(); #endif //PLUGINDEFINITION_H diff --git a/README.md b/README.md index c99592f..5ee92c5 100755 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ SecurePad uses a powerful and secure cryption algorithm (, see https://en.wikipe Changelog ========= +v2.4 +---- +Fixed issue #13, regression form PR #12, completely replacing functionality with just the npp template code +Adpated to size_t for documents greater 2GB + v2.3 ---- diff --git a/Resources.rc b/Resources.rc index d84c207..1408779 100755 --- a/Resources.rc +++ b/Resources.rc @@ -52,8 +52,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,3,0,0 - PRODUCTVERSION 2,3,0,0 + FILEVERSION 2,4,0,0 + PRODUCTVERSION 2,4,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -70,12 +70,12 @@ BEGIN BEGIN VALUE "CompanyName", "DreamInPixels, dreaminpixels.net" VALUE "FileDescription", "SecurePad can be used to securely encrypt plaintext documents with a key of your choice" - VALUE "FileVersion", "2.3.0.0" + VALUE "FileVersion", "2.4.0.0" VALUE "InternalName", "SecurePad.dll" VALUE "LegalCopyright", "Copyright (c) 2022 dreaminpixels.net" VALUE "OriginalFilename", "SecurePad.dll" VALUE "ProductName", "SecurePad" - VALUE "ProductVersion", "2.3.0.0" + VALUE "ProductVersion", "2.4.0.0" END END BLOCK "VarFileInfo" diff --git a/appveyor.yml b/appveyor.yml index 705c98d..728328d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.3.{build} +version: 2.4.{build} image: Visual Studio 2022