mirror of
https://github.com/rizonesoft/Notepad3.git
synced 2026-06-14 21:09:05 +08:00
278 lines
8.5 KiB
C
278 lines
8.5 KiB
C
/*
|
|
*
|
|
* 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 <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] = 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: %d",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("notepadcrypt - command line file encrypt/decrypt compatible with notepad2\n"
|
|
"Usage: notepadcrypt {ef em df dm} source destination {passphrase} {passphrase}\n\n");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|