2020-10-12 23:10:47 +02:00
/******************************************************************************/
2020-10-19 21:56:12 +02:00
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
2020-10-12 23:10:47 +02:00
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
2020-11-03 23:19:00 +01:00
# ifdef _WIN32
# define _CRT_SECURE_NO_WARNINGS 1
# else
# define _GNU_SOURCE 1
# endif
2020-10-12 23:10:47 +02:00
2021-04-18 15:32:37 +02:00
/* Internal */
2020-10-19 21:56:12 +02:00
# include <slunkcrypt.h>
2020-10-13 15:04:59 +02:00
# include "utils.h"
2021-04-18 15:32:37 +02:00
# include "crypt.h"
2021-04-18 22:19:17 +02:00
# include "pwgen.h"
2021-04-18 15:32:37 +02:00
# include "selftest.h"
2020-11-04 23:17:59 +01:00
/* CRT */
2020-10-12 23:10:47 +02:00
# include <string.h>
# include <time.h>
2020-10-13 15:37:40 +02:00
# include <inttypes.h>
2020-10-13 17:42:22 +02:00
# include <ctype.h>
2020-10-14 16:36:17 +02:00
# include <signal.h>
2020-10-12 23:10:47 +02:00
2021-03-26 01:21:29 +01:00
// ==========================================================================
// Constants
// ==========================================================================
# define MODE_HELP 0
# define MODE_VERS 1
# define MODE_ENCR 2
# define MODE_DECR 3
# define MODE_PASS 4
# define MODE_TEST 5
2020-10-16 18:05:37 +02:00
2021-04-18 22:19:17 +02:00
static const size_t RCMD_PWDLEN_LENGTH = 12U ;
2021-05-08 17:55:16 +02:00
static const size_t DFLT_PWDLEN_LENGTH = 24U ;
2021-03-26 01:21:29 +01:00
2021-04-03 17:04:03 +02:00
static const CHR * const ENV_PASSWORD = T ( " SLUNK_PASSPHRASE " ) ;
static const CHR * const ENV_KEEPFILE = T ( " SLUNK_KEEP_INCOMPLETE " ) ;
2021-03-21 22:10:34 +01:00
static const CHR * const PREFIX_PASS = T ( " pass: " ) ;
static const CHR * const PREFIX_FILE = T ( " file: " ) ;
2020-10-20 15:21:00 +02:00
// ==========================================================================
// Auxiliary functions
// ==========================================================================
2021-04-18 22:19:17 +02:00
# define PW_FROM_ENV (!(argc > 4))
2021-03-26 01:21:29 +01:00
static int parse_slunk_mode ( const CHR * const command )
2020-10-20 15:21:00 +02:00
{
if ( ( ! STRICMP ( command , T ( " -h " ) ) ) | | ( ! STRICMP ( command , T ( " /? " ) ) ) | | ( ! STRICMP ( command , T ( " --help " ) ) ) )
{
2021-03-26 01:21:29 +01:00
return MODE_HELP ;
2020-10-20 15:21:00 +02:00
}
else if ( ( ! STRICMP ( command , T ( " -v " ) ) ) | | ( ! STRICMP ( command , T ( " --version " ) ) ) )
{
2021-03-26 01:21:29 +01:00
return MODE_VERS ;
2020-10-20 15:21:00 +02:00
}
else if ( ( ! STRICMP ( command , T ( " -e " ) ) ) | | ( ! STRICMP ( command , T ( " --encrypt " ) ) ) )
{
2021-03-26 01:21:29 +01:00
return MODE_ENCR ;
2020-10-20 15:21:00 +02:00
}
else if ( ( ! STRICMP ( command , T ( " -d " ) ) ) | | ( ! STRICMP ( command , T ( " --decrypt " ) ) ) )
{
2021-03-26 01:21:29 +01:00
return MODE_DECR ;
2020-10-20 15:21:00 +02:00
}
2020-12-01 17:28:32 +01:00
else if ( ( ! STRICMP ( command , T ( " -p " ) ) ) | | ( ! STRICMP ( command , T ( " --make-pw " ) ) ) )
{
2021-03-26 01:21:29 +01:00
return MODE_PASS ;
2020-12-01 17:28:32 +01:00
}
2020-10-20 15:21:00 +02:00
else if ( ( ! STRICMP ( command , T ( " -t " ) ) ) | | ( ! STRICMP ( command , T ( " --self-test " ) ) ) )
{
2021-03-26 01:21:29 +01:00
return MODE_TEST ;
2020-10-20 15:21:00 +02:00
}
else
{
2021-03-26 01:21:29 +01:00
return - 1 ; /*invalid command*/
2020-10-20 15:21:00 +02:00
}
}
static void print_manpage ( const CHR * const program )
{
FPUTS ( T ( " ==================================================================== \n " ) , stderr ) ;
FPUTS ( T ( " This software has been released under the CC0 1.0 Universal license: \n " ) , stderr ) ;
FPUTS ( T ( " https://creativecommons.org/publicdomain/zero/1.0/legalcode \n " ) , stderr ) ;
FPUTS ( T ( " ==================================================================== \n \n " ) , stderr ) ;
2020-10-20 15:33:03 +02:00
FPUTS ( T ( " Usage: \n " ) , stderr ) ;
2021-03-21 22:10:34 +01:00
FPRINTF ( stderr , T ( " % " ) T ( PRISTR ) T ( " --encrypt [pass:<pass>|file:<file>] <input.txt> <output.enc> \n " ) , program ) ;
FPRINTF ( stderr , T ( " % " ) T ( PRISTR ) T ( " --decrypt [pass:<pass>|file:<file>] <input.enc> <output.txt> \n " ) , program ) ;
2020-12-01 17:28:32 +01:00
FPRINTF ( stderr , T ( " % " ) T ( PRISTR ) T ( " --make-pw [<length>] \n \n " ) , program ) ;
2021-04-03 17:04:03 +02:00
FPRINTF ( stderr , T ( " Optionally, reads passphrase from the % " ) T ( PRISTR ) T ( " environment variable. \n \n " ) , ENV_PASSWORD ) ;
2021-03-21 22:10:34 +01:00
}
2021-03-26 01:21:29 +01:00
static char * copy_passphrase ( const CHR * const passphrase )
2021-03-21 22:10:34 +01:00
{
2021-03-26 01:21:29 +01:00
if ( ( ! passphrase ) | | ( ! passphrase [ 0U ] ) )
{
FPUTS ( T ( " Error: The passphrase input string must not be empty! \n \n " ) , stderr ) ;
return NULL ;
}
2021-03-21 22:10:34 +01:00
char * const buffer = CHR_to_utf8 ( passphrase ) ;
if ( ! buffer )
{
FPUTS ( T ( " Error: Failed to allocate the string buffer! \n \n " ) , stderr ) ;
}
2021-03-26 01:21:29 +01:00
2021-03-21 22:10:34 +01:00
return buffer ;
2020-10-20 15:21:00 +02:00
}
2021-04-18 15:32:37 +02:00
static int keep_incomplete_files ( void )
2020-10-12 23:10:47 +02:00
{
2021-04-18 15:32:37 +02:00
const CHR * const keep_files = GETENV ( ENV_KEEPFILE ) ;
if ( keep_files )
2020-10-12 23:10:47 +02:00
{
2021-04-18 15:32:37 +02:00
return BOOLIFY ( STRTOUL ( keep_files ) ) ;
2020-10-12 23:10:47 +02:00
}
2021-04-18 15:32:37 +02:00
return 0 ;
2020-10-13 00:43:57 +02:00
}
2021-04-18 15:32:37 +02:00
static void check_excess_arguments ( const int argc , int maximum )
2021-04-03 17:04:03 +02:00
{
2021-04-18 15:32:37 +02:00
if ( argc > maximum )
2021-04-03 17:04:03 +02:00
{
2021-04-18 15:32:37 +02:00
FPUTS ( T ( " Warning: Excess command-line argument(s) will be ignored! \n \n " ) , stderr ) ;
fflush ( stderr ) ;
2021-04-03 17:04:03 +02:00
}
}
2020-10-20 15:21:00 +02:00
static void sigint_handler ( const int sig )
{
if ( sig = = SIGINT )
{
g_slunkcrypt_abort_flag = 1 ;
}
}
// ==========================================================================
// Main function
// ==========================================================================
2020-10-14 16:36:17 +02:00
2020-10-20 15:21:00 +02:00
int MAIN ( const int argc , CHR * const argv [ ] )
2020-10-12 23:10:47 +02:00
{
2020-10-13 19:33:01 +02:00
init_terminal ( ) ;
2020-10-20 19:13:11 +02:00
setup_signal_handler ( SIGINT , sigint_handler ) ;
2020-10-20 22:13:39 +02:00
int result = EXIT_FAILURE ;
2022-02-20 15:39:33 +01:00
CHR * input_file = NULL , * output_file = NULL ;
2020-10-22 18:01:59 +02:00
char * passphrase_buffer = NULL ;
2020-10-12 23:10:47 +02:00
2020-10-19 21:56:12 +02:00
FPRINTF ( stderr , T ( " SlunkCrypt Utility (% " ) T ( PRIstr ) T ( " -% " ) T ( PRIstr ) T ( " ), by LoRd_MuldeR <MuldeR2@GMX.de> \n " ) , OS_TYPE , CPU_ARCH ) ;
2020-10-20 15:21:00 +02:00
FPRINTF ( stderr , T ( " Using libSlunkCrypt v%u.%u.%u [% " ) T ( PRIstr ) T ( " ] \n \n " ) , SLUNKCRYPT_VERSION_MAJOR , SLUNKCRYPT_VERSION_MINOR , SLUNKCRYPT_VERSION_PATCH , SLUNKCRYPT_BUILD ) ;
2020-10-13 19:33:01 +02:00
2021-04-11 20:55:39 +02:00
fflush ( stderr ) ;
2020-10-20 15:21:00 +02:00
/* ----------------------------------------------------- */
/* Parse arguments */
/* ----------------------------------------------------- */
2022-02-08 22:53:02 +01:00
if ( ( argc < 1 ) | | ( ! argv [ 0 ] ) )
2022-01-27 00:21:28 +01:00
{
FPUTS ( T ( " Error: Argument array is empty. The program was called incorrectly! \n \n " ) , stderr ) ;
goto clean_up ;
}
2020-10-20 22:13:39 +02:00
if ( argc < 2 )
2020-10-14 13:14:47 +02:00
{
2020-10-20 15:21:00 +02:00
FPRINTF ( stderr , T ( " Error: Nothing to do. Please type '% " ) T ( PRISTR ) T ( " --help' for details! \n \n " ) , get_file_name ( argv [ 0U ] ) ) ;
2020-10-22 18:01:59 +02:00
goto clean_up ;
2020-10-20 22:13:39 +02:00
}
2021-03-26 01:21:29 +01:00
const int slunk_mode = parse_slunk_mode ( argv [ 1U ] ) ;
switch ( slunk_mode )
2020-10-20 22:13:39 +02:00
{
2021-03-26 01:21:29 +01:00
case MODE_HELP :
2020-10-20 15:21:00 +02:00
print_manpage ( get_file_name ( argv [ 0U ] ) ) ;
2021-03-26 01:21:29 +01:00
case MODE_VERS :
2020-10-22 18:01:59 +02:00
result = EXIT_SUCCESS ;
goto clean_up ;
2021-03-26 01:21:29 +01:00
case MODE_ENCR :
case MODE_DECR :
break ; /*fallthrough*/
case MODE_PASS :
2021-04-18 15:32:37 +02:00
check_excess_arguments ( argc , 3 ) ;
2021-03-26 01:21:29 +01:00
result = generate_passphrase ( ( argc > 2 ) ? STRTOUL ( argv [ 2U ] ) : DFLT_PWDLEN_LENGTH ) ;
2020-12-01 17:28:32 +01:00
goto clean_up ;
2021-03-26 01:21:29 +01:00
case MODE_TEST :
2021-04-18 15:32:37 +02:00
check_excess_arguments ( argc , 2 ) ;
result = run_selftest_routine ( ) ;
2020-10-22 18:01:59 +02:00
goto clean_up ;
2021-03-26 01:21:29 +01:00
default :
FPRINTF ( stderr , T ( " Error: The specified command \" % " ) T ( PRISTR ) T ( " \" is unknown! \n \n " ) , argv [ 1U ] ) ;
goto clean_up ;
2020-10-14 13:14:47 +02:00
}
2020-10-16 17:37:04 +02:00
if ( argc < 4 )
2020-10-12 23:10:47 +02:00
{
2020-10-20 15:21:00 +02:00
FPRINTF ( stderr , T ( " Error: Required argument is missing. Please type '% " ) T ( PRISTR ) T ( " --help' for details! \n \n " ) , get_file_name ( argv [ 0U ] ) ) ;
2020-10-22 18:01:59 +02:00
goto clean_up ;
2020-10-13 17:42:22 +02:00
}
2021-04-18 15:32:37 +02:00
check_excess_arguments ( argc , 5 ) ;
2021-04-21 13:52:49 +02:00
2021-04-03 17:04:03 +02:00
const CHR * const passphrase = PW_FROM_ENV ? GETENV ( ENV_PASSWORD ) : argv [ 2U ] ;
2021-03-21 22:10:34 +01:00
if ( ( ! passphrase ) | | ( ! passphrase [ 0U ] ) )
{
FPUTS ( T ( " Error: The passphrase must be specified, directly or indirectly! \n \n " ) , stderr ) ;
goto clean_up ;
}
2020-10-14 16:36:17 +02:00
2021-03-26 01:21:29 +01:00
if ( ( ! PW_FROM_ENV ) & & STRICMP ( passphrase , T ( " - " ) ) )
2020-10-13 17:42:22 +02:00
{
2021-03-26 01:21:29 +01:00
if ( ( ! STARTS_WITH ( passphrase , PREFIX_PASS ) ) & & ( ! STARTS_WITH ( passphrase , PREFIX_FILE ) ) )
{
FPRINTF ( stderr , T ( " Error: The passphrase must start with a '% " ) T ( PRISTR ) T ( " ' or '% " ) T ( PRISTR ) T ( " ' prefix! \n \n " ) , PREFIX_PASS , PREFIX_FILE ) ;
goto clean_up ;
}
2020-10-12 23:10:47 +02:00
}
2020-10-13 19:33:01 +02:00
2022-02-20 15:39:33 +01:00
input_file = absolute_path ( argv [ PW_FROM_ENV ? 2U : 3U ] ) ;
if ( ( ! input_file ) | | ( ! input_file [ 0U ] ) )
{
FPUTS ( T ( " Error: Input file path could not be resolved! \n \n " ) , stderr ) ;
goto clean_up ;
}
output_file = absolute_path ( argv [ PW_FROM_ENV ? 3U : 4U ] ) ;
if ( ( ! output_file ) | | ( ! output_file [ 0U ] ) )
2020-10-13 19:33:01 +02:00
{
2022-02-20 15:39:33 +01:00
FPUTS ( T ( " Error: Output file path could not be resolved! \n \n " ) , stderr ) ;
goto clean_up ;
}
if ( ! path_compare ( input_file , output_file ) )
{
FPUTS ( T ( " Error: Input and output files must not be the same path! (effectively) \n \n " ) , stderr ) ;
2020-10-22 18:01:59 +02:00
goto clean_up ;
2020-10-13 19:33:01 +02:00
}
2020-10-20 15:21:00 +02:00
/* ----------------------------------------------------- */
/* Initialize passphrase */
/* ----------------------------------------------------- */
2021-03-26 01:21:29 +01:00
if ( ! ( passphrase_buffer = PW_FROM_ENV ? copy_passphrase ( passphrase ) :
( STARTS_WITH ( passphrase , PREFIX_PASS ) ? copy_passphrase ( passphrase + STRLEN ( PREFIX_PASS ) ) :
( STARTS_WITH ( passphrase , PREFIX_FILE ) ? read_passphrase ( passphrase + STRLEN ( PREFIX_FILE ) ) : read_passphrase ( T ( " - " ) ) ) ) ) )
2020-10-13 19:33:01 +02:00
{
2020-10-22 18:01:59 +02:00
goto clean_up ;
2020-10-13 19:33:01 +02:00
}
2020-10-12 23:10:47 +02:00
2020-10-19 21:56:12 +02:00
slunkcrypt_bzero ( ( CHR * ) passphrase , STRLEN ( passphrase ) * sizeof ( CHR ) ) ;
2020-10-16 18:05:37 +02:00
2020-10-20 15:21:00 +02:00
const size_t passphrase_len = strlen ( passphrase_buffer ) ;
if ( passphrase_len < SLUNKCRYPT_PWDLEN_MIN )
{
2021-03-26 01:21:29 +01:00
FPRINTF ( stderr , T ( " Error: Passphrase must be at least %u characters in length! \n \n " ) , ( unsigned ) SLUNKCRYPT_PWDLEN_MIN ) ;
2020-10-22 18:01:59 +02:00
goto clean_up ;
2020-10-20 15:21:00 +02:00
}
else if ( passphrase_len > SLUNKCRYPT_PWDLEN_MAX )
{
2021-03-26 01:21:29 +01:00
FPRINTF ( stderr , T ( " Error: Passphrase must be at most %u characters in length! \n \n " ) , ( unsigned ) SLUNKCRYPT_PWDLEN_MAX ) ;
2020-10-22 18:01:59 +02:00
goto clean_up ;
2020-10-20 15:21:00 +02:00
}
2021-04-24 02:59:39 +02:00
if ( slunk_mode = = MODE_ENCR )
2020-10-14 16:36:17 +02:00
{
2021-04-24 02:59:39 +02:00
if ( passphrase_len < RCMD_PWDLEN_LENGTH )
{
FPRINTF ( stderr , T ( " Warning: Using a *short* passphrase; a length of %u characters or more is recommended! \n \n " ) , ( unsigned ) RCMD_PWDLEN_LENGTH ) ;
}
else if ( weak_passphrase ( passphrase_buffer ) )
{
FPUTS ( T ( " Warning: Using a *weak* passphrase; a mix of upper-case letters, lower-case letters, digits and other characters is recommended! \n \n " ) , stderr ) ;
}
2020-10-14 16:36:17 +02:00
}
2021-04-11 20:55:39 +02:00
fflush ( stderr ) ;
2020-10-20 15:21:00 +02:00
/* ----------------------------------------------------- */
/* Encrypt or decrypt */
/* ----------------------------------------------------- */
2022-02-11 21:52:22 +01:00
const uint64_t clk_start = clock_read ( ) ;
2021-04-18 15:32:37 +02:00
const int keep_incomplete = keep_incomplete_files ( ) ;
2020-10-12 23:10:47 +02:00
2021-03-26 01:21:29 +01:00
switch ( slunk_mode )
2020-10-13 17:42:22 +02:00
{
2021-03-26 01:21:29 +01:00
case MODE_ENCR :
2021-04-18 15:32:37 +02:00
result = encrypt ( passphrase_buffer , input_file , output_file , keep_incomplete ) ;
2020-10-20 15:21:00 +02:00
break ;
2021-03-26 01:21:29 +01:00
case MODE_DECR :
2021-04-18 15:32:37 +02:00
result = decrypt ( passphrase_buffer , input_file , output_file , keep_incomplete ) ;
2020-10-20 15:21:00 +02:00
break ;
default :
2020-10-20 22:13:39 +02:00
FPUTS ( T ( " Unexpected mode encountered! \n \n " ) , stderr ) ;
2020-10-12 23:10:47 +02:00
}
2020-10-19 21:56:12 +02:00
if ( ! g_slunkcrypt_abort_flag )
2020-10-14 21:55:39 +02:00
{
2022-02-11 21:52:22 +01:00
FPRINTF ( stderr , T ( " -------- \n \n Operation completed after %.1f seconds. \n \n " ) , ( clock_read ( ) - clk_start ) / ( ( double ) clock_freq ( ) ) ) ;
2020-10-14 21:55:39 +02:00
}
2020-10-13 19:33:01 +02:00
2020-10-20 15:21:00 +02:00
/* ----------------------------------------------------- */
/* Final clean-up */
/* ----------------------------------------------------- */
2020-10-22 18:01:59 +02:00
clean_up :
2021-04-11 20:55:39 +02:00
fflush ( stderr ) ;
2020-10-14 16:36:17 +02:00
if ( passphrase_buffer )
2020-10-13 19:50:29 +02:00
{
2020-10-19 21:56:12 +02:00
slunkcrypt_bzero ( passphrase_buffer , strlen ( passphrase_buffer ) ) ;
2020-10-14 16:36:17 +02:00
free ( passphrase_buffer ) ;
2020-10-13 19:50:29 +02:00
}
2022-02-20 15:39:33 +01:00
if ( input_file )
{
free ( input_file ) ;
}
if ( output_file )
{
free ( output_file ) ;
}
2020-10-15 22:41:28 +02:00
return result ;
2020-10-12 23:10:47 +02:00
}
2020-10-21 17:07:03 +02:00
# if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
void __wgetmainargs ( int * , wchar_t * * * , wchar_t * * * , int , int * ) ;
int main ( )
{
wchar_t * * enpv , * * argv ;
int argc , si = 0 ;
__wgetmainargs ( & argc , & argv , & enpv , 1 , & si ) ;
return wmain ( argc , argv ) ;
}
# endif