/* $Id: e2p_crypt.c 907 2008-06-04 04:02:03Z tpgww $
Copyright (C) 2007-2009 tooar
This file is part of emelFM2.
emelFM2 is free software; for the most part you can redistribute it
and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3, or
(at your option) any later version.
emelFM2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with emelFM2; see the file GPL. If not, see http://www.gnu.org/licenses.
*/
/**
@file plugins/optional/e2p_cryptv1.c
@brief plugin for encrypting or decrypting selected items
The encryption code is derived from tinycrypt v.0.4. It’s based on ARC4 (see
http://en.wikipedia.org/wiki/RC4) with some tweaks by the author of tinycrypt:
– reduced nonce value to about 20 bytes (“160 bits should be enough”)
– reduced discarded bytes to 512 (“there’s no evidence on the net that more
are needed”)
– simplified mixing of the password and nonce values with the key, since
throwing away the first 512 bytes mixes them up anyway
For tinycrypt-mode, any [de]compression is peformed using minilzo, the mini
subset of the fast LZO real-time data compression library, v.2.03. Refer to
http://www.oberhumer.com/opensource/lzo/#minilzo
*/
/*TODO
en/de-compression
single
handle counter in new custom name
?m[un]map (void *address, size_t length, int protect, int flags, int filedes, off_t offset)
buffered I/O if not processing whole file at once, can’t be done with compression ?
pthread_testcancel (); //swap threads, cancel if instructed WHERE RELEVANT
library access for [de]compression
check – links ok ?
LZO supports in-place decompression, so sniff or reallocate loaded buffer NO – fails
in-place decompressing instead of duplicate buffers seems to NOT work
max size of lzo buffer lzo_unt = size_t? = ?
LZO streaming?
LZMA/7-zip compression supports streaming ?
UI
error handling – warnings
entry latency sometimes
crashes sometimes during dialog setup (maybe more to do with de-cryption?)
entry text scrolled out of entry window sometimes
some dialog-button choices have a BGL problem, if hide dialog after user-choice ?
*/
#include “emelfm2.h”
#include
#include
#include
#include
#include
#include “e2_plugins.h”
#include “e2_password_dialog.h”
#include “e2_task.h”
#include “e2_filelist.h”
//uncomment to create files compatible with tinycrypt NOT MUCH TESTED
#define TC_COMPATIBLE
#ifdef TC_COMPATIBLE
# define E2_MINICRYPT //TC 0.4 always uses mini-LZO, later versions allow no compression, we don’t do that
# define csize_t guint32 //TC code uses gulong’s, but store/retrieve operations use 4 bytes
# define SIZESHIFT 24 //(sizeof (csize_t) – 1) * 8, for handling endian-independent stored csize_t
# define NONCE_LENGTH 20
# define HEADER_LENGTH1 20 //just the nonce, no flags, in TC file header
#elif defined(_LARGEFILE_SOURCE)//if we cater for “large” files
# define csize_t guint64
# define SIZESHIFT 56
# define NONCE_LENGTH 24 //HEADER_LENGTH1 – csize_t (>=20)
# define HEADER_LENGTH1 32 //multiple of 8 >= (csize_t + 20)
#else //if we cater for files up to 2 GB
# define csize_t guint32
# define SIZESHIFT 24
# define NONCE_LENGTH 20
# define HEADER_LENGTH1 24 //NONCE_LENGTH + csize_t for flags
#endif
#ifndef TC_COMPATIBLE
//enable internal mini-LZO for en/de-cryption
//#define E2_MINICRYPT
#endif
#define KEY_LENGTH 256 //ARC4 hash table size KEY_LENGTH MUST be 256
#ifdef TC_COMPATIBLE
# define DISCARD_BYTES 768 // KEY_LENGTH * 3
#else
//”conservative” approach would use much greater size e.g. KEY_LENGTH * 10 or 12
# define DISCARD_BYTES 512 // KEY_LENGTH * 2 this is recommended by TC author
#endif
#define SWAP(a,b) temp=a;a=b;b=temp;
//bitflags in csize_t stored in after nonce in encrypted file
//(don’t change or remove any existing flag!)
enum
{
E2_CFLAGNONE = 0,
E2_CFLAGCOMPRESS = 1, //file is compressed (and one of the type-flags is set)
E2_CFLAGSIZE = 1 << 4, //original file size stored as csize_t
E2_CFLAGNAME = 1 << 5, //original file name stored as text
E2_CFLAGINFO = 1 << 6, //original file data stored in FileInfo
//bits 10-12 reserved for interface version number, 0..7
E2_CFLAGINTLZ = 1 << 16, //file compressed with internal mini lzo
E2_CFLAGLZO = 1 << 17, //file compressed with external liblzo
E2_CFLAGZ = 1 << 18, //file compressed with external libz
E2_CFLAGBZ2 = 1 << 19, //file compressed with external libbz2
E2_CFLAGLZMA = 1 << 20 //file compressed with external liblzma NOT WORKING
};
//for [de]compression library checking
#define E2_CFLAGLIBMASK 0x1f0000
//for interface version
#define E2_GETFLAGVERSION(s) ((s>>10)&7)
#define E2_SETFLAGVERSION(s,n) (s|((n&7)<<10))
#ifndef E2_MINICRYPT
//FOR LZO COMPRESSION
# define LZO1X_1_MEM_COMPRESS 16384*sizeof(guchar *)
//for dlopen() usage
# define LZO_COMPRESSFUNC "lzo1x_1_compress"
# define LZO_DECOMPRESSFUNC "lzo1x_decompress_safe"
# define GZIP_COMPRESSFUNC "compress2"
# define GZIP_DECOMPRESSFUNC "uncompress"
# define BZIP2_COMPRESSFUNC "BZ2_bzBuffToBuffCompress"
# define BZIP2_DECOMPRESSFUNC "BZ2_bzBuffToBuffDecompress"
//# define LZMA_COMPRESSFUNC "LzmaCompressFIXME"
//# define LZMA_DECOMPRESSFUNC "LzmaUnCompressFIXME"
//the preferred order for automatically choosing a compression library, if present
//NOTE config data now used instead of auto choice
#define COMPRESS_CHOICE1 E2_CFLAGLZO
#define COMPRESS_CHOICE2 E2_CFLAGZ
#define COMPRESS_CHOICE3 E2_CFLAGBZ2
//#define COMPRESS_CHOICE4 E2_CFLAGLZMA
#endif
//FOR LIBZ [DE]COMPRESSION
//compression levels
enum
{
Z_DEFAULT_COMPRESSION = -1,
Z_NO_COMPRESSION,
Z_BEST_SPEED,
Z_BEST_COMPRESSION = 9
};
//FOR LIBBZIP2 [DE]COMPRESSION
//FOR LIBLZMA [DE]COMPRESSION
//#define SZ_OK ?
typedef struct _E2P_CryptOpts
{
#ifndef TC_COMPATIBLE
gboolean en_name_same; //encrypted file name = same as onencrypted name
gboolean en_name_suffix;//encrypted file name has user-specified suffix (if any)
gboolean en_name_custom;//encrypted file name = user-specified
gboolean en_name_embed; //store filename in encrypted file CHECKME used ?
gboolean en_properties_embed; //store filename and statbuf in encrypted file
gboolean de_name_same; //decrypted file name = same as encrypted name
gboolean de_name_stored; //decrypted file name = embedded original name
gboolean de_name_suffix; //decrypted file name omits user-specified suffix (if any)
gboolean de_name_custom; //decrypted file name = user-specified
gboolean de_props_stored; //decrypted file other properties = original file
gboolean compress; //compress file before encryption
#endif
gboolean backup; //preserve any file with same name as specified for the [de]crypted file
gboolean preserve; //preserve the file to be [de]crypted, with alternate name if appropriate
gboolean recurse; //recursively process all files in any selected dir
gboolean walklinks; //process link targets
gboolean decryptmode;//TRUE = decrypt, FALSE = encrypt
gboolean permission;//for transferring whether it's ok to make the change
gboolean multisrc; //TRUE when processing > 1 item in loop
gboolean owrite; //don’t ask to confirm overwrites
#ifndef TC_COMPATIBLE
gboolean ignore_suffix; //don’t worry about incorrect suffix when decompressing
gchar *en_suffix; //user-specified suffix, freeable utf-8
gchar *en_name; //user-specified name, freeable utf-8
gchar *de_suffix; //user-specified suffix, freeable utf-8
gchar *de_name; //user-specified name, freeable utf-8
#endif
gchar *plain_pw; //store for plaintext password ptr utf-8
const gchar *localpath; //copy of dialog-function arg, or substitute during treewalk
#ifndef E2_MINICRYPT
csize_t compresslibflags;
gpointer libhandle; //result from latest dlopen(), need to dlcose() when done
#endif
struct stat *statptr;
#ifdef E2_VFS
PlaceInfo *spacedata;
#endif
GList *dirdata; //list of E2_Dirent’s for dirs yet to be processed
} E2P_CryptOpts;
typedef struct _E2P_CryptDlgRuntime
{
GtkWidget *dialog; //main dialog widget
E2P_CryptOpts *opts;
E2_PWDataRuntime *pwrt; //data struct for password-related widgets
gboolean dlgopen; //the following widgets exist
GtkWidget *mode_btn; //radio button for en/de-crypt mode
#ifndef TC_COMPATIBLE
GtkWidget *encryptbox; //vbox with encryption-specific widgets
GtkWidget *en_name_btn_same;
GtkWidget *en_name_btn_suffix;
GtkWidget *en_name_btn_custom;
GtkWidget *en_name_suffix_entry;
GtkWidget *en_name_custom_entry;
// GtkWidget *en_name_embed_btn; CHECKME use this ?
#endif
GtkWidget *confirmbox;
#ifndef TC_COMPATIBLE
GtkWidget *en_properties_embed_btn;
GtkWidget *compress_btn;
GtkWidget *decryptbox; //vbox with decryption-specific widgets
GtkWidget *de_name_btn_same;
GtkWidget *de_name_btn_stored;
GtkWidget *de_name_btn_suffix;
GtkWidget *de_name_btn_custom;
GtkWidget *de_name_suffix_entry;
GtkWidget *de_name_custom_entry;
#endif
GtkWidget *recurse_btn;
GtkWidget *backup_btn;
GtkWidget *preserve_btn;
GtkWidget *linktarget_btn;
#ifndef TC_COMPATIBLE
GtkWidget *properties_btn;
#endif
// GtkWidget *all_btn; //for [de]sensitisizing apply-to-all choice
GtkWidget *ok_btn;
// DialogButtons result;
gboolean result;
} E2P_CryptDlgRuntime;
//session-static parameters
static E2P_CryptOpts session_opts =
{
#ifndef TC_COMPATIBLE
FALSE, //encrypted file name = same as onencrypted name
TRUE, //encrypted file name has user-specified suffix (if any)
FALSE, //encrypted file name = user-specified
FALSE, //store filename in encrypted file CHECKME relevant ?
FALSE, //store filename and statbuf in encrypted file
FALSE, //decrypted file name = same as encrypted name
FALSE, //decrypted file name = embedded original name
TRUE, //decrypted file name omits user-specified suffix (if any)
FALSE, //decrypted file name = user-specified
FALSE, //reinstate properties of original file other than its name
TRUE, //compress file before encryption
#endif
TRUE, //preserve any file with same name as specified for the [de]crypted file
TRUE, //preserve the file to be [de]crypted, with alternate name if appropriate
FALSE, //recursively process all files in any selected dir
TRUE, //process link targets
/*rest are all default values, FALSE or NULL
FALSE, //decrypt mode
FALSE, //permission
FALSE, //>1 item
FALSE, //overwrite
FALSE, //ignore decomp suffix
NULL, //encryption suffix, initialised to “.enc”
NULL, //custom name for encryption
NULL, //decryption suffix, initialised to “.enc”
NULL, //custom name for decryption
NULL, //password
NULL, //item path
#ifndef E2_MINICRYPT
0, //compresslibflags;
NULL, //libhandle
#endif
NULL //stat buffer ptr
*/
};
//CHECKME are these ok to be static for all usage in session ??
//gboolean lzo_init_done = FALSE; //only init lzo (mini or lib) once?? OR EACH USE ?
//gboolean lzma_init_done = FALSE; //ditto for lzma SHOULD BE DONE ONCE ONLY, NOT EACH USAGE
#ifndef E2_MINICRYPT
//comression-library names used for config labels and error messages. Do not translate
static const gchar *libnames [] =
{
“LZO”, //config option 0
“GZIP”, //1
“BZIP2”,//2
// “LZMA”, //3
NULL
};
#endif
csize_t compresslib = E2_CFLAGNONE;
#ifdef E2_MINICRYPT
# if E2_DEBUG_LEVEL > 2
# define LZO_DEBUG
# endif
/* get mini-lzo headers and code
For convenience, this file merges all relevant files */
# include “e2p_crypt_lzo.source”
/* If the merge is not available, minilzo.c can be included instead. The
files lzodefs.h lzoconf.h minilzo.h will also need to be available, and if
built as is, it wlll result in a bogus plugin minilzo.so */
//# include “minilzo.c”
#endif
static gboolean _e2p_task_docryptQ (E2_ActionTaskData *qed);
static csize_t _e2pcr_compress_buffer (gpointer filebuffer,
/*ssize_t*/ gulong filebuffersize, gpointer *compressedbuffer); //CHECKME gulong size ?
static gboolean _e2pcr_write_buffer (VPATH *localpath, gint descriptor,
gpointer filebuffer, /*ssize_t*/ gulong filebuffersize); //CHECKME gulong size ?
static gboolean _e2pcr_read_file (VPATH *localpath, gpointer *filebuffer,
/*ssize_t*/ gulong filebuffersize);
static csize_t _e2pcr_decompress_buffer (gpointer filebuffer,
/*ssize_t*/ gulong filebuffersize, csize_t originalfilesize,
#ifndef E2_MINICRYPT
csize_t libflags,
#endif
gpointer *decompressedbuffer
#ifndef E2_MINICRYPT
, csize_t *lastlib, gpointer *libhandle
#endif
);
static gboolean _e2pcr_wipe_buffer (gpointer buffer, size_t buffersize);
static gboolean _e2pcr_flush_file (VPATH *localpath, guint8 hashes[]);
/***************/
/** utilities **/
/***************/
/**
@brief get a random value in range 0 .. 255
@param retval pointer to store for value
@return TRUE if the value was filled
*/
static gboolean _e2pcr_getrandom (guint8 *retval)
{
E2_FILE *randFile = fopen //no vfs needed here
#if defined(__linux__) || defined(__solaris__) || defined(darwin) || defined(__OpenBSD__) || defined(AIX)
//CHECKME which other OS’s ?
(“/dev/urandom”, “r”);
#else
(“/dev/random”, “r”);
#endif
if (randFile == NULL)
{
printd (DEBUG, “cannot open random number source”);
//FIXME handle error
*retval = 0;
return FALSE;
}
*retval = getc (randFile);
fclose (randFile);
return TRUE;
}
/**
@brief construct a temporary itemname by adding a suffix to @a localpath
@param localpath absolute path of item to be processed, localised string
@param custom string to append to @a localpath, localised string
@return newly-allocated, localised, path string comprising the temp name
*/
static gchar *_e2pcr_get_tempname (VPATH *localpath, gchar *custom)
{
gchar *temppath, *s;
guint i = 0;
E2_ERR_DECLARE
#ifdef E2_VFS
VPATH tdata;
tdata.spacedata = localpath->spacedata;
#endif
while (TRUE)
{
temppath = g_strdup_printf (“%s%s~%d”, VPSTR(localpath), custom, i);
if (i == 0)
{ //first try without any “~N” suffix
s = strrchr (temppath, ‘~’);
*s = ‘