ProgerXP / Notepad2e

Word highlighting, simultaneous editing, split views, math evaluation, un/grep, comment reformatting, UAC elevation, complete regexps (PCRE), Lua lexers, DPI awareness and more (XP+)
Other
375 stars 52 forks source link

Add encryption #200

Open nhantrn opened 5 years ago

nhantrn commented 5 years ago

Can you add the encryption module from https://github.com/RaiKoHoff/notepad2-mod-crypto ? It's based on XhmikosR's build, but it's been archived.

ProgerXP commented 5 years ago

Are you really using a text editor with built-in encryption?

Why not use some other program that allows working with encrypted file containers? It's much more reliable and flexible and doesn't involve temporary files (temporary files are the weak spot of archive-based encryption like 7-zip, that's true).

nhantrn commented 5 years ago

I'm using it as secondary protection and for non critical info. Just something quick and dirty for around the office use.

ProgerXP commented 5 years ago

I see what you mean. It's possible we may implement it but much simpler (incompatible with that mod). I see no reason to use any libraries for software encryption and/or hashing, Windows CryptoAPI is more than enough for such use case.

Anyway, we have to close some other tasks first.

ProgerXP commented 5 years ago

@cshnik

General notes

Encrypted data formats

There are 3 flavours of the same format.

Format 1 is OpenSSL's verbatim format: magic string = Salted__, 8-byte salt, encrypted data.

Format 2 is format 1 without the magic string (so there's no identification and the data looks like random noise).

Format 3 is format 1 prepended with key slot(s). Each slot is a format 2 data - a master passphrase (mpassphrase) encrypted with different user-supplied passphrases. Each slot is 40 bytes long (8 for salt, 32 for AES block size). Data of format 1 (which follows the key slots) is encrypted with mpassphrase.

Number of slots may be different but never more than MaxKeySlots. This number is calculated by dividing offset of Salted__ (format 1 start) by 40.

The main data is encrypted with a common mpassphrase and master salt (msalt). See openssl enc examples in the File command section.

 _______________ format 3 ______________
/                                       \
[slot 1] [slot 2] ... Salted__ msalt data
40 B     40 B         8 B      8 B   *
format 2 format 2     \____ format 1 ___/

Format detection:


Therefore Notepad 2e must have 2 internal fundamental encryption functions: one for format 2 encryption, another - for format 2 decryption, both using CryptoAPI. Other 2 formats' functions are based on these 2 functions.

You can use this openssl enc command to generate encrypted data in format 1 (result is 32-byte long, note that each time it's different because of the random salt):

echo -n hello | openssl aes-256-cbc -md md5 -pass pass:sEcReT | xxd

To decrypt, use the -d switch:

openssl aes-256-cbc -md md5 -pass pass:sEcReT -d -in encrypted.file

With regular encryption, AES needs an IV and a key. openssl enc derives both from the salt and the password - see EVP_BytesToKey() in OpenSSL's sources.

AES-256's key length is 32 bytes, IV length is 16 bytes.

New commands

New internal variables (proposed)

New internal constant

New dialog

There is a single new dialog - prompt for the password(s). It's shown on secure desktop.

Besides OK/Cancel buttons it contains N inputs (N = MaxKeySlots): Passphrase &N: (e.g. Passphrase &2:).

Encode commands

Both work on the selection or, if it's blank, on the entire buffer. If the operation was successful, the selection (buffer) is replaced by the new content, and re-selected (if there was no selection, entire buffer is replaced and selected). If failed, selection is unchanged and user sees an error message.

Encrypt displays the prompt and produces a data stream (cipher-text) in format 1 (because it's binary, user should enable Binary-Safe Save #170).

Decrypt displays the prompt and produces another data stream (plain-text), or possibly junk if the passphrase specified were incorrect (there's no verification except padding).

File command

Works on the entire buffer ignoring current selection, and resets selection to the start of the buffer when called.

EncryptedFileIO indicates if the buffer should be silently encrypted when saving (FileIO).

When File > Encrypted is called and EncryptedFileIO is true - just set EncryptedFileIO to false. (Reload/F5 would reset EncryptedFileIO and display the garbled encrypted data.)

When File > Encrypted is called and EncryptedFileIO is false - check if the buffer is in format 1 or 3 (not 2).

If it is format 1, display the prompt, decrypt the entire buffer (replace window text), set CachedKeySlots to null and set EncryptedFileIO to true.

If it is format 3, display the prompt, iterate over all key slots (each in format 2), decrypt a slot (mpassphrase), decrypt the main data (format 1), replace the window's text with decrypted buffer, set CachedKeySlots (part before format 3, Salted__) and set EncryptedFileIO to true.

if neither 1 nor 3, display the prompt and set EncryptedFileIO to true. Do not actually encrypt/change the buffer's contents/window text.

Intended usage scenario: user starts with cipher-text or plain-text, calls File > Encrypt to mark it as "encrypt-on-save" and possibly decrypt it; then continues to edit the same plain-text and when he saves it, it gets encrypted internally thanks to the FileIO change (below).

Note: if Encrypt/Decrypt prompts are cancelled or if a encryption/decryption error occurs, this is treated as command failure and global variables' and window's state is unchanged.

FileIO change

On writing to a file (FileIO), if EncryptedFileIO is true the buffer is silently encrypted as format 1 or 3 and then written (window's text and global variables are left as is).

Notes:

openssl enc examples

In the end, when using File > Encrypted, it should be possible to decrypt openssl enc streams as well as produce streams recognized by openssl enc -d.

Example of producing a format 3 data using standard tools:

dd if=/dev/urandom of=mpassphrase bs=20 count=1
echo -n hello | openssl aes-256-cbc -md md5 -pass fd:3 3<mpassphrase -out format-1
openssl aes-256-cbc -md md5 -pass pass:sEcReT -in mpassphrase | xxd -p -seek 8 | xxd -r -p >key-slot-1
openssl aes-256-cbc -md md5 -pass pass:pRiNcEsS -in mpassphrase | xxd -p -seek 8 | xxd -r -p >key-slot-2
cat key-slot-* format-1 >format-3

To decode a format-3 given any one of the key slots' passphrases (no mpassphrase):

xxd -p -seek 0  -len 40 format-3 | xxd -r -p >key-slot-1
xxd -p -seek 40 -len 40 format-3 | xxd -r -p >key-slot-2
xxd -p -seek 80 format-3 | xxd -r -p >format-1
echo -n Salted__ | cat - key-slot-2 | openssl aes-256-cbc -d -md md5 -pass pass:pRiNcEsS -out mpassphrase
openssl aes-256-cbc -d -md md5 -pass fd:3 3<mpassphrase -in format-1 

Unit tests

As part of this task, write unit tests relying on openssl to validate encryption/decryption results. Let's assume some environment variable like NP2E_OPENSSL pointing to openssl.exe is set on the test system.

clach04 commented 5 months ago

echo -n hello | openssl aes-256-cbc -md md5 -pass pass:sEcReT | xxd

I suspect if you try this today you will get warnings (along the lines of https://unix.stackexchange.com/questions/507131/openssl-1-1-1b-warning-using-iter-or-pbkdf2-would-be-better-while-decrypting) with suggestions to use the "new" KDF.

However using the new KDF will result in a number of iterations significantly lower than is recommended.

Encryption support would be great, but not this implementation. Doe you have support (or plans) for buffer/filters where the editor reads stdout from a subprocess (e.g. popen())? Vim and emacs make use of this to shell out to an external exe to handle this. This does require the editor NOT perform file up and instead read/write pipes to the subprocess.

clach04 commented 5 months ago

The ccrypt section in https://vim.fandom.com/wiki/Encryption has a good example (along with gpg)

ProgerXP commented 5 months ago

Do you have support (or plans) for buffer/filters where the editor reads stdout from a subprocess (e.g. popen())? Vim and emacs make use of this to shell out to an external exe to handle this.

I suppose we can buffer the entire pipe or stdin in memory, that's what Notepad2 already does when reading regular files. We don't need input or output to be seekable. But the saving operation (if it's directed to a pipe or stdout) will be essentially one-off, after which Notepad 2e will quit, right?

This will require some work on UI (disable Revert, etc.) as well as a new option for specifying the output (disabling Save As, etc.):

openssl enc -d ... | Notepad2e.exe - - | openssl enc ...
clach04 commented 5 months ago

@ProgerXP I've not seen any editor support that construct. As you said, it would really be limited to a single read and write and be very limiting. Usually piping into stdin of an editor is considered a one time read only operation.

I was suggesting support in the editor to internally call an OS routine like popen() https://man7.org/linux/man-pages/man3/popen.3.html

Take a look at the vim example link above for usage. Any read or write goes through custom config.

https://github.com/clach04/puren_tonbo/blob/main/integrations/scite/pt_scite.lua includes lua code that works with Scite (another scintilla based editor that uses lua for user config/scripting) when opening files with specific file extensions instead of regular fopen() calls.

ProgerXP commented 5 months ago

I see your idea. I'll note it but I can't promise any implementation date. It's not a priority at the moment.