mirror of
https://github.com/rizonesoft/Notepad3.git
synced 2026-06-11 21:03:05 +08:00
275 lines
10 KiB
C
275 lines
10 KiB
C
// encoding: UTF-8
|
|
/*
|
|
*
|
|
* Distributed under the terms of the GNU General Public License,
|
|
* see License.txt for details.
|
|
*
|
|
* Author: Dave Dyer ddyer@real-me.net Oct/2005
|
|
*
|
|
* bug fix 5/2013 by Nayuki Minase <nayuki@eigenstate.org>
|
|
* encrypted file was corrupted for source files of 16-31 bytes.
|
|
* also http://nayuki.eigenstate.org/page/notepadcrypt-format-decryptor-java
|
|
*/
|
|
#include "targetver.h"
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
|
|
#include "rijndael-api-fst.h"
|
|
#include "crypto.h"
|
|
|
|
#define BLOCKSIZE (64 * 1024) // the optimal buffer size for sequential I/O on Windows NT/2k/XP
|
|
typedef struct AES_file
|
|
{
|
|
FILE *file;
|
|
AES_cipherInstance cipher;
|
|
AES_keyInstance key;
|
|
BOOL encrypted;
|
|
BYTE buffer[BLOCKSIZE];
|
|
long bytesleft;
|
|
long buffer_index;
|
|
long buffer_end;
|
|
} AES_file;
|
|
|
|
|
|
void gen_iv(unsigned char *buf, int size)
|
|
{
|
|
while (--size >= 0) buf[size] = (unsigned char)size; //+= CM_random();
|
|
}
|
|
/* @func
|
|
open a file, possibly encrypted using notepad2 format, for reading and decryption.
|
|
@rdesc 0 for success
|
|
*/
|
|
long ROpen_AES
|
|
(char * name, //@parm the file to open
|
|
AES_file * fp, //@parm the <t AES_file> object to keep track of the open file
|
|
char *filekey, //@parm the file's passphrase, or an empty string, or NULL
|
|
char *masterkey//@parm the file's master passphrase, or an empty string, or NULL
|
|
)
|
|
{
|
|
FILE *file = NULL;
|
|
if (fopen_s(&file, name, "rb") != 0) { printf("File %s can't be opened\n", name); return(1); }
|
|
fp->file = file;
|
|
fp->buffer_index = 0;
|
|
fp->buffer_end = 0;
|
|
fp->bytesleft = 0;
|
|
fp->encrypted = FALSE;
|
|
// get the file length
|
|
fseek(file, 0, SEEK_END);
|
|
fp->bytesleft = ftell(file);
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
// read the maximum preable size, so we will have an even number of encrypted blocks
|
|
// left over if this is an encrypted file.
|
|
fp->buffer_end = (long)fread(fp->buffer, 1, MASTER_KEY_OFFSET, fp->file);
|
|
fp->bytesleft -= fp->buffer_end;
|
|
if (fp->buffer_end >= MASTER_KEY_OFFSET) {
|
|
unsigned long *lbuf = (unsigned long *)&fp->buffer;
|
|
BYTE binFileKey[KEY_BYTES];
|
|
BOOL hasFileKey = FALSE;
|
|
|
|
//possibly encrypted
|
|
if (lbuf[0] == PREAMBLE) {
|
|
switch (lbuf[1]) {
|
|
default:
|
|
printf("File %s is encrypted with an unsupported format: %lu", name, lbuf[1]);
|
|
fclose(file);
|
|
return(1);
|
|
case MASTERKEY_FORMAT:
|
|
// read the masterkey block
|
|
if (fread(fp->buffer + fp->buffer_end, 1, KEY_BYTES + AES_MAX_IV_SIZE, fp->file)
|
|
!= (KEY_BYTES + AES_MAX_IV_SIZE)) {
|
|
fclose(fp->file);
|
|
return(2); // short file
|
|
}
|
|
fp->buffer_index = fp->buffer_end;
|
|
fp->bytesleft -= (KEY_BYTES + AES_MAX_IV_SIZE);
|
|
|
|
if (masterkey && *masterkey) {
|
|
BYTE binMasterKey[KEY_BYTES];
|
|
AES_keygen(masterkey, binMasterKey);
|
|
AES_bin_setup(&fp->key, AES_DIR_DECRYPT, KEY_BYTES * 8, binMasterKey);
|
|
AES_bin_cipherInit(&fp->cipher, AES_MODE_CBC, &fp->buffer[MASTER_KEY_OFFSET]);
|
|
AES_blockDecrypt(&fp->cipher, &fp->key, &fp->buffer[MASTER_KEY_OFFSET + AES_MAX_IV_SIZE], sizeof(binFileKey), binFileKey);
|
|
hasFileKey = TRUE;
|
|
}
|
|
else
|
|
if (filekey && *filekey) {
|
|
AES_keygen(filekey, binFileKey);
|
|
fp->buffer_index = fp->buffer_end;
|
|
hasFileKey = TRUE;
|
|
}
|
|
break;
|
|
case FILEKEY_FORMAT:
|
|
if (filekey && *filekey) {
|
|
AES_keygen(filekey, binFileKey);
|
|
fp->buffer_index = fp->buffer_end;
|
|
hasFileKey = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
if (hasFileKey) {
|
|
fp->encrypted = TRUE;
|
|
AES_bin_setup(&fp->key, AES_DIR_DECRYPT, KEY_BYTES * 8, binFileKey);
|
|
AES_bin_cipherInit(&fp->cipher, AES_MODE_CBC, &fp->buffer[PREAMBLE_SIZE]);
|
|
return(0);
|
|
}
|
|
printf("File %s is encrypted, but no suitable passphrase is available",
|
|
name);
|
|
fclose(file);
|
|
return(3);
|
|
}
|
|
}
|
|
return(0); // file is too short to be encrypted
|
|
}
|
|
/* @func
|
|
encrypt infile to outfile, using filephrase to generate the key,
|
|
and optionally using masterphrase as the master key
|
|
*/
|
|
int encrypt(char *infile, char *outfile, char *filephrase, char *masterphrase)
|
|
{
|
|
int err = 0;
|
|
FILE *in = NULL;
|
|
if (fopen_s(&in, infile, "rb") != 0) { printf("input file %s can't be opened\1", infile); err++; }
|
|
else {
|
|
FILE *out = NULL;
|
|
if (fopen_s(&out, outfile, "wb") != 0) { printf("output file %s can't be opened\n", outfile); err++; }
|
|
else {
|
|
BYTE buffer[BLOCKSIZE];
|
|
unsigned long preamble[] = { PREAMBLE, FILEKEY_FORMAT };
|
|
BYTE iv[AES_MAX_IV_SIZE];
|
|
BYTE filekey[KEY_BYTES];
|
|
BOOL masterformat = masterphrase && *masterphrase;
|
|
AES_cipherInstance cipher;
|
|
AES_keyInstance key;
|
|
|
|
if (masterformat) { preamble[1] = MASTERKEY_FORMAT; }
|
|
|
|
gen_iv(iv, sizeof(iv)); // generate a random iv
|
|
AES_keygen(filephrase, filekey); // make key file passphrase
|
|
fwrite(preamble, 1, sizeof(preamble), out); // write the preamble
|
|
fwrite(iv, 1, sizeof(iv), out); // and the iv
|
|
|
|
AES_bin_setup(&key, AES_DIR_ENCRYPT, KEY_BYTES * 8, filekey); // prepare the encryption
|
|
AES_bin_cipherInit(&cipher, AES_MODE_CBC, iv);
|
|
|
|
if (masterformat) { // encrypt the file key with the masterkey and write it.
|
|
BYTE masteriv[AES_MAX_IV_SIZE];
|
|
BYTE masterkey[KEY_BYTES];
|
|
BYTE encfilekey[KEY_BYTES];
|
|
AES_cipherInstance mastercipher;
|
|
AES_keyInstance mkey;
|
|
|
|
AES_keygen(masterphrase, masterkey); // generate the master key
|
|
gen_iv(masteriv, sizeof(masteriv)); // and an iv for it
|
|
AES_bin_setup(&mkey, AES_DIR_ENCRYPT, KEY_BYTES * 8, masterkey);
|
|
AES_bin_cipherInit(&mastercipher, AES_MODE_CBC, masteriv);
|
|
// encrypt the file key using the master key
|
|
AES_blockEncrypt(&mastercipher, &mkey, filekey, sizeof(filekey), encfilekey);
|
|
fwrite(masteriv, 1, sizeof(masteriv), out);
|
|
fwrite(encfilekey, 1, sizeof(encfilekey), out);
|
|
}
|
|
|
|
// now encrypt and output the actual data
|
|
{
|
|
long bytesread = 0;
|
|
long bytesencrypted = 0;
|
|
do {
|
|
bytesread = (long)fread(buffer, 1, sizeof(buffer), in);
|
|
bytesencrypted = 0;
|
|
if (bytesread > 0) {
|
|
bytesencrypted = AES_blockEncrypt(&cipher, &key, buffer, bytesread, buffer);
|
|
fwrite(buffer, 1, bytesencrypted, out);
|
|
}
|
|
} while ((bytesread > 0) && (bytesencrypted == bytesread));
|
|
// pad the last block
|
|
bytesencrypted = AES_padEncrypt(&cipher, &key, buffer + bytesencrypted, (bytesread - bytesencrypted), buffer);
|
|
fwrite(buffer, 1, bytesencrypted, out);
|
|
fclose(out);
|
|
}
|
|
}
|
|
fclose(in);
|
|
}
|
|
return(err);
|
|
}
|
|
/* @func
|
|
decrypt a file using filephrase or masterphrase. If the file has a master key
|
|
and masterphrase is supplied, masterphrase is used. Otherwise filephrase.
|
|
*/
|
|
int decrypt(char *infile, char *outfile, char *filephrase, char *masterphrase)
|
|
{
|
|
AES_file in;
|
|
int err = 0;
|
|
if (0 == ROpen_AES(infile, &in, filephrase, masterphrase)) {
|
|
FILE *out = NULL;
|
|
if (fopen_s(&out, outfile, "wb") == 0) {
|
|
while (in.bytesleft > 0) {
|
|
if (in.buffer_index < in.buffer_end) { //write the data already available
|
|
fwrite(in.buffer + in.buffer_index, 1, in.buffer_end - in.buffer_index, out);
|
|
}
|
|
// read and decrypt some more data
|
|
{
|
|
long sizeread = (long)fread(in.buffer, 1, sizeof(in.buffer), in.file);
|
|
if (sizeread <= 0) {
|
|
printf("ran out of input data\n");
|
|
in.bytesleft = 0;
|
|
err++;
|
|
}
|
|
AES_blockDecrypt(&in.cipher, &in.key, in.buffer, sizeread, in.buffer);
|
|
in.bytesleft -= sizeread;
|
|
in.buffer_index = 0;
|
|
in.buffer_end = sizeread;
|
|
}
|
|
}
|
|
// now we just have one buffer containing some padding
|
|
in.buffer_end -= in.buffer[in.buffer_end - 1];
|
|
fwrite(in.buffer + in.buffer_index, 1, in.buffer_end - in.buffer_index, out);
|
|
fclose(out);
|
|
|
|
}
|
|
fclose(in.file);
|
|
}
|
|
return(err);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int err = 0;
|
|
if (argc >= 4) {
|
|
long idx = 1;
|
|
char *op = argv[idx++];
|
|
char *infile = argv[idx++];
|
|
char *outfile = argv[idx++];
|
|
char *pass1 = argv[idx++];
|
|
char *pass2 = (idx < argc) ? argv[idx++] : "";
|
|
|
|
if (_stricmp(op, "EF") == 0) { // encrypt with file passphrase only
|
|
encrypt(infile, outfile, pass1, "");
|
|
}
|
|
else if (_stricmp(op, "DF") == 0) { // decrypt using the file passphrase
|
|
decrypt(infile, outfile, pass1, "");
|
|
}
|
|
else if ((_stricmp(op, "EM") == 0) && (*pass2 != (char)0)) { // encrypt using file and master passphrases
|
|
encrypt(infile, outfile, pass1, pass2);
|
|
}
|
|
else if (_stricmp(op, "DM") == 0) { // decrypt using the master passphrase
|
|
decrypt(infile, outfile, "", pass1);
|
|
}
|
|
else { err++; }
|
|
}
|
|
else {
|
|
err++;
|
|
}
|
|
if (err) {
|
|
printf("\n%s - command line file encrypt/decrypt compatible with Notepad3\n\n"
|
|
"Usage: %s {ef em df dm} source destination {passphrase} {passphrase}\n", argv[0], argv[0]);
|
|
printf(" ef - encrypt w/filekey\n em - encrypt w/masterkey\n"
|
|
" df - decrypt w/filekey\n dm - decrypt w/masterkey\n\n");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|